blob: bbbc4f4cbf1a7a78c8a9b561989132753d94ab6e [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800110struct via_spec {
111 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200112 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800113 unsigned int num_mixers;
114
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200115 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800116 unsigned int num_iverbs;
117
Takashi Iwai82673bc2011-06-17 16:24:21 +0200118 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200119 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200120 const struct hda_pcm_stream *stream_analog_playback;
121 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122
Takashi Iwai82673bc2011-06-17 16:24:21 +0200123 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_pcm_stream *stream_digital_playback;
125 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800126
127 /* playback */
128 struct hda_multi_out multiout;
129 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200130 hda_nid_t hp_dac_nid;
Takashi Iwai25250502011-06-30 17:24:47 +0200131 bool hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200132 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800133
Takashi Iwai4a796162011-06-17 17:53:38 +0200134 struct nid_path out_path[4];
135 struct nid_path hp_path;
136 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200137 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200138
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800139 /* capture */
140 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200141 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800142 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200143 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
146 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200147 bool dyn_adc_switch;
148 int num_inputs;
149 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800150 unsigned int cur_mux[3];
151
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200152 /* dynamic ADC switching */
153 hda_nid_t cur_adc;
154 unsigned int cur_adc_stream_tag;
155 unsigned int cur_adc_format;
156
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800157 /* PCM information */
158 struct hda_pcm pcm_rec[3];
159
160 /* dynamic controls, init_verbs and input_mux */
161 struct auto_pin_cfg autocfg;
162 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800163 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
164
165 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800166 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800167 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200168 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 enum VIA_HDA_CODEC codec_type;
170
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200171 /* smart51 setup */
172 unsigned int smart51_nums;
173 hda_nid_t smart51_pins[2];
174 int smart51_idxs[2];
175 const char *smart51_labels[2];
176 unsigned int smart51_enabled;
177
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800178 /* work to check hp jack state */
179 struct hda_codec *codec;
180 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200181 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800182 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800183
184 void (*set_widgets_power_state)(struct hda_codec *codec);
185
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800186 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200187 int num_loopbacks;
188 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200189
190 /* bind capture-volume */
191 struct hda_bind_ctls *bind_cap_vol;
192 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800193};
194
Lydia Wang0341ccd2011-03-22 16:25:03 +0800195static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100196static struct via_spec * via_new_spec(struct hda_codec *codec)
197{
198 struct via_spec *spec;
199
200 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
201 if (spec == NULL)
202 return NULL;
203
204 codec->spec = spec;
205 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800206 spec->codec_type = get_codec_type(codec);
207 /* VT1708BCE & VT1708S are almost same */
208 if (spec->codec_type == VT1708BCE)
209 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100210 return spec;
211}
212
Lydia Wang744ff5f2009-10-10 19:07:26 +0800213static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800214{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800215 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800216 u16 ven_id = vendor_id >> 16;
217 u16 dev_id = vendor_id & 0xffff;
218 enum VIA_HDA_CODEC codec_type;
219
220 /* get codec type */
221 if (ven_id != 0x1106)
222 codec_type = UNKNOWN;
223 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
224 codec_type = VT1708;
225 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
226 codec_type = VT1709_10CH;
227 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
228 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800229 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800230 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800231 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
232 codec_type = VT1708BCE;
233 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800234 codec_type = VT1708B_4CH;
235 else if ((dev_id & 0xfff) == 0x397
236 && (dev_id >> 12) < 8)
237 codec_type = VT1708S;
238 else if ((dev_id & 0xfff) == 0x398
239 && (dev_id >> 12) < 8)
240 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800241 else if ((dev_id & 0xfff) == 0x428
242 && (dev_id >> 12) < 8)
243 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800244 else if (dev_id == 0x0433 || dev_id == 0xa721)
245 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800246 else if (dev_id == 0x0441 || dev_id == 0x4441)
247 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800248 else if (dev_id == 0x0438 || dev_id == 0x4438)
249 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800250 else if (dev_id == 0x0448)
251 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800252 else if (dev_id == 0x0440)
253 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800254 else if ((dev_id & 0xfff) == 0x446)
255 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800256 else
257 codec_type = UNKNOWN;
258 return codec_type;
259};
260
Lydia Wangec7e7e42011-03-24 12:43:44 +0800261#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800262#define VIA_HP_EVENT 0x01
263#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200264#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800265
Joseph Chanc577b8a2006-11-29 15:29:40 +0100266enum {
267 VIA_CTL_WIDGET_VOL,
268 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800269 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100270};
271
Takashi Iwaiada509e2011-06-20 15:40:19 +0200272static void analog_low_current_mode(struct hda_codec *codec);
273static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800274
275static void vt1708_start_hp_work(struct via_spec *spec)
276{
277 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
278 return;
279 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200280 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800281 if (!delayed_work_pending(&spec->vt1708_hp_work))
282 schedule_delayed_work(&spec->vt1708_hp_work,
283 msecs_to_jiffies(100));
284}
285
286static void vt1708_stop_hp_work(struct via_spec *spec)
287{
288 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
289 return;
290 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
291 && !is_aa_path_mute(spec->codec))
292 return;
293 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200294 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100295 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800296}
Lydia Wangf5271102009-10-10 19:07:35 +0800297
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800298static void set_widgets_power_state(struct hda_codec *codec)
299{
300 struct via_spec *spec = codec->spec;
301 if (spec->set_widgets_power_state)
302 spec->set_widgets_power_state(codec);
303}
Lydia Wang25eaba22009-10-10 19:08:43 +0800304
Lydia Wangf5271102009-10-10 19:07:35 +0800305static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
306 struct snd_ctl_elem_value *ucontrol)
307{
308 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
309 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
310
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800311 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200312 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800313 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
314 if (is_aa_path_mute(codec))
315 vt1708_start_hp_work(codec->spec);
316 else
317 vt1708_stop_hp_work(codec->spec);
318 }
Lydia Wangf5271102009-10-10 19:07:35 +0800319 return change;
320}
321
322/* modify .put = snd_hda_mixer_amp_switch_put */
323#define ANALOG_INPUT_MUTE \
324 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
325 .name = NULL, \
326 .index = 0, \
327 .info = snd_hda_mixer_amp_switch_info, \
328 .get = snd_hda_mixer_amp_switch_get, \
329 .put = analog_input_switch_put, \
330 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
331
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200332static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100333 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
334 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800335 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100336};
337
Lydia Wangab6734e2009-10-10 19:08:46 +0800338
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200340static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
341 const struct snd_kcontrol_new *tmpl,
342 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100343{
344 struct snd_kcontrol_new *knew;
345
Takashi Iwai603c4012008-07-30 15:01:44 +0200346 snd_array_init(&spec->kctls, sizeof(*knew), 32);
347 knew = snd_array_new(&spec->kctls);
348 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200349 return NULL;
350 *knew = *tmpl;
351 if (!name)
352 name = tmpl->name;
353 if (name) {
354 knew->name = kstrdup(name, GFP_KERNEL);
355 if (!knew->name)
356 return NULL;
357 }
358 return knew;
359}
360
361static int __via_add_control(struct via_spec *spec, int type, const char *name,
362 int idx, unsigned long val)
363{
364 struct snd_kcontrol_new *knew;
365
366 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
367 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200369 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100370 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100371 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100372 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100373 return 0;
374}
375
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200376#define via_add_control(spec, type, name, val) \
377 __via_add_control(spec, type, name, 0, val)
378
Takashi Iwai291c9e32011-06-17 16:15:26 +0200379#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100380
Takashi Iwai603c4012008-07-30 15:01:44 +0200381static void via_free_kctls(struct hda_codec *codec)
382{
383 struct via_spec *spec = codec->spec;
384
385 if (spec->kctls.list) {
386 struct snd_kcontrol_new *kctl = spec->kctls.list;
387 int i;
388 for (i = 0; i < spec->kctls.used; i++)
389 kfree(kctl[i].name);
390 }
391 snd_array_free(&spec->kctls);
392}
393
Joseph Chanc577b8a2006-11-29 15:29:40 +0100394/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800395static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200396 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100397{
398 char name[32];
399 int err;
400
401 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200402 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100403 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
404 if (err < 0)
405 return err;
406 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200407 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100408 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
409 if (err < 0)
410 return err;
411 return 0;
412}
413
Takashi Iwai5d417622011-06-20 11:32:27 +0200414#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200415 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200416
Takashi Iwai8df2a312011-06-21 11:48:29 +0200417static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
418 unsigned int mask)
419{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200420 unsigned int caps;
421 if (!nid)
422 return false;
423 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200424 if (dir == HDA_INPUT)
425 caps &= AC_WCAP_IN_AMP;
426 else
427 caps &= AC_WCAP_OUT_AMP;
428 if (!caps)
429 return false;
430 if (query_amp_caps(codec, nid, dir) & mask)
431 return true;
432 return false;
433}
434
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200435#define have_mute(codec, nid, dir) \
436 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200437
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200438/* enable/disable the output-route */
439static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
440 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200441{
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200442 int i;
443 for (i = 0; i < path->depth; i++) {
444 hda_nid_t src, dst;
445 int idx = path->idx[i];
446 src = path->path[i];
447 if (i < path->depth - 1)
448 dst = path->path[i + 1];
449 else
450 dst = 0;
451 if (enable && path->multi[i])
452 snd_hda_codec_write(codec, dst, 0,
453 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wange5e14682011-07-01 10:55:07 +0800454 if (get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT &&
455 get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
456 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200457 if (have_mute(codec, dst, HDA_INPUT)) {
458 int val = enable ? AMP_IN_UNMUTE(idx) :
459 AMP_IN_MUTE(idx);
460 snd_hda_codec_write(codec, dst, 0,
461 AC_VERB_SET_AMP_GAIN_MUTE, val);
462 }
463 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
464 continue;
465 if (have_mute(codec, src, HDA_OUTPUT)) {
466 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
467 snd_hda_codec_write(codec, src, 0,
468 AC_VERB_SET_AMP_GAIN_MUTE, val);
469 }
470 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200471}
472
473/* set the given pin as output */
474static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
475 int pin_type)
476{
477 if (!pin)
478 return;
479 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
480 pin_type);
481 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
482 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200483 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100484}
485
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200486static void via_auto_init_output(struct hda_codec *codec,
487 struct nid_path *path, int pin_type,
488 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200489{
490 struct via_spec *spec = codec->spec;
491 unsigned int caps;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200492 hda_nid_t pin, nid;
493 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200494
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200495 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200496 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200497 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200498
499 init_output_pin(codec, pin, pin_type);
500 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
501 if (caps & AC_AMPCAP_MUTE) {
502 unsigned int val;
503 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
504 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
505 AMP_OUT_MUTE | val);
506 }
507
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200508 activate_output_path(codec, path, true, force);
509
510 /* initialize the AA-path */
511 if (!spec->aa_mix_nid)
512 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200513 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200514 nid = path->path[i];
515 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
516 if (idx >= 0) {
517 if (have_mute(codec, nid, HDA_INPUT))
518 snd_hda_codec_write(codec, nid, 0,
519 AC_VERB_SET_AMP_GAIN_MUTE,
520 AMP_IN_UNMUTE(idx));
521 break;
522 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200523 }
524}
525
Joseph Chanc577b8a2006-11-29 15:29:40 +0100526static void via_auto_init_multi_out(struct hda_codec *codec)
527{
528 struct via_spec *spec = codec->spec;
529 int i;
530
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200531 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200532 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100533}
534
535static void via_auto_init_hp_out(struct hda_codec *codec)
536{
537 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100538
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200539 if (!spec->hp_dac_nid) {
540 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
541 return;
542 }
543 if (spec->hp_independent_mode) {
544 activate_output_path(codec, &spec->hp_dep_path, false, false);
545 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
546 } else {
547 activate_output_path(codec, &spec->hp_path, false, false);
548 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
549 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100550}
551
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200552static void via_auto_init_speaker_out(struct hda_codec *codec)
553{
554 struct via_spec *spec = codec->spec;
555
556 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200557 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200558}
559
Takashi Iwaif4a78282011-06-17 18:46:48 +0200560static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200561
Joseph Chanc577b8a2006-11-29 15:29:40 +0100562static void via_auto_init_analog_input(struct hda_codec *codec)
563{
564 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200565 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200566 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200567 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200568 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100569
Takashi Iwai096a8852011-06-20 12:09:02 +0200570 /* init ADCs */
571 for (i = 0; i < spec->num_adc_nids; i++) {
572 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
573 AC_VERB_SET_AMP_GAIN_MUTE,
574 AMP_IN_UNMUTE(0));
575 }
576
577 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200578 for (i = 0; i < cfg->num_inputs; i++) {
579 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200580 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200581 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100582 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200583 ctl = PIN_VREF50;
584 else
585 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100586 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200587 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100588 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200589
590 /* init input-src */
591 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200592 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
593 if (spec->mux_nids[adc_idx]) {
594 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
595 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
596 AC_VERB_SET_CONNECT_SEL,
597 mux_idx);
598 }
599 if (spec->dyn_adc_switch)
600 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200601 }
602
603 /* init aa-mixer */
604 if (!spec->aa_mix_nid)
605 return;
606 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
607 ARRAY_SIZE(conn));
608 for (i = 0; i < num_conns; i++) {
609 unsigned int caps = get_wcaps(codec, conn[i]);
610 if (get_wcaps_type(caps) == AC_WID_PIN)
611 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
612 AC_VERB_SET_AMP_GAIN_MUTE,
613 AMP_IN_MUTE(i));
614 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100615}
Lydia Wangf5271102009-10-10 19:07:35 +0800616
617static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
618 unsigned int *affected_parm)
619{
620 unsigned parm;
621 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
622 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
623 >> AC_DEFCFG_MISC_SHIFT
624 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800625 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200626 unsigned present = 0;
627
628 no_presence |= spec->no_pin_power_ctl;
629 if (!no_presence)
630 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200631 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800632 || ((no_presence || present)
633 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800634 *affected_parm = AC_PWRST_D0; /* if it's connected */
635 parm = AC_PWRST_D0;
636 } else
637 parm = AC_PWRST_D3;
638
639 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
640}
641
Takashi Iwai24088a52011-06-17 16:59:21 +0200642static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
643 struct snd_ctl_elem_info *uinfo)
644{
645 static const char * const texts[] = {
646 "Disabled", "Enabled"
647 };
648
649 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
650 uinfo->count = 1;
651 uinfo->value.enumerated.items = 2;
652 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
653 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
654 strcpy(uinfo->value.enumerated.name,
655 texts[uinfo->value.enumerated.item]);
656 return 0;
657}
658
659static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
660 struct snd_ctl_elem_value *ucontrol)
661{
662 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
663 struct via_spec *spec = codec->spec;
664 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
665 return 0;
666}
667
668static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
669 struct snd_ctl_elem_value *ucontrol)
670{
671 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
672 struct via_spec *spec = codec->spec;
673 unsigned int val = !ucontrol->value.enumerated.item[0];
674
675 if (val == spec->no_pin_power_ctl)
676 return 0;
677 spec->no_pin_power_ctl = val;
678 set_widgets_power_state(codec);
679 return 1;
680}
681
682static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
683 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
684 .name = "Dynamic Power-Control",
685 .info = via_pin_power_ctl_info,
686 .get = via_pin_power_ctl_get,
687 .put = via_pin_power_ctl_put,
688};
689
690
Harald Welte0aa62ae2008-09-09 15:58:27 +0800691static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
692 struct snd_ctl_elem_info *uinfo)
693{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200694 static const char * const texts[] = { "OFF", "ON" };
695
696 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
697 uinfo->count = 1;
698 uinfo->value.enumerated.items = 2;
699 if (uinfo->value.enumerated.item >= 2)
700 uinfo->value.enumerated.item = 1;
701 strcpy(uinfo->value.enumerated.name,
702 texts[uinfo->value.enumerated.item]);
703 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800704}
705
706static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
707 struct snd_ctl_elem_value *ucontrol)
708{
709 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800710 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800711
Takashi Iwaiece8d042011-06-19 16:24:21 +0200712 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800713 return 0;
714}
715
Harald Welte0aa62ae2008-09-09 15:58:27 +0800716static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
717 struct snd_ctl_elem_value *ucontrol)
718{
719 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
720 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200721 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200722
Takashi Iwai25250502011-06-30 17:24:47 +0200723 /* no independent-hp status change during PCM playback is running */
724 if (spec->num_active_streams)
725 return -EBUSY;
726
727 cur = !!ucontrol->value.enumerated.item[0];
728 if (spec->hp_independent_mode == cur)
729 return 0;
730 spec->hp_independent_mode = cur;
731 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200732 activate_output_path(codec, &spec->hp_dep_path, false, false);
733 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200734 if (spec->hp_indep_shared)
735 activate_output_path(codec, &spec->out_path[HDA_SIDE],
736 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200737 } else {
738 activate_output_path(codec, &spec->hp_path, false, false);
739 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200740 if (spec->hp_indep_shared)
741 activate_output_path(codec, &spec->out_path[HDA_SIDE],
742 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200743 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800744
Lydia Wangce0e5a92011-03-22 16:22:37 +0800745 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800746 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200747 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800748}
749
Takashi Iwaiece8d042011-06-19 16:24:21 +0200750static const struct snd_kcontrol_new via_hp_mixer = {
751 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
752 .name = "Independent HP",
753 .info = via_independent_hp_info,
754 .get = via_independent_hp_get,
755 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800756};
757
Takashi Iwai3d83e572010-04-14 14:36:23 +0200758static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100759{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200760 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100761 struct snd_kcontrol_new *knew;
762 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100763
Takashi Iwaiece8d042011-06-19 16:24:21 +0200764 nid = spec->autocfg.hp_pins[0];
765 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200766 if (knew == NULL)
767 return -ENOMEM;
768
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100769 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100770
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100771 return 0;
772}
773
Lydia Wang1564b282009-10-10 19:07:52 +0800774static void notify_aa_path_ctls(struct hda_codec *codec)
775{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200776 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800777 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800778
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200779 for (i = 0; i < spec->smart51_nums; i++) {
780 struct snd_kcontrol *ctl;
781 struct snd_ctl_elem_id id;
782 memset(&id, 0, sizeof(id));
783 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
784 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800785 ctl = snd_hda_find_mixer_ctl(codec, id.name);
786 if (ctl)
787 snd_ctl_notify(codec->bus->card,
788 SNDRV_CTL_EVENT_MASK_VALUE,
789 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800790 }
791}
792
793static void mute_aa_path(struct hda_codec *codec, int mute)
794{
795 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200796 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800797 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200798
Lydia Wang1564b282009-10-10 19:07:52 +0800799 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200800 for (i = 0; i < spec->smart51_nums; i++) {
801 if (spec->smart51_idxs[i] < 0)
802 continue;
803 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
804 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800805 HDA_AMP_MUTE, val);
806 }
807}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200808
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200809static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
810{
811 struct via_spec *spec = codec->spec;
812 int i;
813
814 for (i = 0; i < spec->smart51_nums; i++)
815 if (spec->smart51_pins[i] == pin)
816 return true;
817 return false;
818}
819
Lydia Wang1564b282009-10-10 19:07:52 +0800820static int via_smart51_get(struct snd_kcontrol *kcontrol,
821 struct snd_ctl_elem_value *ucontrol)
822{
823 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
824 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800825
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200826 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800827 return 0;
828}
829
830static int via_smart51_put(struct snd_kcontrol *kcontrol,
831 struct snd_ctl_elem_value *ucontrol)
832{
833 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
834 struct via_spec *spec = codec->spec;
835 int out_in = *ucontrol->value.integer.value
836 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800837 int i;
838
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200839 for (i = 0; i < spec->smart51_nums; i++) {
840 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200841 unsigned int parm;
842
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200843 parm = snd_hda_codec_read(codec, nid, 0,
844 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
845 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
846 parm |= out_in;
847 snd_hda_codec_write(codec, nid, 0,
848 AC_VERB_SET_PIN_WIDGET_CONTROL,
849 parm);
850 if (out_in == AC_PINCTL_OUT_EN) {
851 mute_aa_path(codec, 1);
852 notify_aa_path_ctls(codec);
853 }
Lydia Wang1564b282009-10-10 19:07:52 +0800854 }
855 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800856 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800857 return 1;
858}
859
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200860static const struct snd_kcontrol_new via_smart51_mixer = {
861 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
862 .name = "Smart 5.1",
863 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200864 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200865 .get = via_smart51_get,
866 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800867};
868
Takashi Iwaif4a78282011-06-17 18:46:48 +0200869static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100870{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200871 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100872
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200873 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800874 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200875 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100876 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100877 return 0;
878}
879
Takashi Iwaiada509e2011-06-20 15:40:19 +0200880/* check AA path's mute status */
881static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800882{
Lydia Wangf5271102009-10-10 19:07:35 +0800883 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200884 const struct hda_amp_list *p;
885 int i, ch, v;
886
887 for (i = 0; i < spec->num_loopbacks; i++) {
888 p = &spec->loopback_list[i];
889 for (ch = 0; ch < 2; ch++) {
890 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
891 p->idx);
892 if (!(v & HDA_AMP_MUTE) && v > 0)
893 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800894 }
895 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200896 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800897}
898
899/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200900static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800901{
902 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200903 bool enable;
904 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800905
Takashi Iwaiada509e2011-06-20 15:40:19 +0200906 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800907
908 /* decide low current mode's verb & parameter */
909 switch (spec->codec_type) {
910 case VT1708B_8CH:
911 case VT1708B_4CH:
912 verb = 0xf70;
913 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
914 break;
915 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800916 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800917 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800918 verb = 0xf73;
919 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
920 break;
921 case VT1702:
922 verb = 0xf73;
923 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
924 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800925 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800926 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800927 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800928 verb = 0xf93;
929 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
930 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800931 default:
932 return; /* other codecs are not supported */
933 }
934 /* send verb */
935 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
936}
937
Joseph Chanc577b8a2006-11-29 15:29:40 +0100938/*
939 * generic initialization of ADC, input mixers and output mixers
940 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200941static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800942 /* power down jack detect function */
943 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100944 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100945};
946
Takashi Iwaiada509e2011-06-20 15:40:19 +0200947static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200948{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200949 struct via_spec *spec = codec->spec;
950
951 if (active)
952 spec->num_active_streams++;
953 else
954 spec->num_active_streams--;
955 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200956}
957
Takashi Iwaiece8d042011-06-19 16:24:21 +0200958static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100959 struct hda_codec *codec,
960 struct snd_pcm_substream *substream)
961{
962 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200963 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200964 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200965
Takashi Iwai25250502011-06-30 17:24:47 +0200966 spec->multiout.hp_nid = 0;
967 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
968 if (!spec->hp_independent_mode) {
969 if (!spec->hp_indep_shared)
970 spec->multiout.hp_nid = spec->hp_dac_nid;
971 } else {
972 if (spec->hp_indep_shared)
973 spec->multiout.num_dacs = cfg->line_outs - 1;
974 }
975 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200976 set_stream_active(codec, true);
977 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
978 hinfo);
979 if (err < 0) {
980 spec->multiout.hp_nid = 0;
981 set_stream_active(codec, false);
982 return err;
983 }
984 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100985}
986
Takashi Iwaiece8d042011-06-19 16:24:21 +0200987static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +0200988 struct hda_codec *codec,
989 struct snd_pcm_substream *substream)
990{
Takashi Iwaiece8d042011-06-19 16:24:21 +0200991 struct via_spec *spec = codec->spec;
992
993 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200994 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +0200995 return 0;
996}
997
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200998static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
999 struct hda_codec *codec,
1000 struct snd_pcm_substream *substream)
1001{
1002 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001003
Takashi Iwaiece8d042011-06-19 16:24:21 +02001004 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001005 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001006 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1007 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001008 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001009 return 0;
1010}
1011
1012static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1013 struct hda_codec *codec,
1014 struct snd_pcm_substream *substream)
1015{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001016 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001017 return 0;
1018}
1019
1020static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1021 struct hda_codec *codec,
1022 unsigned int stream_tag,
1023 unsigned int format,
1024 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001025{
1026 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001027
Takashi Iwaiece8d042011-06-19 16:24:21 +02001028 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1029 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001030 vt1708_start_hp_work(spec);
1031 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001032}
1033
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001034static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1035 struct hda_codec *codec,
1036 unsigned int stream_tag,
1037 unsigned int format,
1038 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001039{
1040 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001041
Takashi Iwaiece8d042011-06-19 16:24:21 +02001042 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1043 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001044 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001045 return 0;
1046}
1047
1048static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1049 struct hda_codec *codec,
1050 struct snd_pcm_substream *substream)
1051{
1052 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001053
Takashi Iwaiece8d042011-06-19 16:24:21 +02001054 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001055 vt1708_stop_hp_work(spec);
1056 return 0;
1057}
1058
1059static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1060 struct hda_codec *codec,
1061 struct snd_pcm_substream *substream)
1062{
1063 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001064
Takashi Iwaiece8d042011-06-19 16:24:21 +02001065 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001066 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001067 return 0;
1068}
1069
Joseph Chanc577b8a2006-11-29 15:29:40 +01001070/*
1071 * Digital out
1072 */
1073static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1074 struct hda_codec *codec,
1075 struct snd_pcm_substream *substream)
1076{
1077 struct via_spec *spec = codec->spec;
1078 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1079}
1080
1081static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1082 struct hda_codec *codec,
1083 struct snd_pcm_substream *substream)
1084{
1085 struct via_spec *spec = codec->spec;
1086 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1087}
1088
Harald Welte5691ec72008-09-15 22:42:26 +08001089static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001090 struct hda_codec *codec,
1091 unsigned int stream_tag,
1092 unsigned int format,
1093 struct snd_pcm_substream *substream)
1094{
1095 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001096 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1097 stream_tag, format, substream);
1098}
Harald Welte5691ec72008-09-15 22:42:26 +08001099
Takashi Iwai9da29272009-05-07 16:31:14 +02001100static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1101 struct hda_codec *codec,
1102 struct snd_pcm_substream *substream)
1103{
1104 struct via_spec *spec = codec->spec;
1105 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001106 return 0;
1107}
1108
Joseph Chanc577b8a2006-11-29 15:29:40 +01001109/*
1110 * Analog capture
1111 */
1112static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1113 struct hda_codec *codec,
1114 unsigned int stream_tag,
1115 unsigned int format,
1116 struct snd_pcm_substream *substream)
1117{
1118 struct via_spec *spec = codec->spec;
1119
1120 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1121 stream_tag, 0, format);
1122 return 0;
1123}
1124
1125static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1126 struct hda_codec *codec,
1127 struct snd_pcm_substream *substream)
1128{
1129 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001130 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001131 return 0;
1132}
1133
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001134/* analog capture with dynamic ADC switching */
1135static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1136 struct hda_codec *codec,
1137 unsigned int stream_tag,
1138 unsigned int format,
1139 struct snd_pcm_substream *substream)
1140{
1141 struct via_spec *spec = codec->spec;
1142 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1143
1144 spec->cur_adc = spec->adc_nids[adc_idx];
1145 spec->cur_adc_stream_tag = stream_tag;
1146 spec->cur_adc_format = format;
1147 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1148 return 0;
1149}
1150
1151static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1152 struct hda_codec *codec,
1153 struct snd_pcm_substream *substream)
1154{
1155 struct via_spec *spec = codec->spec;
1156
1157 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1158 spec->cur_adc = 0;
1159 return 0;
1160}
1161
1162/* re-setup the stream if running; called from input-src put */
1163static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1164{
1165 struct via_spec *spec = codec->spec;
1166 int adc_idx = spec->inputs[cur].adc_idx;
1167 hda_nid_t adc = spec->adc_nids[adc_idx];
1168
1169 if (spec->cur_adc && spec->cur_adc != adc) {
1170 /* stream is running, let's swap the current ADC */
1171 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1172 spec->cur_adc = adc;
1173 snd_hda_codec_setup_stream(codec, adc,
1174 spec->cur_adc_stream_tag, 0,
1175 spec->cur_adc_format);
1176 return true;
1177 }
1178 return false;
1179}
1180
Takashi Iwai9af74212011-06-18 16:17:45 +02001181static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001182 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001183 .channels_min = 2,
1184 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001185 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001186 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001187 .open = via_playback_multi_pcm_open,
1188 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001189 .prepare = via_playback_multi_pcm_prepare,
1190 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001191 },
1192};
1193
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001194static const struct hda_pcm_stream via_pcm_hp_playback = {
1195 .substreams = 1,
1196 .channels_min = 2,
1197 .channels_max = 2,
1198 /* NID is set in via_build_pcms */
1199 .ops = {
1200 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001201 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001202 .prepare = via_playback_hp_pcm_prepare,
1203 .cleanup = via_playback_hp_pcm_cleanup
1204 },
1205};
1206
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001207static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001208 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001209 .channels_min = 2,
1210 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001211 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001212 /* We got noisy outputs on the right channel on VT1708 when
1213 * 24bit samples are used. Until any workaround is found,
1214 * disable the 24bit format, so far.
1215 */
1216 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1217 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001218 .open = via_playback_multi_pcm_open,
1219 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001220 .prepare = via_playback_multi_pcm_prepare,
1221 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001222 },
1223};
1224
Takashi Iwai9af74212011-06-18 16:17:45 +02001225static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001226 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001227 .channels_min = 2,
1228 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001229 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001230 .ops = {
1231 .prepare = via_capture_pcm_prepare,
1232 .cleanup = via_capture_pcm_cleanup
1233 },
1234};
1235
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001236static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1237 .substreams = 1,
1238 .channels_min = 2,
1239 .channels_max = 2,
1240 /* NID is set in via_build_pcms */
1241 .ops = {
1242 .prepare = via_dyn_adc_capture_pcm_prepare,
1243 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1244 },
1245};
1246
Takashi Iwai9af74212011-06-18 16:17:45 +02001247static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001248 .substreams = 1,
1249 .channels_min = 2,
1250 .channels_max = 2,
1251 /* NID is set in via_build_pcms */
1252 .ops = {
1253 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001254 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001255 .prepare = via_dig_playback_pcm_prepare,
1256 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001257 },
1258};
1259
Takashi Iwai9af74212011-06-18 16:17:45 +02001260static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001261 .substreams = 1,
1262 .channels_min = 2,
1263 .channels_max = 2,
1264};
1265
Takashi Iwai370bafb2011-06-20 12:47:45 +02001266/*
1267 * slave controls for virtual master
1268 */
1269static const char * const via_slave_vols[] = {
1270 "Front Playback Volume",
1271 "Surround Playback Volume",
1272 "Center Playback Volume",
1273 "LFE Playback Volume",
1274 "Side Playback Volume",
1275 "Headphone Playback Volume",
1276 "Speaker Playback Volume",
1277 NULL,
1278};
1279
1280static const char * const via_slave_sws[] = {
1281 "Front Playback Switch",
1282 "Surround Playback Switch",
1283 "Center Playback Switch",
1284 "LFE Playback Switch",
1285 "Side Playback Switch",
1286 "Headphone Playback Switch",
1287 "Speaker Playback Switch",
1288 NULL,
1289};
1290
Joseph Chanc577b8a2006-11-29 15:29:40 +01001291static int via_build_controls(struct hda_codec *codec)
1292{
1293 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001294 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001295 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001296
Takashi Iwai24088a52011-06-17 16:59:21 +02001297 if (spec->set_widgets_power_state)
1298 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1299 return -ENOMEM;
1300
Joseph Chanc577b8a2006-11-29 15:29:40 +01001301 for (i = 0; i < spec->num_mixers; i++) {
1302 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1303 if (err < 0)
1304 return err;
1305 }
1306
1307 if (spec->multiout.dig_out_nid) {
1308 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001309 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001310 spec->multiout.dig_out_nid);
1311 if (err < 0)
1312 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001313 err = snd_hda_create_spdif_share_sw(codec,
1314 &spec->multiout);
1315 if (err < 0)
1316 return err;
1317 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001318 }
1319 if (spec->dig_in_nid) {
1320 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1321 if (err < 0)
1322 return err;
1323 }
Lydia Wang17314372009-10-10 19:07:37 +08001324
Takashi Iwai370bafb2011-06-20 12:47:45 +02001325 /* if we have no master control, let's create it */
1326 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1327 unsigned int vmaster_tlv[4];
1328 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1329 HDA_OUTPUT, vmaster_tlv);
1330 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1331 vmaster_tlv, via_slave_vols);
1332 if (err < 0)
1333 return err;
1334 }
1335 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1336 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1337 NULL, via_slave_sws);
1338 if (err < 0)
1339 return err;
1340 }
1341
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001342 /* assign Capture Source enums to NID */
1343 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1344 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001345 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001346 if (err < 0)
1347 return err;
1348 }
1349
Lydia Wang17314372009-10-10 19:07:37 +08001350 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001351 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001352 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001353
Takashi Iwai603c4012008-07-30 15:01:44 +02001354 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001355 return 0;
1356}
1357
1358static int via_build_pcms(struct hda_codec *codec)
1359{
1360 struct via_spec *spec = codec->spec;
1361 struct hda_pcm *info = spec->pcm_rec;
1362
1363 codec->num_pcms = 1;
1364 codec->pcm_info = info;
1365
Takashi Iwai82673bc2011-06-17 16:24:21 +02001366 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1367 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001368 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001369
1370 if (!spec->stream_analog_playback)
1371 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001372 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001373 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001374 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1375 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001376 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1377 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001378
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001379 if (!spec->stream_analog_capture) {
1380 if (spec->dyn_adc_switch)
1381 spec->stream_analog_capture =
1382 &via_pcm_dyn_adc_analog_capture;
1383 else
1384 spec->stream_analog_capture = &via_pcm_analog_capture;
1385 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001386 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1387 *spec->stream_analog_capture;
1388 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001389 if (!spec->dyn_adc_switch)
1390 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1391 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001392
1393 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1394 codec->num_pcms++;
1395 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001396 snprintf(spec->stream_name_digital,
1397 sizeof(spec->stream_name_digital),
1398 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001399 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001400 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001401 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001402 if (!spec->stream_digital_playback)
1403 spec->stream_digital_playback =
1404 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001405 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001406 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001407 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1408 spec->multiout.dig_out_nid;
1409 }
1410 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001411 if (!spec->stream_digital_capture)
1412 spec->stream_digital_capture =
1413 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001414 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001415 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001416 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1417 spec->dig_in_nid;
1418 }
1419 }
1420
Takashi Iwaiece8d042011-06-19 16:24:21 +02001421 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001422 codec->num_pcms++;
1423 info++;
1424 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1425 "%s HP", codec->chip_name);
1426 info->name = spec->stream_name_hp;
1427 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1428 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001429 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001430 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001431 return 0;
1432}
1433
1434static void via_free(struct hda_codec *codec)
1435{
1436 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001437
1438 if (!spec)
1439 return;
1440
Takashi Iwai603c4012008-07-30 15:01:44 +02001441 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001442 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001443 kfree(spec->bind_cap_vol);
1444 kfree(spec->bind_cap_sw);
1445 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001446}
1447
Takashi Iwai64be2852011-06-17 16:51:39 +02001448/* mute/unmute outputs */
1449static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1450 hda_nid_t *pins, bool mute)
1451{
1452 int i;
1453 for (i = 0; i < num_pins; i++)
1454 snd_hda_codec_write(codec, pins[i], 0,
1455 AC_VERB_SET_PIN_WIDGET_CONTROL,
1456 mute ? 0 : PIN_OUT);
1457}
1458
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001459/* mute internal speaker if line-out is plugged */
1460static void via_line_automute(struct hda_codec *codec, int present)
1461{
1462 struct via_spec *spec = codec->spec;
1463
1464 if (!spec->autocfg.speaker_outs)
1465 return;
1466 if (!present)
1467 present = snd_hda_jack_detect(codec,
1468 spec->autocfg.line_out_pins[0]);
1469 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1470 spec->autocfg.speaker_pins,
1471 present);
1472}
1473
Harald Welte69e52a82008-09-09 15:57:32 +08001474/* mute internal speaker if HP is plugged */
1475static void via_hp_automute(struct hda_codec *codec)
1476{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001477 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001478 struct via_spec *spec = codec->spec;
1479
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001480 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001481 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001482 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001483 if (spec->smart51_enabled)
1484 nums = spec->autocfg.line_outs + spec->smart51_nums;
1485 else
1486 nums = spec->autocfg.line_outs;
1487 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001488 spec->autocfg.line_out_pins,
1489 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001490 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001491 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001492}
1493
Harald Welte69e52a82008-09-09 15:57:32 +08001494static void via_gpio_control(struct hda_codec *codec)
1495{
1496 unsigned int gpio_data;
1497 unsigned int vol_counter;
1498 unsigned int vol;
1499 unsigned int master_vol;
1500
1501 struct via_spec *spec = codec->spec;
1502
1503 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1504 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1505
1506 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1507 0xF84, 0) & 0x3F0000) >> 16;
1508
1509 vol = vol_counter & 0x1F;
1510 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1511 AC_VERB_GET_AMP_GAIN_MUTE,
1512 AC_AMP_GET_INPUT);
1513
1514 if (gpio_data == 0x02) {
1515 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001516 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1517 AC_VERB_SET_PIN_WIDGET_CONTROL,
1518 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001519 if (vol_counter & 0x20) {
1520 /* decrease volume */
1521 if (vol > master_vol)
1522 vol = master_vol;
1523 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1524 0, HDA_AMP_VOLMASK,
1525 master_vol-vol);
1526 } else {
1527 /* increase volume */
1528 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1529 HDA_AMP_VOLMASK,
1530 ((master_vol+vol) > 0x2A) ? 0x2A :
1531 (master_vol+vol));
1532 }
1533 } else if (!(gpio_data & 0x02)) {
1534 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001535 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1536 AC_VERB_SET_PIN_WIDGET_CONTROL,
1537 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001538 }
1539}
1540
1541/* unsolicited event for jack sensing */
1542static void via_unsol_event(struct hda_codec *codec,
1543 unsigned int res)
1544{
1545 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001546
Lydia Wanga34df192009-10-10 19:08:01 +08001547 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001548 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001549
1550 res &= ~VIA_JACK_EVENT;
1551
1552 if (res == VIA_HP_EVENT)
1553 via_hp_automute(codec);
1554 else if (res == VIA_GPIO_EVENT)
1555 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001556 else if (res == VIA_LINE_EVENT)
1557 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001558}
1559
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001560#ifdef SND_HDA_NEEDS_RESUME
1561static int via_suspend(struct hda_codec *codec, pm_message_t state)
1562{
1563 struct via_spec *spec = codec->spec;
1564 vt1708_stop_hp_work(spec);
1565 return 0;
1566}
1567#endif
1568
Takashi Iwaicb53c622007-08-10 17:21:45 +02001569#ifdef CONFIG_SND_HDA_POWER_SAVE
1570static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1571{
1572 struct via_spec *spec = codec->spec;
1573 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1574}
1575#endif
1576
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577/*
1578 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001579
1580static int via_init(struct hda_codec *codec);
1581
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001582static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001583 .build_controls = via_build_controls,
1584 .build_pcms = via_build_pcms,
1585 .init = via_init,
1586 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001587 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001588#ifdef SND_HDA_NEEDS_RESUME
1589 .suspend = via_suspend,
1590#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001591#ifdef CONFIG_SND_HDA_POWER_SAVE
1592 .check_power_status = via_check_power_status,
1593#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001594};
1595
Takashi Iwai4a796162011-06-17 17:53:38 +02001596static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001597{
Takashi Iwai4a796162011-06-17 17:53:38 +02001598 struct via_spec *spec = codec->spec;
1599 int i;
1600
1601 for (i = 0; i < spec->multiout.num_dacs; i++) {
1602 if (spec->multiout.dac_nids[i] == dac)
1603 return false;
1604 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001605 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001606 return false;
1607 return true;
1608}
1609
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001610static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001611 hda_nid_t target_dac, struct nid_path *path,
1612 int depth, int wid_type)
1613{
1614 hda_nid_t conn[8];
1615 int i, nums;
1616
1617 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1618 for (i = 0; i < nums; i++) {
1619 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1620 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001621 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1622 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001623 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001624 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001625 return false;
1626 for (i = 0; i < nums; i++) {
1627 unsigned int type;
1628 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1629 if (type == AC_WID_AUD_OUT ||
1630 (wid_type != -1 && type != wid_type))
1631 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001632 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001633 path, depth + 1, AC_WID_AUD_SEL))
1634 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001635 }
1636 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001637
1638 found:
1639 path->path[path->depth] = conn[i];
1640 path->idx[path->depth] = i;
1641 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1642 path->multi[path->depth] = 1;
1643 path->depth++;
1644 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001645}
1646
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001647static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1648 hda_nid_t target_dac, struct nid_path *path)
1649{
1650 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1651 path->path[path->depth] = nid;
1652 path->depth++;
1653 return true;
1654 }
1655 return false;
1656}
1657
Takashi Iwai4a796162011-06-17 17:53:38 +02001658static int via_auto_fill_dac_nids(struct hda_codec *codec)
1659{
1660 struct via_spec *spec = codec->spec;
1661 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001662 int i;
1663 hda_nid_t nid;
1664
Joseph Chanc577b8a2006-11-29 15:29:40 +01001665 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001666 spec->multiout.num_dacs = cfg->line_outs;
1667 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001668 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001669 if (!nid)
1670 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001671 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1672 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001673 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001674 return 0;
1675}
1676
Takashi Iwai4a796162011-06-17 17:53:38 +02001677static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001678 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001679{
Takashi Iwai4a796162011-06-17 17:53:38 +02001680 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001681 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001682 hda_nid_t dac, pin, sel, nid;
1683 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001684
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001685 dac = check_dac ? path->path[0] : 0;
1686 pin = path->path[path->depth - 1];
1687 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001688
Takashi Iwai8df2a312011-06-21 11:48:29 +02001689 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001690 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001691 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001692 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001693 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1694 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001695 else
1696 nid = 0;
1697 if (nid) {
1698 sprintf(name, "%s Playback Volume", pfx);
1699 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001700 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001701 if (err < 0)
1702 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001703 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001704 }
1705
Takashi Iwai8df2a312011-06-21 11:48:29 +02001706 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001707 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001708 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001709 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001710 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1711 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001712 else
1713 nid = 0;
1714 if (nid) {
1715 sprintf(name, "%s Playback Switch", pfx);
1716 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1717 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1718 if (err < 0)
1719 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001720 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001721 }
1722 return 0;
1723}
1724
Takashi Iwaif4a78282011-06-17 18:46:48 +02001725static void mangle_smart51(struct hda_codec *codec)
1726{
1727 struct via_spec *spec = codec->spec;
1728 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001729 struct auto_pin_cfg_item *ins = cfg->inputs;
1730 int i, j, nums, attr;
1731 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001732
Takashi Iwai0f98c242011-06-21 12:51:33 +02001733 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1734 nums = 0;
1735 for (i = 0; i < cfg->num_inputs; i++) {
1736 unsigned int def;
1737 if (ins[i].type > AUTO_PIN_LINE_IN)
1738 continue;
1739 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1740 if (snd_hda_get_input_pin_attr(def) != attr)
1741 continue;
1742 for (j = 0; j < nums; j++)
1743 if (ins[pins[j]].type < ins[i].type) {
1744 memmove(pins + j + 1, pins + j,
1745 (nums - j - 1) * sizeof(int));
1746 break;
1747 }
1748 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001749 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001750 }
1751 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001752 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001753 for (i = 0; i < nums; i++) {
1754 hda_nid_t pin = ins[pins[i]].pin;
1755 spec->smart51_pins[spec->smart51_nums++] = pin;
1756 cfg->line_out_pins[cfg->line_outs++] = pin;
1757 if (cfg->line_outs == 3)
1758 break;
1759 }
1760 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001761 }
1762}
1763
Takashi Iwai4a796162011-06-17 17:53:38 +02001764/* add playback controls from the parsed DAC table */
1765static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1766{
1767 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001768 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001769 static const char * const chname[4] = {
1770 "Front", "Surround", "C/LFE", "Side"
1771 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001772 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001773 int old_line_outs;
1774
1775 /* check smart51 */
1776 old_line_outs = cfg->line_outs;
1777 if (cfg->line_outs == 1)
1778 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001779
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001780 err = via_auto_fill_dac_nids(codec);
1781 if (err < 0)
1782 return err;
1783
Takashi Iwai4a796162011-06-17 17:53:38 +02001784 for (i = 0; i < cfg->line_outs; i++) {
1785 hda_nid_t pin, dac;
1786 pin = cfg->line_out_pins[i];
1787 dac = spec->multiout.dac_nids[i];
1788 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001789 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001790 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001791 err = create_ch_ctls(codec, "Center", 1, true,
1792 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001793 if (err < 0)
1794 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001795 err = create_ch_ctls(codec, "LFE", 2, true,
1796 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797 if (err < 0)
1798 return err;
1799 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001800 const char *pfx = chname[i];
1801 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1802 cfg->line_outs == 1)
1803 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001804 err = create_ch_ctls(codec, pfx, 3, true,
1805 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001806 if (err < 0)
1807 return err;
1808 }
1809 }
1810
Takashi Iwai4a796162011-06-17 17:53:38 +02001811 idx = get_connection_index(codec, spec->aa_mix_nid,
1812 spec->multiout.dac_nids[0]);
1813 if (idx >= 0) {
1814 /* add control to mixer */
1815 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1816 "PCM Playback Volume",
1817 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1818 idx, HDA_INPUT));
1819 if (err < 0)
1820 return err;
1821 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1822 "PCM Playback Switch",
1823 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1824 idx, HDA_INPUT));
1825 if (err < 0)
1826 return err;
1827 }
1828
Takashi Iwaif4a78282011-06-17 18:46:48 +02001829 cfg->line_outs = old_line_outs;
1830
Joseph Chanc577b8a2006-11-29 15:29:40 +01001831 return 0;
1832}
1833
Takashi Iwai4a796162011-06-17 17:53:38 +02001834static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001835{
Takashi Iwai4a796162011-06-17 17:53:38 +02001836 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001837 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001838 int err;
1839
1840 if (!pin)
1841 return 0;
1842
Takashi Iwai8df2a312011-06-21 11:48:29 +02001843 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001844 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001845 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1846 parse_output_path(codec, pin,
1847 spec->multiout.dac_nids[HDA_SIDE],
1848 &spec->hp_path)) {
1849 spec->hp_dac_nid = spec->hp_path.path[0];
1850 spec->hp_indep_shared = true;
1851 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001852
Takashi Iwaiece8d042011-06-19 16:24:21 +02001853 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001854 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001855 !spec->hp_dac_nid)
1856 return 0;
1857
Takashi Iwai25250502011-06-30 17:24:47 +02001858 if (spec->hp_dac_nid && !spec->hp_indep_shared)
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001859 path = &spec->hp_path;
1860 else
1861 path = &spec->hp_dep_path;
1862 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001863 if (err < 0)
1864 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001865 if (spec->hp_dac_nid) {
1866 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1867 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1868 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001869
Joseph Chanc577b8a2006-11-29 15:29:40 +01001870 return 0;
1871}
1872
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001873static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1874{
1875 struct via_spec *spec = codec->spec;
1876 hda_nid_t pin, dac;
1877
1878 pin = spec->autocfg.speaker_pins[0];
1879 if (!spec->autocfg.speaker_outs || !pin)
1880 return 0;
1881
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001882 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1883 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001884 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001885 return create_ch_ctls(codec, "Speaker", 3, true,
1886 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001887 }
1888 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001889 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001890 return create_ch_ctls(codec, "Speaker", 3, false,
1891 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001892
1893 return 0;
1894}
1895
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001896/* look for ADCs */
1897static int via_fill_adcs(struct hda_codec *codec)
1898{
1899 struct via_spec *spec = codec->spec;
1900 hda_nid_t nid = codec->start_nid;
1901 int i;
1902
1903 for (i = 0; i < codec->num_nodes; i++, nid++) {
1904 unsigned int wcaps = get_wcaps(codec, nid);
1905 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1906 continue;
1907 if (wcaps & AC_WCAP_DIGITAL)
1908 continue;
1909 if (!(wcaps & AC_WCAP_CONN_LIST))
1910 continue;
1911 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1912 return -ENOMEM;
1913 spec->adc_nids[spec->num_adc_nids++] = nid;
1914 }
1915 return 0;
1916}
1917
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001918/* input-src control */
1919static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1920 struct snd_ctl_elem_info *uinfo)
1921{
1922 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1923 struct via_spec *spec = codec->spec;
1924
1925 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1926 uinfo->count = 1;
1927 uinfo->value.enumerated.items = spec->num_inputs;
1928 if (uinfo->value.enumerated.item >= spec->num_inputs)
1929 uinfo->value.enumerated.item = spec->num_inputs - 1;
1930 strcpy(uinfo->value.enumerated.name,
1931 spec->inputs[uinfo->value.enumerated.item].label);
1932 return 0;
1933}
1934
1935static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1936 struct snd_ctl_elem_value *ucontrol)
1937{
1938 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1939 struct via_spec *spec = codec->spec;
1940 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1941
1942 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1943 return 0;
1944}
1945
1946static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1947 struct snd_ctl_elem_value *ucontrol)
1948{
1949 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1950 struct via_spec *spec = codec->spec;
1951 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1952 hda_nid_t mux;
1953 int cur;
1954
1955 cur = ucontrol->value.enumerated.item[0];
1956 if (cur < 0 || cur >= spec->num_inputs)
1957 return -EINVAL;
1958 if (spec->cur_mux[idx] == cur)
1959 return 0;
1960 spec->cur_mux[idx] = cur;
1961 if (spec->dyn_adc_switch) {
1962 int adc_idx = spec->inputs[cur].adc_idx;
1963 mux = spec->mux_nids[adc_idx];
1964 via_dyn_adc_pcm_resetup(codec, cur);
1965 } else {
1966 mux = spec->mux_nids[idx];
1967 if (snd_BUG_ON(!mux))
1968 return -EINVAL;
1969 }
1970
1971 if (mux) {
1972 /* switch to D0 beofre change index */
1973 if (snd_hda_codec_read(codec, mux, 0,
1974 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1975 snd_hda_codec_write(codec, mux, 0,
1976 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1977 snd_hda_codec_write(codec, mux, 0,
1978 AC_VERB_SET_CONNECT_SEL,
1979 spec->inputs[cur].mux_idx);
1980 }
1981
1982 /* update jack power state */
1983 set_widgets_power_state(codec);
1984 return 0;
1985}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001986
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001987static const struct snd_kcontrol_new via_input_src_ctl = {
1988 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1989 /* The multiple "Capture Source" controls confuse alsamixer
1990 * So call somewhat different..
1991 */
1992 /* .name = "Capture Source", */
1993 .name = "Input Source",
1994 .info = via_mux_enum_info,
1995 .get = via_mux_enum_get,
1996 .put = via_mux_enum_put,
1997};
1998
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001999static int create_input_src_ctls(struct hda_codec *codec, int count)
2000{
2001 struct via_spec *spec = codec->spec;
2002 struct snd_kcontrol_new *knew;
2003
2004 if (spec->num_inputs <= 1 || !count)
2005 return 0; /* no need for single src */
2006
2007 knew = via_clone_control(spec, &via_input_src_ctl);
2008 if (!knew)
2009 return -ENOMEM;
2010 knew->count = count;
2011 return 0;
2012}
2013
2014/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002015static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2016{
2017 struct hda_amp_list *list;
2018
2019 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2020 return;
2021 list = spec->loopback_list + spec->num_loopbacks;
2022 list->nid = mix;
2023 list->dir = HDA_INPUT;
2024 list->idx = idx;
2025 spec->num_loopbacks++;
2026 spec->loopback.amplist = spec->loopback_list;
2027}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002028
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002029static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002030 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002031{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002032 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002033}
2034
2035/* add the input-route to the given pin */
2036static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002037{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002038 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002039 int c, idx;
2040
2041 spec->inputs[spec->num_inputs].adc_idx = -1;
2042 spec->inputs[spec->num_inputs].pin = pin;
2043 for (c = 0; c < spec->num_adc_nids; c++) {
2044 if (spec->mux_nids[c]) {
2045 idx = get_connection_index(codec, spec->mux_nids[c],
2046 pin);
2047 if (idx < 0)
2048 continue;
2049 spec->inputs[spec->num_inputs].mux_idx = idx;
2050 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002051 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002052 continue;
2053 }
2054 spec->inputs[spec->num_inputs].adc_idx = c;
2055 /* Can primary ADC satisfy all inputs? */
2056 if (!spec->dyn_adc_switch &&
2057 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2058 snd_printd(KERN_INFO
2059 "via: dynamic ADC switching enabled\n");
2060 spec->dyn_adc_switch = 1;
2061 }
2062 return true;
2063 }
2064 return false;
2065}
2066
2067static int get_mux_nids(struct hda_codec *codec);
2068
2069/* parse input-routes; fill ADCs, MUXs and input-src entries */
2070static int parse_analog_inputs(struct hda_codec *codec)
2071{
2072 struct via_spec *spec = codec->spec;
2073 const struct auto_pin_cfg *cfg = &spec->autocfg;
2074 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002075
2076 err = via_fill_adcs(codec);
2077 if (err < 0)
2078 return err;
2079 err = get_mux_nids(codec);
2080 if (err < 0)
2081 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002082
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002083 /* fill all input-routes */
2084 for (i = 0; i < cfg->num_inputs; i++) {
2085 if (add_input_route(codec, cfg->inputs[i].pin))
2086 spec->inputs[spec->num_inputs++].label =
2087 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002088 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002089
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002090 /* check for internal loopback recording */
2091 if (spec->aa_mix_nid &&
2092 add_input_route(codec, spec->aa_mix_nid))
2093 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2094
2095 return 0;
2096}
2097
2098/* create analog-loopback volume/switch controls */
2099static int create_loopback_ctls(struct hda_codec *codec)
2100{
2101 struct via_spec *spec = codec->spec;
2102 const struct auto_pin_cfg *cfg = &spec->autocfg;
2103 const char *prev_label = NULL;
2104 int type_idx = 0;
2105 int i, j, err, idx;
2106
2107 if (!spec->aa_mix_nid)
2108 return 0;
2109
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002110 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002111 hda_nid_t pin = cfg->inputs[i].pin;
2112 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2113
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002114 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002115 type_idx++;
2116 else
2117 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002118 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002119 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2120 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002121 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002122 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002123 if (err < 0)
2124 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002125 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002126 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002127
2128 /* remember the label for smart51 control */
2129 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002130 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002131 spec->smart51_idxs[j] = idx;
2132 spec->smart51_labels[j] = label;
2133 break;
2134 }
2135 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002136 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002137 return 0;
2138}
2139
2140/* create mic-boost controls (if present) */
2141static int create_mic_boost_ctls(struct hda_codec *codec)
2142{
2143 struct via_spec *spec = codec->spec;
2144 const struct auto_pin_cfg *cfg = &spec->autocfg;
2145 int i, err;
2146
2147 for (i = 0; i < cfg->num_inputs; i++) {
2148 hda_nid_t pin = cfg->inputs[i].pin;
2149 unsigned int caps;
2150 const char *label;
2151 char name[32];
2152
2153 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2154 continue;
2155 caps = query_amp_caps(codec, pin, HDA_INPUT);
2156 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2157 continue;
2158 label = hda_get_autocfg_input_label(codec, cfg, i);
2159 snprintf(name, sizeof(name), "%s Boost Volume", label);
2160 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2161 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2162 if (err < 0)
2163 return err;
2164 }
2165 return 0;
2166}
2167
2168/* create capture and input-src controls for multiple streams */
2169static int create_multi_adc_ctls(struct hda_codec *codec)
2170{
2171 struct via_spec *spec = codec->spec;
2172 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002173
2174 /* create capture mixer elements */
2175 for (i = 0; i < spec->num_adc_nids; i++) {
2176 hda_nid_t adc = spec->adc_nids[i];
2177 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2178 "Capture Volume", i,
2179 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2180 HDA_INPUT));
2181 if (err < 0)
2182 return err;
2183 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2184 "Capture Switch", i,
2185 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2186 HDA_INPUT));
2187 if (err < 0)
2188 return err;
2189 }
2190
2191 /* input-source control */
2192 for (i = 0; i < spec->num_adc_nids; i++)
2193 if (!spec->mux_nids[i])
2194 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002195 err = create_input_src_ctls(codec, i);
2196 if (err < 0)
2197 return err;
2198 return 0;
2199}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002200
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002201/* bind capture volume/switch */
2202static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2203 HDA_BIND_VOL("Capture Volume", 0);
2204static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2205 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002206
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002207static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2208 struct hda_ctl_ops *ops)
2209{
2210 struct hda_bind_ctls *ctl;
2211 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002212
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002213 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2214 if (!ctl)
2215 return -ENOMEM;
2216 ctl->ops = ops;
2217 for (i = 0; i < spec->num_adc_nids; i++)
2218 ctl->values[i] =
2219 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2220 *ctl_ret = ctl;
2221 return 0;
2222}
2223
2224/* create capture and input-src controls for dynamic ADC-switch case */
2225static int create_dyn_adc_ctls(struct hda_codec *codec)
2226{
2227 struct via_spec *spec = codec->spec;
2228 struct snd_kcontrol_new *knew;
2229 int err;
2230
2231 /* set up the bind capture ctls */
2232 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2233 if (err < 0)
2234 return err;
2235 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2236 if (err < 0)
2237 return err;
2238
2239 /* create capture mixer elements */
2240 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2241 if (!knew)
2242 return -ENOMEM;
2243 knew->private_value = (long)spec->bind_cap_vol;
2244
2245 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2246 if (!knew)
2247 return -ENOMEM;
2248 knew->private_value = (long)spec->bind_cap_sw;
2249
2250 /* input-source control */
2251 err = create_input_src_ctls(codec, 1);
2252 if (err < 0)
2253 return err;
2254 return 0;
2255}
2256
2257/* parse and create capture-related stuff */
2258static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2259{
2260 struct via_spec *spec = codec->spec;
2261 int err;
2262
2263 err = parse_analog_inputs(codec);
2264 if (err < 0)
2265 return err;
2266 if (spec->dyn_adc_switch)
2267 err = create_dyn_adc_ctls(codec);
2268 else
2269 err = create_multi_adc_ctls(codec);
2270 if (err < 0)
2271 return err;
2272 err = create_loopback_ctls(codec);
2273 if (err < 0)
2274 return err;
2275 err = create_mic_boost_ctls(codec);
2276 if (err < 0)
2277 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002278 return 0;
2279}
2280
Harald Welte76d9b0d2008-09-09 15:50:37 +08002281static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2282{
2283 unsigned int def_conf;
2284 unsigned char seqassoc;
2285
Takashi Iwai2f334f92009-02-20 14:37:42 +01002286 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002287 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2288 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002289 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2290 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2291 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2292 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002293 }
2294
2295 return;
2296}
2297
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002298static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002299 struct snd_ctl_elem_value *ucontrol)
2300{
2301 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2302 struct via_spec *spec = codec->spec;
2303
2304 if (spec->codec_type != VT1708)
2305 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002306 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002307 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002308 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002309 return 0;
2310}
2311
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002312static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002313 struct snd_ctl_elem_value *ucontrol)
2314{
2315 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2316 struct via_spec *spec = codec->spec;
2317 int change;
2318
2319 if (spec->codec_type != VT1708)
2320 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002321 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002322 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002323 == !spec->vt1708_jack_detect;
2324 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002325 mute_aa_path(codec, 1);
2326 notify_aa_path_ctls(codec);
2327 }
2328 return change;
2329}
2330
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002331static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2332 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2333 .name = "Jack Detect",
2334 .count = 1,
2335 .info = snd_ctl_boolean_mono_info,
2336 .get = vt1708_jack_detect_get,
2337 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002338};
2339
Takashi Iwai12daef62011-06-18 17:45:49 +02002340static void fill_dig_outs(struct hda_codec *codec);
2341static void fill_dig_in(struct hda_codec *codec);
2342
2343static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002344{
2345 struct via_spec *spec = codec->spec;
2346 int err;
2347
2348 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2349 if (err < 0)
2350 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002351 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002352 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002353
Takashi Iwai4a796162011-06-17 17:53:38 +02002354 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002355 if (err < 0)
2356 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002357 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002358 if (err < 0)
2359 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002360 err = via_auto_create_speaker_ctls(codec);
2361 if (err < 0)
2362 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002363 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002364 if (err < 0)
2365 return err;
2366
2367 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2368
Takashi Iwai12daef62011-06-18 17:45:49 +02002369 fill_dig_outs(codec);
2370 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002371
Takashi Iwai603c4012008-07-30 15:01:44 +02002372 if (spec->kctls.list)
2373 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002374
Joseph Chanc577b8a2006-11-29 15:29:40 +01002375
Takashi Iwai8df2a312011-06-21 11:48:29 +02002376 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002377 err = via_hp_build(codec);
2378 if (err < 0)
2379 return err;
2380 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002381
Takashi Iwaif4a78282011-06-17 18:46:48 +02002382 err = via_smart51_build(codec);
2383 if (err < 0)
2384 return err;
2385
Takashi Iwai5d417622011-06-20 11:32:27 +02002386 /* assign slave outs */
2387 if (spec->slave_dig_outs[0])
2388 codec->slave_dig_outs = spec->slave_dig_outs;
2389
Joseph Chanc577b8a2006-11-29 15:29:40 +01002390 return 1;
2391}
2392
Takashi Iwai5d417622011-06-20 11:32:27 +02002393static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002394{
Lydia Wang25eaba22009-10-10 19:08:43 +08002395 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002396 if (spec->multiout.dig_out_nid)
2397 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2398 if (spec->slave_dig_outs[0])
2399 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2400}
Lydia Wang25eaba22009-10-10 19:08:43 +08002401
Takashi Iwai5d417622011-06-20 11:32:27 +02002402static void via_auto_init_dig_in(struct hda_codec *codec)
2403{
2404 struct via_spec *spec = codec->spec;
2405 if (!spec->dig_in_nid)
2406 return;
2407 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2408 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2409}
2410
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002411/* initialize the unsolicited events */
2412static void via_auto_init_unsol_event(struct hda_codec *codec)
2413{
2414 struct via_spec *spec = codec->spec;
2415 struct auto_pin_cfg *cfg = &spec->autocfg;
2416 unsigned int ev;
2417 int i;
2418
2419 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2420 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2421 AC_VERB_SET_UNSOLICITED_ENABLE,
2422 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2423
2424 if (cfg->speaker_pins[0])
2425 ev = VIA_LINE_EVENT;
2426 else
2427 ev = 0;
2428 for (i = 0; i < cfg->line_outs; i++) {
2429 if (cfg->line_out_pins[i] &&
2430 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002431 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002432 AC_VERB_SET_UNSOLICITED_ENABLE,
2433 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2434 }
2435
2436 for (i = 0; i < cfg->num_inputs; i++) {
2437 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2438 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2439 AC_VERB_SET_UNSOLICITED_ENABLE,
2440 AC_USRSP_EN | VIA_JACK_EVENT);
2441 }
2442}
2443
Takashi Iwai5d417622011-06-20 11:32:27 +02002444static int via_init(struct hda_codec *codec)
2445{
2446 struct via_spec *spec = codec->spec;
2447 int i;
2448
2449 for (i = 0; i < spec->num_iverbs; i++)
2450 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2451
Joseph Chanc577b8a2006-11-29 15:29:40 +01002452 via_auto_init_multi_out(codec);
2453 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002454 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002455 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002456 via_auto_init_dig_outs(codec);
2457 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002458
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002459 via_auto_init_unsol_event(codec);
2460
2461 via_hp_automute(codec);
2462 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002463
Joseph Chanc577b8a2006-11-29 15:29:40 +01002464 return 0;
2465}
2466
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002467static void vt1708_update_hp_jack_state(struct work_struct *work)
2468{
2469 struct via_spec *spec = container_of(work, struct via_spec,
2470 vt1708_hp_work.work);
2471 if (spec->codec_type != VT1708)
2472 return;
2473 /* if jack state toggled */
2474 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002475 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002476 spec->vt1708_hp_present ^= 1;
2477 via_hp_automute(spec->codec);
2478 }
2479 vt1708_start_hp_work(spec);
2480}
2481
Takashi Iwai337b9d02009-07-07 18:18:59 +02002482static int get_mux_nids(struct hda_codec *codec)
2483{
2484 struct via_spec *spec = codec->spec;
2485 hda_nid_t nid, conn[8];
2486 unsigned int type;
2487 int i, n;
2488
2489 for (i = 0; i < spec->num_adc_nids; i++) {
2490 nid = spec->adc_nids[i];
2491 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002492 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002493 if (type == AC_WID_PIN)
2494 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002495 n = snd_hda_get_connections(codec, nid, conn,
2496 ARRAY_SIZE(conn));
2497 if (n <= 0)
2498 break;
2499 if (n > 1) {
2500 spec->mux_nids[i] = nid;
2501 break;
2502 }
2503 nid = conn[0];
2504 }
2505 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002506 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002507}
2508
Joseph Chanc577b8a2006-11-29 15:29:40 +01002509static int patch_vt1708(struct hda_codec *codec)
2510{
2511 struct via_spec *spec;
2512 int err;
2513
2514 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002515 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002516 if (spec == NULL)
2517 return -ENOMEM;
2518
Takashi Iwai620e2b22011-06-17 17:19:19 +02002519 spec->aa_mix_nid = 0x17;
2520
Takashi Iwai12daef62011-06-18 17:45:49 +02002521 /* Add HP and CD pin config connect bit re-config action */
2522 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2523 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2524
Joseph Chanc577b8a2006-11-29 15:29:40 +01002525 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002526 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002527 if (err < 0) {
2528 via_free(codec);
2529 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002530 }
2531
Takashi Iwai12daef62011-06-18 17:45:49 +02002532 /* add jack detect on/off control */
2533 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2534 return -ENOMEM;
2535
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002536 /* disable 32bit format on VT1708 */
2537 if (codec->vendor_id == 0x11061708)
2538 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002539
Lydia Wange322a362011-06-29 13:52:02 +08002540 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2541
Joseph Chanc577b8a2006-11-29 15:29:40 +01002542 codec->patch_ops = via_patch_ops;
2543
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002544 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002545 return 0;
2546}
2547
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002548static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002549{
2550 struct via_spec *spec;
2551 int err;
2552
2553 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002554 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002555 if (spec == NULL)
2556 return -ENOMEM;
2557
Takashi Iwai620e2b22011-06-17 17:19:19 +02002558 spec->aa_mix_nid = 0x18;
2559
Takashi Iwai12daef62011-06-18 17:45:49 +02002560 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002561 if (err < 0) {
2562 via_free(codec);
2563 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002564 }
2565
Joseph Chanc577b8a2006-11-29 15:29:40 +01002566 codec->patch_ops = via_patch_ops;
2567
Josepch Chanf7278fd2007-12-13 16:40:40 +01002568 return 0;
2569}
2570
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002571static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2572{
2573 struct via_spec *spec = codec->spec;
2574 int imux_is_smixer;
2575 unsigned int parm;
2576 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002577 if ((spec->codec_type != VT1708B_4CH) &&
2578 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002579 is_8ch = 1;
2580
2581 /* SW0 (17h) = stereo mixer */
2582 imux_is_smixer =
2583 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2584 == ((spec->codec_type == VT1708S) ? 5 : 0));
2585 /* inputs */
2586 /* PW 1/2/5 (1ah/1bh/1eh) */
2587 parm = AC_PWRST_D3;
2588 set_pin_power_state(codec, 0x1a, &parm);
2589 set_pin_power_state(codec, 0x1b, &parm);
2590 set_pin_power_state(codec, 0x1e, &parm);
2591 if (imux_is_smixer)
2592 parm = AC_PWRST_D0;
2593 /* SW0 (17h), AIW 0/1 (13h/14h) */
2594 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2595 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2596 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2597
2598 /* outputs */
2599 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2600 parm = AC_PWRST_D3;
2601 set_pin_power_state(codec, 0x19, &parm);
2602 if (spec->smart51_enabled)
2603 set_pin_power_state(codec, 0x1b, &parm);
2604 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2605 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2606
2607 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2608 if (is_8ch) {
2609 parm = AC_PWRST_D3;
2610 set_pin_power_state(codec, 0x22, &parm);
2611 if (spec->smart51_enabled)
2612 set_pin_power_state(codec, 0x1a, &parm);
2613 snd_hda_codec_write(codec, 0x26, 0,
2614 AC_VERB_SET_POWER_STATE, parm);
2615 snd_hda_codec_write(codec, 0x24, 0,
2616 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002617 } else if (codec->vendor_id == 0x11064397) {
2618 /* PW7(23h), SW2(27h), AOW2(25h) */
2619 parm = AC_PWRST_D3;
2620 set_pin_power_state(codec, 0x23, &parm);
2621 if (spec->smart51_enabled)
2622 set_pin_power_state(codec, 0x1a, &parm);
2623 snd_hda_codec_write(codec, 0x27, 0,
2624 AC_VERB_SET_POWER_STATE, parm);
2625 snd_hda_codec_write(codec, 0x25, 0,
2626 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002627 }
2628
2629 /* PW 3/4/7 (1ch/1dh/23h) */
2630 parm = AC_PWRST_D3;
2631 /* force to D0 for internal Speaker */
2632 set_pin_power_state(codec, 0x1c, &parm);
2633 set_pin_power_state(codec, 0x1d, &parm);
2634 if (is_8ch)
2635 set_pin_power_state(codec, 0x23, &parm);
2636
2637 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2638 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2639 imux_is_smixer ? AC_PWRST_D0 : parm);
2640 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2641 if (is_8ch) {
2642 snd_hda_codec_write(codec, 0x25, 0,
2643 AC_VERB_SET_POWER_STATE, parm);
2644 snd_hda_codec_write(codec, 0x27, 0,
2645 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002646 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2647 snd_hda_codec_write(codec, 0x25, 0,
2648 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002649}
2650
Lydia Wang518bf3b2009-10-10 19:07:29 +08002651static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002652static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002653{
2654 struct via_spec *spec;
2655 int err;
2656
Lydia Wang518bf3b2009-10-10 19:07:29 +08002657 if (get_codec_type(codec) == VT1708BCE)
2658 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002659
Josepch Chanf7278fd2007-12-13 16:40:40 +01002660 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002661 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002662 if (spec == NULL)
2663 return -ENOMEM;
2664
Takashi Iwai620e2b22011-06-17 17:19:19 +02002665 spec->aa_mix_nid = 0x16;
2666
Josepch Chanf7278fd2007-12-13 16:40:40 +01002667 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002668 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002669 if (err < 0) {
2670 via_free(codec);
2671 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002672 }
2673
Josepch Chanf7278fd2007-12-13 16:40:40 +01002674 codec->patch_ops = via_patch_ops;
2675
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002676 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2677
Josepch Chanf7278fd2007-12-13 16:40:40 +01002678 return 0;
2679}
2680
Harald Welted949cac2008-09-09 15:56:01 +08002681/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002682static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002683 /* Enable Mic Boost Volume backdoor */
2684 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002685 /* don't bybass mixer */
2686 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002687 { }
2688};
2689
Takashi Iwai9da29272009-05-07 16:31:14 +02002690/* fill out digital output widgets; one for master and one for slave outputs */
2691static void fill_dig_outs(struct hda_codec *codec)
2692{
2693 struct via_spec *spec = codec->spec;
2694 int i;
2695
2696 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2697 hda_nid_t nid;
2698 int conn;
2699
2700 nid = spec->autocfg.dig_out_pins[i];
2701 if (!nid)
2702 continue;
2703 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2704 if (conn < 1)
2705 continue;
2706 if (!spec->multiout.dig_out_nid)
2707 spec->multiout.dig_out_nid = nid;
2708 else {
2709 spec->slave_dig_outs[0] = nid;
2710 break; /* at most two dig outs */
2711 }
2712 }
2713}
2714
Takashi Iwai12daef62011-06-18 17:45:49 +02002715static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002716{
2717 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002718 hda_nid_t dig_nid;
2719 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002720
Takashi Iwai12daef62011-06-18 17:45:49 +02002721 if (!spec->autocfg.dig_in_pin)
2722 return;
Harald Welted949cac2008-09-09 15:56:01 +08002723
Takashi Iwai12daef62011-06-18 17:45:49 +02002724 dig_nid = codec->start_nid;
2725 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2726 unsigned int wcaps = get_wcaps(codec, dig_nid);
2727 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2728 continue;
2729 if (!(wcaps & AC_WCAP_DIGITAL))
2730 continue;
2731 if (!(wcaps & AC_WCAP_CONN_LIST))
2732 continue;
2733 err = get_connection_index(codec, dig_nid,
2734 spec->autocfg.dig_in_pin);
2735 if (err >= 0) {
2736 spec->dig_in_nid = dig_nid;
2737 break;
2738 }
2739 }
Harald Welted949cac2008-09-09 15:56:01 +08002740}
2741
Lydia Wang6369bcf2009-10-10 19:08:31 +08002742static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2743 int offset, int num_steps, int step_size)
2744{
2745 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2746 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2747 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2748 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2749 (0 << AC_AMPCAP_MUTE_SHIFT));
2750}
2751
Harald Welted949cac2008-09-09 15:56:01 +08002752static int patch_vt1708S(struct hda_codec *codec)
2753{
2754 struct via_spec *spec;
2755 int err;
2756
2757 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002758 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002759 if (spec == NULL)
2760 return -ENOMEM;
2761
Takashi Iwai620e2b22011-06-17 17:19:19 +02002762 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002763 override_mic_boost(codec, 0x1a, 0, 3, 40);
2764 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002765
Harald Welted949cac2008-09-09 15:56:01 +08002766 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002767 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002768 if (err < 0) {
2769 via_free(codec);
2770 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002771 }
2772
Takashi Iwai096a8852011-06-20 12:09:02 +02002773 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002774
Harald Welted949cac2008-09-09 15:56:01 +08002775 codec->patch_ops = via_patch_ops;
2776
Lydia Wang518bf3b2009-10-10 19:07:29 +08002777 /* correct names for VT1708BCE */
2778 if (get_codec_type(codec) == VT1708BCE) {
2779 kfree(codec->chip_name);
2780 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2781 snprintf(codec->bus->card->mixername,
2782 sizeof(codec->bus->card->mixername),
2783 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002784 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002785 /* correct names for VT1705 */
2786 if (codec->vendor_id == 0x11064397) {
2787 kfree(codec->chip_name);
2788 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2789 snprintf(codec->bus->card->mixername,
2790 sizeof(codec->bus->card->mixername),
2791 "%s %s", codec->vendor_name, codec->chip_name);
2792 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002793 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002794 return 0;
2795}
2796
2797/* Patch for VT1702 */
2798
Takashi Iwai096a8852011-06-20 12:09:02 +02002799static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002800 /* mixer enable */
2801 {0x1, 0xF88, 0x3},
2802 /* GPIO 0~2 */
2803 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002804 { }
2805};
2806
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002807static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2808{
2809 int imux_is_smixer =
2810 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2811 unsigned int parm;
2812 /* inputs */
2813 /* PW 1/2/5 (14h/15h/18h) */
2814 parm = AC_PWRST_D3;
2815 set_pin_power_state(codec, 0x14, &parm);
2816 set_pin_power_state(codec, 0x15, &parm);
2817 set_pin_power_state(codec, 0x18, &parm);
2818 if (imux_is_smixer)
2819 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2820 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2821 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2822 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2823 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2824 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2825
2826 /* outputs */
2827 /* PW 3/4 (16h/17h) */
2828 parm = AC_PWRST_D3;
2829 set_pin_power_state(codec, 0x17, &parm);
2830 set_pin_power_state(codec, 0x16, &parm);
2831 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2832 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2833 imux_is_smixer ? AC_PWRST_D0 : parm);
2834 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2835 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2836}
2837
Harald Welted949cac2008-09-09 15:56:01 +08002838static int patch_vt1702(struct hda_codec *codec)
2839{
2840 struct via_spec *spec;
2841 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002842
2843 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002844 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002845 if (spec == NULL)
2846 return -ENOMEM;
2847
Takashi Iwai620e2b22011-06-17 17:19:19 +02002848 spec->aa_mix_nid = 0x1a;
2849
Takashi Iwai12daef62011-06-18 17:45:49 +02002850 /* limit AA path volume to 0 dB */
2851 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2852 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2853 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2854 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2855 (1 << AC_AMPCAP_MUTE_SHIFT));
2856
Harald Welted949cac2008-09-09 15:56:01 +08002857 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002858 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002859 if (err < 0) {
2860 via_free(codec);
2861 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002862 }
2863
Takashi Iwai096a8852011-06-20 12:09:02 +02002864 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002865
Harald Welted949cac2008-09-09 15:56:01 +08002866 codec->patch_ops = via_patch_ops;
2867
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002868 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002869 return 0;
2870}
2871
Lydia Wangeb7188c2009-10-10 19:08:34 +08002872/* Patch for VT1718S */
2873
Takashi Iwai096a8852011-06-20 12:09:02 +02002874static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002875 /* Enable MW0 adjust Gain 5 */
2876 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002877 /* Enable Boost Volume backdoor */
2878 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002879
Lydia Wangeb7188c2009-10-10 19:08:34 +08002880 { }
2881};
2882
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002883static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2884{
2885 struct via_spec *spec = codec->spec;
2886 int imux_is_smixer;
2887 unsigned int parm;
2888 /* MUX6 (1eh) = stereo mixer */
2889 imux_is_smixer =
2890 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2891 /* inputs */
2892 /* PW 5/6/7 (29h/2ah/2bh) */
2893 parm = AC_PWRST_D3;
2894 set_pin_power_state(codec, 0x29, &parm);
2895 set_pin_power_state(codec, 0x2a, &parm);
2896 set_pin_power_state(codec, 0x2b, &parm);
2897 if (imux_is_smixer)
2898 parm = AC_PWRST_D0;
2899 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2900 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2901 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2902 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2903 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2904
2905 /* outputs */
2906 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2907 parm = AC_PWRST_D3;
2908 set_pin_power_state(codec, 0x27, &parm);
2909 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2910 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2911
2912 /* PW2 (26h), AOW2 (ah) */
2913 parm = AC_PWRST_D3;
2914 set_pin_power_state(codec, 0x26, &parm);
2915 if (spec->smart51_enabled)
2916 set_pin_power_state(codec, 0x2b, &parm);
2917 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2918
2919 /* PW0 (24h), AOW0 (8h) */
2920 parm = AC_PWRST_D3;
2921 set_pin_power_state(codec, 0x24, &parm);
2922 if (!spec->hp_independent_mode) /* check for redirected HP */
2923 set_pin_power_state(codec, 0x28, &parm);
2924 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2925 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2926 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2927 imux_is_smixer ? AC_PWRST_D0 : parm);
2928
2929 /* PW1 (25h), AOW1 (9h) */
2930 parm = AC_PWRST_D3;
2931 set_pin_power_state(codec, 0x25, &parm);
2932 if (spec->smart51_enabled)
2933 set_pin_power_state(codec, 0x2a, &parm);
2934 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2935
2936 if (spec->hp_independent_mode) {
2937 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2938 parm = AC_PWRST_D3;
2939 set_pin_power_state(codec, 0x28, &parm);
2940 snd_hda_codec_write(codec, 0x1b, 0,
2941 AC_VERB_SET_POWER_STATE, parm);
2942 snd_hda_codec_write(codec, 0x34, 0,
2943 AC_VERB_SET_POWER_STATE, parm);
2944 snd_hda_codec_write(codec, 0xc, 0,
2945 AC_VERB_SET_POWER_STATE, parm);
2946 }
2947}
2948
Lydia Wangeb7188c2009-10-10 19:08:34 +08002949static int patch_vt1718S(struct hda_codec *codec)
2950{
2951 struct via_spec *spec;
2952 int err;
2953
2954 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002955 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002956 if (spec == NULL)
2957 return -ENOMEM;
2958
Takashi Iwai620e2b22011-06-17 17:19:19 +02002959 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002960 override_mic_boost(codec, 0x2b, 0, 3, 40);
2961 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002962
Lydia Wangeb7188c2009-10-10 19:08:34 +08002963 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002964 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002965 if (err < 0) {
2966 via_free(codec);
2967 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002968 }
2969
Takashi Iwai096a8852011-06-20 12:09:02 +02002970 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002971
Lydia Wangeb7188c2009-10-10 19:08:34 +08002972 codec->patch_ops = via_patch_ops;
2973
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002974 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2975
Lydia Wangeb7188c2009-10-10 19:08:34 +08002976 return 0;
2977}
Lydia Wangf3db4232009-10-10 19:08:41 +08002978
2979/* Patch for VT1716S */
2980
2981static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2982 struct snd_ctl_elem_info *uinfo)
2983{
2984 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2985 uinfo->count = 1;
2986 uinfo->value.integer.min = 0;
2987 uinfo->value.integer.max = 1;
2988 return 0;
2989}
2990
2991static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2992 struct snd_ctl_elem_value *ucontrol)
2993{
2994 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2995 int index = 0;
2996
2997 index = snd_hda_codec_read(codec, 0x26, 0,
2998 AC_VERB_GET_CONNECT_SEL, 0);
2999 if (index != -1)
3000 *ucontrol->value.integer.value = index;
3001
3002 return 0;
3003}
3004
3005static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3006 struct snd_ctl_elem_value *ucontrol)
3007{
3008 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3009 struct via_spec *spec = codec->spec;
3010 int index = *ucontrol->value.integer.value;
3011
3012 snd_hda_codec_write(codec, 0x26, 0,
3013 AC_VERB_SET_CONNECT_SEL, index);
3014 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003015 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003016 return 1;
3017}
3018
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003019static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003020 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3021 {
3022 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3023 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003024 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003025 .count = 1,
3026 .info = vt1716s_dmic_info,
3027 .get = vt1716s_dmic_get,
3028 .put = vt1716s_dmic_put,
3029 },
3030 {} /* end */
3031};
3032
3033
3034/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003035static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003036 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3037 { } /* end */
3038};
3039
Takashi Iwai096a8852011-06-20 12:09:02 +02003040static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003041 /* Enable Boost Volume backdoor */
3042 {0x1, 0xf8a, 0x80},
3043 /* don't bybass mixer */
3044 {0x1, 0xf88, 0xc0},
3045 /* Enable mono output */
3046 {0x1, 0xf90, 0x08},
3047 { }
3048};
3049
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003050static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3051{
3052 struct via_spec *spec = codec->spec;
3053 int imux_is_smixer;
3054 unsigned int parm;
3055 unsigned int mono_out, present;
3056 /* SW0 (17h) = stereo mixer */
3057 imux_is_smixer =
3058 (snd_hda_codec_read(codec, 0x17, 0,
3059 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3060 /* inputs */
3061 /* PW 1/2/5 (1ah/1bh/1eh) */
3062 parm = AC_PWRST_D3;
3063 set_pin_power_state(codec, 0x1a, &parm);
3064 set_pin_power_state(codec, 0x1b, &parm);
3065 set_pin_power_state(codec, 0x1e, &parm);
3066 if (imux_is_smixer)
3067 parm = AC_PWRST_D0;
3068 /* SW0 (17h), AIW0(13h) */
3069 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3070 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3071
3072 parm = AC_PWRST_D3;
3073 set_pin_power_state(codec, 0x1e, &parm);
3074 /* PW11 (22h) */
3075 if (spec->dmic_enabled)
3076 set_pin_power_state(codec, 0x22, &parm);
3077 else
3078 snd_hda_codec_write(codec, 0x22, 0,
3079 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3080
3081 /* SW2(26h), AIW1(14h) */
3082 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3083 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3084
3085 /* outputs */
3086 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3087 parm = AC_PWRST_D3;
3088 set_pin_power_state(codec, 0x19, &parm);
3089 /* Smart 5.1 PW2(1bh) */
3090 if (spec->smart51_enabled)
3091 set_pin_power_state(codec, 0x1b, &parm);
3092 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3093 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3094
3095 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3096 parm = AC_PWRST_D3;
3097 set_pin_power_state(codec, 0x23, &parm);
3098 /* Smart 5.1 PW1(1ah) */
3099 if (spec->smart51_enabled)
3100 set_pin_power_state(codec, 0x1a, &parm);
3101 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3102
3103 /* Smart 5.1 PW5(1eh) */
3104 if (spec->smart51_enabled)
3105 set_pin_power_state(codec, 0x1e, &parm);
3106 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3107
3108 /* Mono out */
3109 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3110 present = snd_hda_jack_detect(codec, 0x1c);
3111
3112 if (present)
3113 mono_out = 0;
3114 else {
3115 present = snd_hda_jack_detect(codec, 0x1d);
3116 if (!spec->hp_independent_mode && present)
3117 mono_out = 0;
3118 else
3119 mono_out = 1;
3120 }
3121 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3122 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3123 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3124 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3125
3126 /* PW 3/4 (1ch/1dh) */
3127 parm = AC_PWRST_D3;
3128 set_pin_power_state(codec, 0x1c, &parm);
3129 set_pin_power_state(codec, 0x1d, &parm);
3130 /* HP Independent Mode, power on AOW3 */
3131 if (spec->hp_independent_mode)
3132 snd_hda_codec_write(codec, 0x25, 0,
3133 AC_VERB_SET_POWER_STATE, parm);
3134
3135 /* force to D0 for internal Speaker */
3136 /* MW0 (16h), AOW0 (10h) */
3137 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3138 imux_is_smixer ? AC_PWRST_D0 : parm);
3139 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3140 mono_out ? AC_PWRST_D0 : parm);
3141}
3142
Lydia Wangf3db4232009-10-10 19:08:41 +08003143static int patch_vt1716S(struct hda_codec *codec)
3144{
3145 struct via_spec *spec;
3146 int err;
3147
3148 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003149 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003150 if (spec == NULL)
3151 return -ENOMEM;
3152
Takashi Iwai620e2b22011-06-17 17:19:19 +02003153 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003154 override_mic_boost(codec, 0x1a, 0, 3, 40);
3155 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003156
Lydia Wangf3db4232009-10-10 19:08:41 +08003157 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003158 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003159 if (err < 0) {
3160 via_free(codec);
3161 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003162 }
3163
Takashi Iwai096a8852011-06-20 12:09:02 +02003164 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003165
Lydia Wangf3db4232009-10-10 19:08:41 +08003166 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3167 spec->num_mixers++;
3168
3169 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3170
3171 codec->patch_ops = via_patch_ops;
3172
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003173 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003174 return 0;
3175}
Lydia Wang25eaba22009-10-10 19:08:43 +08003176
3177/* for vt2002P */
3178
Takashi Iwai096a8852011-06-20 12:09:02 +02003179static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003180 /* Class-D speaker related verbs */
3181 {0x1, 0xfe0, 0x4},
3182 {0x1, 0xfe9, 0x80},
3183 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003184 /* Enable Boost Volume backdoor */
3185 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003186 /* Enable AOW0 to MW9 */
3187 {0x1, 0xfb8, 0x88},
3188 { }
3189};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003190
Takashi Iwai096a8852011-06-20 12:09:02 +02003191static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003192 /* Enable Boost Volume backdoor */
3193 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003194 /* Enable AOW0 to MW9 */
3195 {0x1, 0xfb8, 0x88},
3196 { }
3197};
Lydia Wang25eaba22009-10-10 19:08:43 +08003198
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003199static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3200{
3201 struct via_spec *spec = codec->spec;
3202 int imux_is_smixer;
3203 unsigned int parm;
3204 unsigned int present;
3205 /* MUX9 (1eh) = stereo mixer */
3206 imux_is_smixer =
3207 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3208 /* inputs */
3209 /* PW 5/6/7 (29h/2ah/2bh) */
3210 parm = AC_PWRST_D3;
3211 set_pin_power_state(codec, 0x29, &parm);
3212 set_pin_power_state(codec, 0x2a, &parm);
3213 set_pin_power_state(codec, 0x2b, &parm);
3214 parm = AC_PWRST_D0;
3215 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3216 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3217 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3218 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3219 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3220
3221 /* outputs */
3222 /* AOW0 (8h)*/
3223 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3224
Lydia Wang118909562011-03-23 17:57:34 +08003225 if (spec->codec_type == VT1802) {
3226 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3227 parm = AC_PWRST_D3;
3228 set_pin_power_state(codec, 0x28, &parm);
3229 snd_hda_codec_write(codec, 0x18, 0,
3230 AC_VERB_SET_POWER_STATE, parm);
3231 snd_hda_codec_write(codec, 0x38, 0,
3232 AC_VERB_SET_POWER_STATE, parm);
3233 } else {
3234 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3235 parm = AC_PWRST_D3;
3236 set_pin_power_state(codec, 0x26, &parm);
3237 snd_hda_codec_write(codec, 0x1c, 0,
3238 AC_VERB_SET_POWER_STATE, parm);
3239 snd_hda_codec_write(codec, 0x37, 0,
3240 AC_VERB_SET_POWER_STATE, parm);
3241 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003242
Lydia Wang118909562011-03-23 17:57:34 +08003243 if (spec->codec_type == VT1802) {
3244 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3245 parm = AC_PWRST_D3;
3246 set_pin_power_state(codec, 0x25, &parm);
3247 snd_hda_codec_write(codec, 0x15, 0,
3248 AC_VERB_SET_POWER_STATE, parm);
3249 snd_hda_codec_write(codec, 0x35, 0,
3250 AC_VERB_SET_POWER_STATE, parm);
3251 } else {
3252 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3253 parm = AC_PWRST_D3;
3254 set_pin_power_state(codec, 0x25, &parm);
3255 snd_hda_codec_write(codec, 0x19, 0,
3256 AC_VERB_SET_POWER_STATE, parm);
3257 snd_hda_codec_write(codec, 0x35, 0,
3258 AC_VERB_SET_POWER_STATE, parm);
3259 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003260
3261 if (spec->hp_independent_mode)
3262 snd_hda_codec_write(codec, 0x9, 0,
3263 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3264
3265 /* Class-D */
3266 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3267 present = snd_hda_jack_detect(codec, 0x25);
3268
3269 parm = AC_PWRST_D3;
3270 set_pin_power_state(codec, 0x24, &parm);
3271 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003272 if (spec->codec_type == VT1802)
3273 snd_hda_codec_write(codec, 0x14, 0,
3274 AC_VERB_SET_POWER_STATE, parm);
3275 else
3276 snd_hda_codec_write(codec, 0x18, 0,
3277 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003278 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3279
3280 /* Mono Out */
3281 present = snd_hda_jack_detect(codec, 0x26);
3282
3283 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003284 if (spec->codec_type == VT1802) {
3285 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3286 snd_hda_codec_write(codec, 0x33, 0,
3287 AC_VERB_SET_POWER_STATE, parm);
3288 snd_hda_codec_write(codec, 0x1c, 0,
3289 AC_VERB_SET_POWER_STATE, parm);
3290 snd_hda_codec_write(codec, 0x3c, 0,
3291 AC_VERB_SET_POWER_STATE, parm);
3292 } else {
3293 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3294 snd_hda_codec_write(codec, 0x31, 0,
3295 AC_VERB_SET_POWER_STATE, parm);
3296 snd_hda_codec_write(codec, 0x17, 0,
3297 AC_VERB_SET_POWER_STATE, parm);
3298 snd_hda_codec_write(codec, 0x3b, 0,
3299 AC_VERB_SET_POWER_STATE, parm);
3300 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003301 /* MW9 (21h) */
3302 if (imux_is_smixer || !is_aa_path_mute(codec))
3303 snd_hda_codec_write(codec, 0x21, 0,
3304 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3305 else
3306 snd_hda_codec_write(codec, 0x21, 0,
3307 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3308}
Lydia Wang25eaba22009-10-10 19:08:43 +08003309
3310/* patch for vt2002P */
3311static int patch_vt2002P(struct hda_codec *codec)
3312{
3313 struct via_spec *spec;
3314 int err;
3315
3316 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003317 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003318 if (spec == NULL)
3319 return -ENOMEM;
3320
Takashi Iwai620e2b22011-06-17 17:19:19 +02003321 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003322 override_mic_boost(codec, 0x2b, 0, 3, 40);
3323 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003324
Lydia Wang25eaba22009-10-10 19:08:43 +08003325 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003326 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003327 if (err < 0) {
3328 via_free(codec);
3329 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003330 }
3331
Lydia Wang118909562011-03-23 17:57:34 +08003332 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003333 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003334 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003335 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003336
Lydia Wang25eaba22009-10-10 19:08:43 +08003337 codec->patch_ops = via_patch_ops;
3338
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003339 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003340 return 0;
3341}
Lydia Wangab6734e2009-10-10 19:08:46 +08003342
3343/* for vt1812 */
3344
Takashi Iwai096a8852011-06-20 12:09:02 +02003345static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003346 /* Enable Boost Volume backdoor */
3347 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003348 /* Enable AOW0 to MW9 */
3349 {0x1, 0xfb8, 0xa8},
3350 { }
3351};
3352
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003353static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3354{
3355 struct via_spec *spec = codec->spec;
3356 int imux_is_smixer =
3357 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3358 unsigned int parm;
3359 unsigned int present;
3360 /* MUX10 (1eh) = stereo mixer */
3361 imux_is_smixer =
3362 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3363 /* inputs */
3364 /* PW 5/6/7 (29h/2ah/2bh) */
3365 parm = AC_PWRST_D3;
3366 set_pin_power_state(codec, 0x29, &parm);
3367 set_pin_power_state(codec, 0x2a, &parm);
3368 set_pin_power_state(codec, 0x2b, &parm);
3369 parm = AC_PWRST_D0;
3370 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3371 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3372 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3373 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3374 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3375
3376 /* outputs */
3377 /* AOW0 (8h)*/
3378 snd_hda_codec_write(codec, 0x8, 0,
3379 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3380
3381 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3382 parm = AC_PWRST_D3;
3383 set_pin_power_state(codec, 0x28, &parm);
3384 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3385 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3386
3387 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3388 parm = AC_PWRST_D3;
3389 set_pin_power_state(codec, 0x25, &parm);
3390 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3391 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3392 if (spec->hp_independent_mode)
3393 snd_hda_codec_write(codec, 0x9, 0,
3394 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3395
3396 /* Internal Speaker */
3397 /* PW0 (24h), MW0(14h), MUX0(34h) */
3398 present = snd_hda_jack_detect(codec, 0x25);
3399
3400 parm = AC_PWRST_D3;
3401 set_pin_power_state(codec, 0x24, &parm);
3402 if (present) {
3403 snd_hda_codec_write(codec, 0x14, 0,
3404 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3405 snd_hda_codec_write(codec, 0x34, 0,
3406 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3407 } else {
3408 snd_hda_codec_write(codec, 0x14, 0,
3409 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3410 snd_hda_codec_write(codec, 0x34, 0,
3411 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3412 }
3413
3414
3415 /* Mono Out */
3416 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3417 present = snd_hda_jack_detect(codec, 0x28);
3418
3419 parm = AC_PWRST_D3;
3420 set_pin_power_state(codec, 0x31, &parm);
3421 if (present) {
3422 snd_hda_codec_write(codec, 0x1c, 0,
3423 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3424 snd_hda_codec_write(codec, 0x3c, 0,
3425 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3426 snd_hda_codec_write(codec, 0x3e, 0,
3427 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3428 } else {
3429 snd_hda_codec_write(codec, 0x1c, 0,
3430 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3431 snd_hda_codec_write(codec, 0x3c, 0,
3432 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3433 snd_hda_codec_write(codec, 0x3e, 0,
3434 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3435 }
3436
3437 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3438 parm = AC_PWRST_D3;
3439 set_pin_power_state(codec, 0x33, &parm);
3440 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3441 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3442
3443}
Lydia Wangab6734e2009-10-10 19:08:46 +08003444
3445/* patch for vt1812 */
3446static int patch_vt1812(struct hda_codec *codec)
3447{
3448 struct via_spec *spec;
3449 int err;
3450
3451 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003452 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003453 if (spec == NULL)
3454 return -ENOMEM;
3455
Takashi Iwai620e2b22011-06-17 17:19:19 +02003456 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003457 override_mic_boost(codec, 0x2b, 0, 3, 40);
3458 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003459
Lydia Wangab6734e2009-10-10 19:08:46 +08003460 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003461 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003462 if (err < 0) {
3463 via_free(codec);
3464 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003465 }
3466
Takashi Iwai096a8852011-06-20 12:09:02 +02003467 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003468
Lydia Wangab6734e2009-10-10 19:08:46 +08003469 codec->patch_ops = via_patch_ops;
3470
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003471 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003472 return 0;
3473}
3474
Joseph Chanc577b8a2006-11-29 15:29:40 +01003475/*
3476 * patch entries
3477 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003478static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003479 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3480 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3481 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3482 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3483 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003484 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003485 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003486 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003487 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003488 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003489 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003490 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003491 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003492 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003493 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003494 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003495 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003496 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003497 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003498 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003499 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003500 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003501 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003502 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003503 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003504 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003505 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003506 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003507 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003508 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003509 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003510 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003511 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003512 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003513 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003514 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003515 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003516 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003517 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003518 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003519 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003520 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003521 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003522 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003523 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003524 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003525 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003526 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003527 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003528 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003529 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003530 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003531 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003532 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003533 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003534 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003535 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003536 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003537 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003538 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003539 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003540 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003541 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003542 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003543 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003544 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003545 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003546 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003547 { .id = 0x11060428, .name = "VT1718S",
3548 .patch = patch_vt1718S},
3549 { .id = 0x11064428, .name = "VT1718S",
3550 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003551 { .id = 0x11060441, .name = "VT2020",
3552 .patch = patch_vt1718S},
3553 { .id = 0x11064441, .name = "VT1828S",
3554 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003555 { .id = 0x11060433, .name = "VT1716S",
3556 .patch = patch_vt1716S},
3557 { .id = 0x1106a721, .name = "VT1716S",
3558 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003559 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3560 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003561 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003562 { .id = 0x11060440, .name = "VT1818S",
3563 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003564 { .id = 0x11060446, .name = "VT1802",
3565 .patch = patch_vt2002P},
3566 { .id = 0x11068446, .name = "VT1802",
3567 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003568 {} /* terminator */
3569};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003570
3571MODULE_ALIAS("snd-hda-codec-id:1106*");
3572
3573static struct hda_codec_preset_list via_list = {
3574 .preset = snd_hda_preset_via,
3575 .owner = THIS_MODULE,
3576};
3577
3578MODULE_LICENSE("GPL");
3579MODULE_DESCRIPTION("VIA HD-audio codec");
3580
3581static int __init patch_via_init(void)
3582{
3583 return snd_hda_add_codec_preset(&via_list);
3584}
3585
3586static void __exit patch_via_exit(void)
3587{
3588 snd_hda_delete_codec_preset(&via_list);
3589}
3590
3591module_init(patch_via_init)
3592module_exit(patch_via_exit)