blob: 5ef14dd7a568cc476ca0dded68ca0f78280f6010 [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);
454 if (have_mute(codec, dst, HDA_INPUT)) {
455 int val = enable ? AMP_IN_UNMUTE(idx) :
456 AMP_IN_MUTE(idx);
457 snd_hda_codec_write(codec, dst, 0,
458 AC_VERB_SET_AMP_GAIN_MUTE, val);
459 }
460 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
461 continue;
462 if (have_mute(codec, src, HDA_OUTPUT)) {
463 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
464 snd_hda_codec_write(codec, src, 0,
465 AC_VERB_SET_AMP_GAIN_MUTE, val);
466 }
467 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200468}
469
470/* set the given pin as output */
471static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
472 int pin_type)
473{
474 if (!pin)
475 return;
476 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
477 pin_type);
478 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
479 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200480 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100481}
482
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200483static void via_auto_init_output(struct hda_codec *codec,
484 struct nid_path *path, int pin_type,
485 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200486{
487 struct via_spec *spec = codec->spec;
488 unsigned int caps;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200489 hda_nid_t pin, nid;
490 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200491
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200492 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200494 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200495
496 init_output_pin(codec, pin, pin_type);
497 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
498 if (caps & AC_AMPCAP_MUTE) {
499 unsigned int val;
500 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
501 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
502 AMP_OUT_MUTE | val);
503 }
504
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200505 activate_output_path(codec, path, true, force);
506
507 /* initialize the AA-path */
508 if (!spec->aa_mix_nid)
509 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200510 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200511 nid = path->path[i];
512 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
513 if (idx >= 0) {
514 if (have_mute(codec, nid, HDA_INPUT))
515 snd_hda_codec_write(codec, nid, 0,
516 AC_VERB_SET_AMP_GAIN_MUTE,
517 AMP_IN_UNMUTE(idx));
518 break;
519 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200520 }
521}
522
Joseph Chanc577b8a2006-11-29 15:29:40 +0100523static void via_auto_init_multi_out(struct hda_codec *codec)
524{
525 struct via_spec *spec = codec->spec;
526 int i;
527
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200528 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200529 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100530}
531
532static void via_auto_init_hp_out(struct hda_codec *codec)
533{
534 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100535
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200536 if (!spec->hp_dac_nid) {
537 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
538 return;
539 }
540 if (spec->hp_independent_mode) {
541 activate_output_path(codec, &spec->hp_dep_path, false, false);
542 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
543 } else {
544 activate_output_path(codec, &spec->hp_path, false, false);
545 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
546 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100547}
548
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200549static void via_auto_init_speaker_out(struct hda_codec *codec)
550{
551 struct via_spec *spec = codec->spec;
552
553 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200554 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200555}
556
Takashi Iwaif4a78282011-06-17 18:46:48 +0200557static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200558
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559static void via_auto_init_analog_input(struct hda_codec *codec)
560{
561 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200562 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200563 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200564 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200565 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100566
Takashi Iwai096a8852011-06-20 12:09:02 +0200567 /* init ADCs */
568 for (i = 0; i < spec->num_adc_nids; i++) {
569 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
570 AC_VERB_SET_AMP_GAIN_MUTE,
571 AMP_IN_UNMUTE(0));
572 }
573
574 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200575 for (i = 0; i < cfg->num_inputs; i++) {
576 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200577 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200578 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100579 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200580 ctl = PIN_VREF50;
581 else
582 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100583 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200584 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200586
587 /* init input-src */
588 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200589 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
590 if (spec->mux_nids[adc_idx]) {
591 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
592 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
593 AC_VERB_SET_CONNECT_SEL,
594 mux_idx);
595 }
596 if (spec->dyn_adc_switch)
597 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200598 }
599
600 /* init aa-mixer */
601 if (!spec->aa_mix_nid)
602 return;
603 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
604 ARRAY_SIZE(conn));
605 for (i = 0; i < num_conns; i++) {
606 unsigned int caps = get_wcaps(codec, conn[i]);
607 if (get_wcaps_type(caps) == AC_WID_PIN)
608 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
609 AC_VERB_SET_AMP_GAIN_MUTE,
610 AMP_IN_MUTE(i));
611 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100612}
Lydia Wangf5271102009-10-10 19:07:35 +0800613
614static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
615 unsigned int *affected_parm)
616{
617 unsigned parm;
618 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
619 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
620 >> AC_DEFCFG_MISC_SHIFT
621 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800622 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200623 unsigned present = 0;
624
625 no_presence |= spec->no_pin_power_ctl;
626 if (!no_presence)
627 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200628 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800629 || ((no_presence || present)
630 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800631 *affected_parm = AC_PWRST_D0; /* if it's connected */
632 parm = AC_PWRST_D0;
633 } else
634 parm = AC_PWRST_D3;
635
636 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
637}
638
Takashi Iwai24088a52011-06-17 16:59:21 +0200639static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
640 struct snd_ctl_elem_info *uinfo)
641{
642 static const char * const texts[] = {
643 "Disabled", "Enabled"
644 };
645
646 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
647 uinfo->count = 1;
648 uinfo->value.enumerated.items = 2;
649 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
650 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
651 strcpy(uinfo->value.enumerated.name,
652 texts[uinfo->value.enumerated.item]);
653 return 0;
654}
655
656static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
657 struct snd_ctl_elem_value *ucontrol)
658{
659 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
660 struct via_spec *spec = codec->spec;
661 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
662 return 0;
663}
664
665static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
666 struct snd_ctl_elem_value *ucontrol)
667{
668 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
669 struct via_spec *spec = codec->spec;
670 unsigned int val = !ucontrol->value.enumerated.item[0];
671
672 if (val == spec->no_pin_power_ctl)
673 return 0;
674 spec->no_pin_power_ctl = val;
675 set_widgets_power_state(codec);
676 return 1;
677}
678
679static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
680 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
681 .name = "Dynamic Power-Control",
682 .info = via_pin_power_ctl_info,
683 .get = via_pin_power_ctl_get,
684 .put = via_pin_power_ctl_put,
685};
686
687
Harald Welte0aa62ae2008-09-09 15:58:27 +0800688static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
689 struct snd_ctl_elem_info *uinfo)
690{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200691 static const char * const texts[] = { "OFF", "ON" };
692
693 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
694 uinfo->count = 1;
695 uinfo->value.enumerated.items = 2;
696 if (uinfo->value.enumerated.item >= 2)
697 uinfo->value.enumerated.item = 1;
698 strcpy(uinfo->value.enumerated.name,
699 texts[uinfo->value.enumerated.item]);
700 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800701}
702
703static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
704 struct snd_ctl_elem_value *ucontrol)
705{
706 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800707 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800708
Takashi Iwaiece8d042011-06-19 16:24:21 +0200709 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800710 return 0;
711}
712
Harald Welte0aa62ae2008-09-09 15:58:27 +0800713static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
714 struct snd_ctl_elem_value *ucontrol)
715{
716 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
717 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200718 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200719
Takashi Iwai25250502011-06-30 17:24:47 +0200720 /* no independent-hp status change during PCM playback is running */
721 if (spec->num_active_streams)
722 return -EBUSY;
723
724 cur = !!ucontrol->value.enumerated.item[0];
725 if (spec->hp_independent_mode == cur)
726 return 0;
727 spec->hp_independent_mode = cur;
728 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200729 activate_output_path(codec, &spec->hp_dep_path, false, false);
730 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200731 if (spec->hp_indep_shared)
732 activate_output_path(codec, &spec->out_path[HDA_SIDE],
733 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200734 } else {
735 activate_output_path(codec, &spec->hp_path, false, false);
736 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200737 if (spec->hp_indep_shared)
738 activate_output_path(codec, &spec->out_path[HDA_SIDE],
739 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200740 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800741
Lydia Wangce0e5a92011-03-22 16:22:37 +0800742 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800743 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200744 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800745}
746
Takashi Iwaiece8d042011-06-19 16:24:21 +0200747static const struct snd_kcontrol_new via_hp_mixer = {
748 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
749 .name = "Independent HP",
750 .info = via_independent_hp_info,
751 .get = via_independent_hp_get,
752 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800753};
754
Takashi Iwai3d83e572010-04-14 14:36:23 +0200755static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100756{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200757 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100758 struct snd_kcontrol_new *knew;
759 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100760
Takashi Iwaiece8d042011-06-19 16:24:21 +0200761 nid = spec->autocfg.hp_pins[0];
762 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200763 if (knew == NULL)
764 return -ENOMEM;
765
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100766 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100767
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100768 return 0;
769}
770
Lydia Wang1564b282009-10-10 19:07:52 +0800771static void notify_aa_path_ctls(struct hda_codec *codec)
772{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200773 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800774 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800775
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200776 for (i = 0; i < spec->smart51_nums; i++) {
777 struct snd_kcontrol *ctl;
778 struct snd_ctl_elem_id id;
779 memset(&id, 0, sizeof(id));
780 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
781 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800782 ctl = snd_hda_find_mixer_ctl(codec, id.name);
783 if (ctl)
784 snd_ctl_notify(codec->bus->card,
785 SNDRV_CTL_EVENT_MASK_VALUE,
786 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800787 }
788}
789
790static void mute_aa_path(struct hda_codec *codec, int mute)
791{
792 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200793 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800794 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200795
Lydia Wang1564b282009-10-10 19:07:52 +0800796 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200797 for (i = 0; i < spec->smart51_nums; i++) {
798 if (spec->smart51_idxs[i] < 0)
799 continue;
800 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
801 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800802 HDA_AMP_MUTE, val);
803 }
804}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200805
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200806static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
807{
808 struct via_spec *spec = codec->spec;
809 int i;
810
811 for (i = 0; i < spec->smart51_nums; i++)
812 if (spec->smart51_pins[i] == pin)
813 return true;
814 return false;
815}
816
Lydia Wang1564b282009-10-10 19:07:52 +0800817static int via_smart51_get(struct snd_kcontrol *kcontrol,
818 struct snd_ctl_elem_value *ucontrol)
819{
820 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
821 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800822
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200823 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800824 return 0;
825}
826
827static int via_smart51_put(struct snd_kcontrol *kcontrol,
828 struct snd_ctl_elem_value *ucontrol)
829{
830 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
831 struct via_spec *spec = codec->spec;
832 int out_in = *ucontrol->value.integer.value
833 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800834 int i;
835
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200836 for (i = 0; i < spec->smart51_nums; i++) {
837 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200838 unsigned int parm;
839
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200840 parm = snd_hda_codec_read(codec, nid, 0,
841 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
842 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
843 parm |= out_in;
844 snd_hda_codec_write(codec, nid, 0,
845 AC_VERB_SET_PIN_WIDGET_CONTROL,
846 parm);
847 if (out_in == AC_PINCTL_OUT_EN) {
848 mute_aa_path(codec, 1);
849 notify_aa_path_ctls(codec);
850 }
Lydia Wang1564b282009-10-10 19:07:52 +0800851 }
852 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800853 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800854 return 1;
855}
856
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200857static const struct snd_kcontrol_new via_smart51_mixer = {
858 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
859 .name = "Smart 5.1",
860 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200861 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200862 .get = via_smart51_get,
863 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800864};
865
Takashi Iwaif4a78282011-06-17 18:46:48 +0200866static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100867{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200868 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100869
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200870 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800871 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200872 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100873 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100874 return 0;
875}
876
Takashi Iwaiada509e2011-06-20 15:40:19 +0200877/* check AA path's mute status */
878static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800879{
Lydia Wangf5271102009-10-10 19:07:35 +0800880 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200881 const struct hda_amp_list *p;
882 int i, ch, v;
883
884 for (i = 0; i < spec->num_loopbacks; i++) {
885 p = &spec->loopback_list[i];
886 for (ch = 0; ch < 2; ch++) {
887 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
888 p->idx);
889 if (!(v & HDA_AMP_MUTE) && v > 0)
890 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800891 }
892 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200893 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800894}
895
896/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200897static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800898{
899 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200900 bool enable;
901 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800902
Takashi Iwaiada509e2011-06-20 15:40:19 +0200903 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800904
905 /* decide low current mode's verb & parameter */
906 switch (spec->codec_type) {
907 case VT1708B_8CH:
908 case VT1708B_4CH:
909 verb = 0xf70;
910 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
911 break;
912 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800913 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800914 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800915 verb = 0xf73;
916 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
917 break;
918 case VT1702:
919 verb = 0xf73;
920 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
921 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800922 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800923 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800924 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800925 verb = 0xf93;
926 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
927 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800928 default:
929 return; /* other codecs are not supported */
930 }
931 /* send verb */
932 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
933}
934
Joseph Chanc577b8a2006-11-29 15:29:40 +0100935/*
936 * generic initialization of ADC, input mixers and output mixers
937 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200938static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800939 /* power down jack detect function */
940 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100941 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100942};
943
Takashi Iwaiada509e2011-06-20 15:40:19 +0200944static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200945{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200946 struct via_spec *spec = codec->spec;
947
948 if (active)
949 spec->num_active_streams++;
950 else
951 spec->num_active_streams--;
952 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200953}
954
Takashi Iwaiece8d042011-06-19 16:24:21 +0200955static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100956 struct hda_codec *codec,
957 struct snd_pcm_substream *substream)
958{
959 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200960 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200961 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200962
Takashi Iwai25250502011-06-30 17:24:47 +0200963 spec->multiout.hp_nid = 0;
964 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
965 if (!spec->hp_independent_mode) {
966 if (!spec->hp_indep_shared)
967 spec->multiout.hp_nid = spec->hp_dac_nid;
968 } else {
969 if (spec->hp_indep_shared)
970 spec->multiout.num_dacs = cfg->line_outs - 1;
971 }
972 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200973 set_stream_active(codec, true);
974 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
975 hinfo);
976 if (err < 0) {
977 spec->multiout.hp_nid = 0;
978 set_stream_active(codec, false);
979 return err;
980 }
981 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100982}
983
Takashi Iwaiece8d042011-06-19 16:24:21 +0200984static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +0200985 struct hda_codec *codec,
986 struct snd_pcm_substream *substream)
987{
Takashi Iwaiece8d042011-06-19 16:24:21 +0200988 struct via_spec *spec = codec->spec;
989
990 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200991 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +0200992 return 0;
993}
994
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200995static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
996 struct hda_codec *codec,
997 struct snd_pcm_substream *substream)
998{
999 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001000
Takashi Iwaiece8d042011-06-19 16:24:21 +02001001 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001002 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001003 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1004 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001005 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001006 return 0;
1007}
1008
1009static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1010 struct hda_codec *codec,
1011 struct snd_pcm_substream *substream)
1012{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001013 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001014 return 0;
1015}
1016
1017static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1018 struct hda_codec *codec,
1019 unsigned int stream_tag,
1020 unsigned int format,
1021 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001022{
1023 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001024
Takashi Iwaiece8d042011-06-19 16:24:21 +02001025 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1026 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001027 vt1708_start_hp_work(spec);
1028 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001029}
1030
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001031static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1032 struct hda_codec *codec,
1033 unsigned int stream_tag,
1034 unsigned int format,
1035 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001036{
1037 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001038
Takashi Iwaiece8d042011-06-19 16:24:21 +02001039 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1040 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001041 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001042 return 0;
1043}
1044
1045static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1046 struct hda_codec *codec,
1047 struct snd_pcm_substream *substream)
1048{
1049 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001050
Takashi Iwaiece8d042011-06-19 16:24:21 +02001051 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001052 vt1708_stop_hp_work(spec);
1053 return 0;
1054}
1055
1056static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1057 struct hda_codec *codec,
1058 struct snd_pcm_substream *substream)
1059{
1060 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001061
Takashi Iwaiece8d042011-06-19 16:24:21 +02001062 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001063 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001064 return 0;
1065}
1066
Joseph Chanc577b8a2006-11-29 15:29:40 +01001067/*
1068 * Digital out
1069 */
1070static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1071 struct hda_codec *codec,
1072 struct snd_pcm_substream *substream)
1073{
1074 struct via_spec *spec = codec->spec;
1075 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1076}
1077
1078static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1079 struct hda_codec *codec,
1080 struct snd_pcm_substream *substream)
1081{
1082 struct via_spec *spec = codec->spec;
1083 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1084}
1085
Harald Welte5691ec72008-09-15 22:42:26 +08001086static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001087 struct hda_codec *codec,
1088 unsigned int stream_tag,
1089 unsigned int format,
1090 struct snd_pcm_substream *substream)
1091{
1092 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001093 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1094 stream_tag, format, substream);
1095}
Harald Welte5691ec72008-09-15 22:42:26 +08001096
Takashi Iwai9da29272009-05-07 16:31:14 +02001097static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1098 struct hda_codec *codec,
1099 struct snd_pcm_substream *substream)
1100{
1101 struct via_spec *spec = codec->spec;
1102 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001103 return 0;
1104}
1105
Joseph Chanc577b8a2006-11-29 15:29:40 +01001106/*
1107 * Analog capture
1108 */
1109static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1110 struct hda_codec *codec,
1111 unsigned int stream_tag,
1112 unsigned int format,
1113 struct snd_pcm_substream *substream)
1114{
1115 struct via_spec *spec = codec->spec;
1116
1117 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1118 stream_tag, 0, format);
1119 return 0;
1120}
1121
1122static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1123 struct hda_codec *codec,
1124 struct snd_pcm_substream *substream)
1125{
1126 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001127 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001128 return 0;
1129}
1130
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001131/* analog capture with dynamic ADC switching */
1132static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1133 struct hda_codec *codec,
1134 unsigned int stream_tag,
1135 unsigned int format,
1136 struct snd_pcm_substream *substream)
1137{
1138 struct via_spec *spec = codec->spec;
1139 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1140
1141 spec->cur_adc = spec->adc_nids[adc_idx];
1142 spec->cur_adc_stream_tag = stream_tag;
1143 spec->cur_adc_format = format;
1144 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1145 return 0;
1146}
1147
1148static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1149 struct hda_codec *codec,
1150 struct snd_pcm_substream *substream)
1151{
1152 struct via_spec *spec = codec->spec;
1153
1154 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1155 spec->cur_adc = 0;
1156 return 0;
1157}
1158
1159/* re-setup the stream if running; called from input-src put */
1160static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1161{
1162 struct via_spec *spec = codec->spec;
1163 int adc_idx = spec->inputs[cur].adc_idx;
1164 hda_nid_t adc = spec->adc_nids[adc_idx];
1165
1166 if (spec->cur_adc && spec->cur_adc != adc) {
1167 /* stream is running, let's swap the current ADC */
1168 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1169 spec->cur_adc = adc;
1170 snd_hda_codec_setup_stream(codec, adc,
1171 spec->cur_adc_stream_tag, 0,
1172 spec->cur_adc_format);
1173 return true;
1174 }
1175 return false;
1176}
1177
Takashi Iwai9af74212011-06-18 16:17:45 +02001178static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001179 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001180 .channels_min = 2,
1181 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001182 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001183 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001184 .open = via_playback_multi_pcm_open,
1185 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001186 .prepare = via_playback_multi_pcm_prepare,
1187 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001188 },
1189};
1190
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001191static const struct hda_pcm_stream via_pcm_hp_playback = {
1192 .substreams = 1,
1193 .channels_min = 2,
1194 .channels_max = 2,
1195 /* NID is set in via_build_pcms */
1196 .ops = {
1197 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001198 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001199 .prepare = via_playback_hp_pcm_prepare,
1200 .cleanup = via_playback_hp_pcm_cleanup
1201 },
1202};
1203
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001204static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001205 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001206 .channels_min = 2,
1207 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001208 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001209 /* We got noisy outputs on the right channel on VT1708 when
1210 * 24bit samples are used. Until any workaround is found,
1211 * disable the 24bit format, so far.
1212 */
1213 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1214 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001215 .open = via_playback_multi_pcm_open,
1216 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001217 .prepare = via_playback_multi_pcm_prepare,
1218 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001219 },
1220};
1221
Takashi Iwai9af74212011-06-18 16:17:45 +02001222static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001223 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001224 .channels_min = 2,
1225 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001226 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001227 .ops = {
1228 .prepare = via_capture_pcm_prepare,
1229 .cleanup = via_capture_pcm_cleanup
1230 },
1231};
1232
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001233static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1234 .substreams = 1,
1235 .channels_min = 2,
1236 .channels_max = 2,
1237 /* NID is set in via_build_pcms */
1238 .ops = {
1239 .prepare = via_dyn_adc_capture_pcm_prepare,
1240 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1241 },
1242};
1243
Takashi Iwai9af74212011-06-18 16:17:45 +02001244static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001245 .substreams = 1,
1246 .channels_min = 2,
1247 .channels_max = 2,
1248 /* NID is set in via_build_pcms */
1249 .ops = {
1250 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001251 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001252 .prepare = via_dig_playback_pcm_prepare,
1253 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001254 },
1255};
1256
Takashi Iwai9af74212011-06-18 16:17:45 +02001257static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001258 .substreams = 1,
1259 .channels_min = 2,
1260 .channels_max = 2,
1261};
1262
Takashi Iwai370bafb2011-06-20 12:47:45 +02001263/*
1264 * slave controls for virtual master
1265 */
1266static const char * const via_slave_vols[] = {
1267 "Front Playback Volume",
1268 "Surround Playback Volume",
1269 "Center Playback Volume",
1270 "LFE Playback Volume",
1271 "Side Playback Volume",
1272 "Headphone Playback Volume",
1273 "Speaker Playback Volume",
1274 NULL,
1275};
1276
1277static const char * const via_slave_sws[] = {
1278 "Front Playback Switch",
1279 "Surround Playback Switch",
1280 "Center Playback Switch",
1281 "LFE Playback Switch",
1282 "Side Playback Switch",
1283 "Headphone Playback Switch",
1284 "Speaker Playback Switch",
1285 NULL,
1286};
1287
Joseph Chanc577b8a2006-11-29 15:29:40 +01001288static int via_build_controls(struct hda_codec *codec)
1289{
1290 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001291 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001292 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001293
Takashi Iwai24088a52011-06-17 16:59:21 +02001294 if (spec->set_widgets_power_state)
1295 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1296 return -ENOMEM;
1297
Joseph Chanc577b8a2006-11-29 15:29:40 +01001298 for (i = 0; i < spec->num_mixers; i++) {
1299 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1300 if (err < 0)
1301 return err;
1302 }
1303
1304 if (spec->multiout.dig_out_nid) {
1305 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001306 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001307 spec->multiout.dig_out_nid);
1308 if (err < 0)
1309 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001310 err = snd_hda_create_spdif_share_sw(codec,
1311 &spec->multiout);
1312 if (err < 0)
1313 return err;
1314 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001315 }
1316 if (spec->dig_in_nid) {
1317 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1318 if (err < 0)
1319 return err;
1320 }
Lydia Wang17314372009-10-10 19:07:37 +08001321
Takashi Iwai370bafb2011-06-20 12:47:45 +02001322 /* if we have no master control, let's create it */
1323 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1324 unsigned int vmaster_tlv[4];
1325 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1326 HDA_OUTPUT, vmaster_tlv);
1327 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1328 vmaster_tlv, via_slave_vols);
1329 if (err < 0)
1330 return err;
1331 }
1332 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1333 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1334 NULL, via_slave_sws);
1335 if (err < 0)
1336 return err;
1337 }
1338
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001339 /* assign Capture Source enums to NID */
1340 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1341 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001342 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001343 if (err < 0)
1344 return err;
1345 }
1346
Lydia Wang17314372009-10-10 19:07:37 +08001347 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001348 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001349 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001350
Takashi Iwai603c4012008-07-30 15:01:44 +02001351 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001352 return 0;
1353}
1354
1355static int via_build_pcms(struct hda_codec *codec)
1356{
1357 struct via_spec *spec = codec->spec;
1358 struct hda_pcm *info = spec->pcm_rec;
1359
1360 codec->num_pcms = 1;
1361 codec->pcm_info = info;
1362
Takashi Iwai82673bc2011-06-17 16:24:21 +02001363 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1364 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001365 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001366
1367 if (!spec->stream_analog_playback)
1368 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001369 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001370 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001371 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1372 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001373 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1374 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001375
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001376 if (!spec->stream_analog_capture) {
1377 if (spec->dyn_adc_switch)
1378 spec->stream_analog_capture =
1379 &via_pcm_dyn_adc_analog_capture;
1380 else
1381 spec->stream_analog_capture = &via_pcm_analog_capture;
1382 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001383 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1384 *spec->stream_analog_capture;
1385 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001386 if (!spec->dyn_adc_switch)
1387 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1388 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001389
1390 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1391 codec->num_pcms++;
1392 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001393 snprintf(spec->stream_name_digital,
1394 sizeof(spec->stream_name_digital),
1395 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001396 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001397 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001398 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001399 if (!spec->stream_digital_playback)
1400 spec->stream_digital_playback =
1401 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001402 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001403 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001404 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1405 spec->multiout.dig_out_nid;
1406 }
1407 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001408 if (!spec->stream_digital_capture)
1409 spec->stream_digital_capture =
1410 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001411 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001412 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001413 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1414 spec->dig_in_nid;
1415 }
1416 }
1417
Takashi Iwaiece8d042011-06-19 16:24:21 +02001418 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001419 codec->num_pcms++;
1420 info++;
1421 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1422 "%s HP", codec->chip_name);
1423 info->name = spec->stream_name_hp;
1424 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1425 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001426 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001427 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001428 return 0;
1429}
1430
1431static void via_free(struct hda_codec *codec)
1432{
1433 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434
1435 if (!spec)
1436 return;
1437
Takashi Iwai603c4012008-07-30 15:01:44 +02001438 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001439 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001440 kfree(spec->bind_cap_vol);
1441 kfree(spec->bind_cap_sw);
1442 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001443}
1444
Takashi Iwai64be2852011-06-17 16:51:39 +02001445/* mute/unmute outputs */
1446static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1447 hda_nid_t *pins, bool mute)
1448{
1449 int i;
1450 for (i = 0; i < num_pins; i++)
1451 snd_hda_codec_write(codec, pins[i], 0,
1452 AC_VERB_SET_PIN_WIDGET_CONTROL,
1453 mute ? 0 : PIN_OUT);
1454}
1455
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001456/* mute internal speaker if line-out is plugged */
1457static void via_line_automute(struct hda_codec *codec, int present)
1458{
1459 struct via_spec *spec = codec->spec;
1460
1461 if (!spec->autocfg.speaker_outs)
1462 return;
1463 if (!present)
1464 present = snd_hda_jack_detect(codec,
1465 spec->autocfg.line_out_pins[0]);
1466 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1467 spec->autocfg.speaker_pins,
1468 present);
1469}
1470
Harald Welte69e52a82008-09-09 15:57:32 +08001471/* mute internal speaker if HP is plugged */
1472static void via_hp_automute(struct hda_codec *codec)
1473{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001474 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001475 struct via_spec *spec = codec->spec;
1476
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001477 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001478 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001479 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001480 if (spec->smart51_enabled)
1481 nums = spec->autocfg.line_outs + spec->smart51_nums;
1482 else
1483 nums = spec->autocfg.line_outs;
1484 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001485 spec->autocfg.line_out_pins,
1486 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001487 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001488 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001489}
1490
Harald Welte69e52a82008-09-09 15:57:32 +08001491static void via_gpio_control(struct hda_codec *codec)
1492{
1493 unsigned int gpio_data;
1494 unsigned int vol_counter;
1495 unsigned int vol;
1496 unsigned int master_vol;
1497
1498 struct via_spec *spec = codec->spec;
1499
1500 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1501 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1502
1503 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1504 0xF84, 0) & 0x3F0000) >> 16;
1505
1506 vol = vol_counter & 0x1F;
1507 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1508 AC_VERB_GET_AMP_GAIN_MUTE,
1509 AC_AMP_GET_INPUT);
1510
1511 if (gpio_data == 0x02) {
1512 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001513 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1514 AC_VERB_SET_PIN_WIDGET_CONTROL,
1515 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001516 if (vol_counter & 0x20) {
1517 /* decrease volume */
1518 if (vol > master_vol)
1519 vol = master_vol;
1520 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1521 0, HDA_AMP_VOLMASK,
1522 master_vol-vol);
1523 } else {
1524 /* increase volume */
1525 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1526 HDA_AMP_VOLMASK,
1527 ((master_vol+vol) > 0x2A) ? 0x2A :
1528 (master_vol+vol));
1529 }
1530 } else if (!(gpio_data & 0x02)) {
1531 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001532 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1533 AC_VERB_SET_PIN_WIDGET_CONTROL,
1534 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001535 }
1536}
1537
1538/* unsolicited event for jack sensing */
1539static void via_unsol_event(struct hda_codec *codec,
1540 unsigned int res)
1541{
1542 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001543
Lydia Wanga34df192009-10-10 19:08:01 +08001544 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001545 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001546
1547 res &= ~VIA_JACK_EVENT;
1548
1549 if (res == VIA_HP_EVENT)
1550 via_hp_automute(codec);
1551 else if (res == VIA_GPIO_EVENT)
1552 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001553 else if (res == VIA_LINE_EVENT)
1554 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001555}
1556
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001557#ifdef SND_HDA_NEEDS_RESUME
1558static int via_suspend(struct hda_codec *codec, pm_message_t state)
1559{
1560 struct via_spec *spec = codec->spec;
1561 vt1708_stop_hp_work(spec);
1562 return 0;
1563}
1564#endif
1565
Takashi Iwaicb53c622007-08-10 17:21:45 +02001566#ifdef CONFIG_SND_HDA_POWER_SAVE
1567static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1568{
1569 struct via_spec *spec = codec->spec;
1570 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1571}
1572#endif
1573
Joseph Chanc577b8a2006-11-29 15:29:40 +01001574/*
1575 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001576
1577static int via_init(struct hda_codec *codec);
1578
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001579static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001580 .build_controls = via_build_controls,
1581 .build_pcms = via_build_pcms,
1582 .init = via_init,
1583 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001584 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001585#ifdef SND_HDA_NEEDS_RESUME
1586 .suspend = via_suspend,
1587#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001588#ifdef CONFIG_SND_HDA_POWER_SAVE
1589 .check_power_status = via_check_power_status,
1590#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001591};
1592
Takashi Iwai4a796162011-06-17 17:53:38 +02001593static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001594{
Takashi Iwai4a796162011-06-17 17:53:38 +02001595 struct via_spec *spec = codec->spec;
1596 int i;
1597
1598 for (i = 0; i < spec->multiout.num_dacs; i++) {
1599 if (spec->multiout.dac_nids[i] == dac)
1600 return false;
1601 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001602 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001603 return false;
1604 return true;
1605}
1606
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001607static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001608 hda_nid_t target_dac, struct nid_path *path,
1609 int depth, int wid_type)
1610{
1611 hda_nid_t conn[8];
1612 int i, nums;
1613
1614 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1615 for (i = 0; i < nums; i++) {
1616 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1617 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001618 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1619 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001620 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001621 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001622 return false;
1623 for (i = 0; i < nums; i++) {
1624 unsigned int type;
1625 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1626 if (type == AC_WID_AUD_OUT ||
1627 (wid_type != -1 && type != wid_type))
1628 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001629 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001630 path, depth + 1, AC_WID_AUD_SEL))
1631 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001632 }
1633 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001634
1635 found:
1636 path->path[path->depth] = conn[i];
1637 path->idx[path->depth] = i;
1638 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1639 path->multi[path->depth] = 1;
1640 path->depth++;
1641 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001642}
1643
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001644static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1645 hda_nid_t target_dac, struct nid_path *path)
1646{
1647 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1648 path->path[path->depth] = nid;
1649 path->depth++;
1650 return true;
1651 }
1652 return false;
1653}
1654
Takashi Iwai4a796162011-06-17 17:53:38 +02001655static int via_auto_fill_dac_nids(struct hda_codec *codec)
1656{
1657 struct via_spec *spec = codec->spec;
1658 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001659 int i;
1660 hda_nid_t nid;
1661
Joseph Chanc577b8a2006-11-29 15:29:40 +01001662 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001663 spec->multiout.num_dacs = cfg->line_outs;
1664 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001665 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001666 if (!nid)
1667 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001668 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1669 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001670 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001671 return 0;
1672}
1673
Takashi Iwai4a796162011-06-17 17:53:38 +02001674static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001675 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001676{
Takashi Iwai4a796162011-06-17 17:53:38 +02001677 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001678 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001679 hda_nid_t dac, pin, sel, nid;
1680 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001681
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001682 dac = check_dac ? path->path[0] : 0;
1683 pin = path->path[path->depth - 1];
1684 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001685
Takashi Iwai8df2a312011-06-21 11:48:29 +02001686 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001687 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001688 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001689 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001690 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1691 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001692 else
1693 nid = 0;
1694 if (nid) {
1695 sprintf(name, "%s Playback Volume", pfx);
1696 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001697 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001698 if (err < 0)
1699 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001700 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001701 }
1702
Takashi Iwai8df2a312011-06-21 11:48:29 +02001703 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001704 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001705 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001706 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001707 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1708 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001709 else
1710 nid = 0;
1711 if (nid) {
1712 sprintf(name, "%s Playback Switch", pfx);
1713 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1714 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1715 if (err < 0)
1716 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001717 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001718 }
1719 return 0;
1720}
1721
Takashi Iwaif4a78282011-06-17 18:46:48 +02001722static void mangle_smart51(struct hda_codec *codec)
1723{
1724 struct via_spec *spec = codec->spec;
1725 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001726 struct auto_pin_cfg_item *ins = cfg->inputs;
1727 int i, j, nums, attr;
1728 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001729
Takashi Iwai0f98c242011-06-21 12:51:33 +02001730 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1731 nums = 0;
1732 for (i = 0; i < cfg->num_inputs; i++) {
1733 unsigned int def;
1734 if (ins[i].type > AUTO_PIN_LINE_IN)
1735 continue;
1736 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1737 if (snd_hda_get_input_pin_attr(def) != attr)
1738 continue;
1739 for (j = 0; j < nums; j++)
1740 if (ins[pins[j]].type < ins[i].type) {
1741 memmove(pins + j + 1, pins + j,
1742 (nums - j - 1) * sizeof(int));
1743 break;
1744 }
1745 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001746 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001747 }
1748 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001749 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001750 for (i = 0; i < nums; i++) {
1751 hda_nid_t pin = ins[pins[i]].pin;
1752 spec->smart51_pins[spec->smart51_nums++] = pin;
1753 cfg->line_out_pins[cfg->line_outs++] = pin;
1754 if (cfg->line_outs == 3)
1755 break;
1756 }
1757 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001758 }
1759}
1760
Takashi Iwai4a796162011-06-17 17:53:38 +02001761/* add playback controls from the parsed DAC table */
1762static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1763{
1764 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001765 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001766 static const char * const chname[4] = {
1767 "Front", "Surround", "C/LFE", "Side"
1768 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001769 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001770 int old_line_outs;
1771
1772 /* check smart51 */
1773 old_line_outs = cfg->line_outs;
1774 if (cfg->line_outs == 1)
1775 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001776
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001777 err = via_auto_fill_dac_nids(codec);
1778 if (err < 0)
1779 return err;
1780
Takashi Iwai4a796162011-06-17 17:53:38 +02001781 for (i = 0; i < cfg->line_outs; i++) {
1782 hda_nid_t pin, dac;
1783 pin = cfg->line_out_pins[i];
1784 dac = spec->multiout.dac_nids[i];
1785 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001786 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001787 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001788 err = create_ch_ctls(codec, "Center", 1, true,
1789 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001790 if (err < 0)
1791 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001792 err = create_ch_ctls(codec, "LFE", 2, true,
1793 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001794 if (err < 0)
1795 return err;
1796 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001797 const char *pfx = chname[i];
1798 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1799 cfg->line_outs == 1)
1800 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001801 err = create_ch_ctls(codec, pfx, 3, true,
1802 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001803 if (err < 0)
1804 return err;
1805 }
1806 }
1807
Takashi Iwai4a796162011-06-17 17:53:38 +02001808 idx = get_connection_index(codec, spec->aa_mix_nid,
1809 spec->multiout.dac_nids[0]);
1810 if (idx >= 0) {
1811 /* add control to mixer */
1812 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1813 "PCM Playback Volume",
1814 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1815 idx, HDA_INPUT));
1816 if (err < 0)
1817 return err;
1818 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1819 "PCM Playback Switch",
1820 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1821 idx, HDA_INPUT));
1822 if (err < 0)
1823 return err;
1824 }
1825
Takashi Iwaif4a78282011-06-17 18:46:48 +02001826 cfg->line_outs = old_line_outs;
1827
Joseph Chanc577b8a2006-11-29 15:29:40 +01001828 return 0;
1829}
1830
Takashi Iwai4a796162011-06-17 17:53:38 +02001831static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001832{
Takashi Iwai4a796162011-06-17 17:53:38 +02001833 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001834 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001835 int err;
1836
1837 if (!pin)
1838 return 0;
1839
Takashi Iwai8df2a312011-06-21 11:48:29 +02001840 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001841 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001842 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1843 parse_output_path(codec, pin,
1844 spec->multiout.dac_nids[HDA_SIDE],
1845 &spec->hp_path)) {
1846 spec->hp_dac_nid = spec->hp_path.path[0];
1847 spec->hp_indep_shared = true;
1848 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001849
Takashi Iwaiece8d042011-06-19 16:24:21 +02001850 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001851 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001852 !spec->hp_dac_nid)
1853 return 0;
1854
Takashi Iwai25250502011-06-30 17:24:47 +02001855 if (spec->hp_dac_nid && !spec->hp_indep_shared)
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001856 path = &spec->hp_path;
1857 else
1858 path = &spec->hp_dep_path;
1859 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001860 if (err < 0)
1861 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001862 if (spec->hp_dac_nid) {
1863 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1864 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1865 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001866
Joseph Chanc577b8a2006-11-29 15:29:40 +01001867 return 0;
1868}
1869
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001870static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1871{
1872 struct via_spec *spec = codec->spec;
1873 hda_nid_t pin, dac;
1874
1875 pin = spec->autocfg.speaker_pins[0];
1876 if (!spec->autocfg.speaker_outs || !pin)
1877 return 0;
1878
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001879 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1880 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001881 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001882 return create_ch_ctls(codec, "Speaker", 3, true,
1883 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001884 }
1885 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001886 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001887 return create_ch_ctls(codec, "Speaker", 3, false,
1888 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001889
1890 return 0;
1891}
1892
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001893/* look for ADCs */
1894static int via_fill_adcs(struct hda_codec *codec)
1895{
1896 struct via_spec *spec = codec->spec;
1897 hda_nid_t nid = codec->start_nid;
1898 int i;
1899
1900 for (i = 0; i < codec->num_nodes; i++, nid++) {
1901 unsigned int wcaps = get_wcaps(codec, nid);
1902 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1903 continue;
1904 if (wcaps & AC_WCAP_DIGITAL)
1905 continue;
1906 if (!(wcaps & AC_WCAP_CONN_LIST))
1907 continue;
1908 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1909 return -ENOMEM;
1910 spec->adc_nids[spec->num_adc_nids++] = nid;
1911 }
1912 return 0;
1913}
1914
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001915/* input-src control */
1916static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1917 struct snd_ctl_elem_info *uinfo)
1918{
1919 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1920 struct via_spec *spec = codec->spec;
1921
1922 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1923 uinfo->count = 1;
1924 uinfo->value.enumerated.items = spec->num_inputs;
1925 if (uinfo->value.enumerated.item >= spec->num_inputs)
1926 uinfo->value.enumerated.item = spec->num_inputs - 1;
1927 strcpy(uinfo->value.enumerated.name,
1928 spec->inputs[uinfo->value.enumerated.item].label);
1929 return 0;
1930}
1931
1932static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1933 struct snd_ctl_elem_value *ucontrol)
1934{
1935 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1936 struct via_spec *spec = codec->spec;
1937 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1938
1939 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1940 return 0;
1941}
1942
1943static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1944 struct snd_ctl_elem_value *ucontrol)
1945{
1946 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1947 struct via_spec *spec = codec->spec;
1948 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1949 hda_nid_t mux;
1950 int cur;
1951
1952 cur = ucontrol->value.enumerated.item[0];
1953 if (cur < 0 || cur >= spec->num_inputs)
1954 return -EINVAL;
1955 if (spec->cur_mux[idx] == cur)
1956 return 0;
1957 spec->cur_mux[idx] = cur;
1958 if (spec->dyn_adc_switch) {
1959 int adc_idx = spec->inputs[cur].adc_idx;
1960 mux = spec->mux_nids[adc_idx];
1961 via_dyn_adc_pcm_resetup(codec, cur);
1962 } else {
1963 mux = spec->mux_nids[idx];
1964 if (snd_BUG_ON(!mux))
1965 return -EINVAL;
1966 }
1967
1968 if (mux) {
1969 /* switch to D0 beofre change index */
1970 if (snd_hda_codec_read(codec, mux, 0,
1971 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1972 snd_hda_codec_write(codec, mux, 0,
1973 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1974 snd_hda_codec_write(codec, mux, 0,
1975 AC_VERB_SET_CONNECT_SEL,
1976 spec->inputs[cur].mux_idx);
1977 }
1978
1979 /* update jack power state */
1980 set_widgets_power_state(codec);
1981 return 0;
1982}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001983
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001984static const struct snd_kcontrol_new via_input_src_ctl = {
1985 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1986 /* The multiple "Capture Source" controls confuse alsamixer
1987 * So call somewhat different..
1988 */
1989 /* .name = "Capture Source", */
1990 .name = "Input Source",
1991 .info = via_mux_enum_info,
1992 .get = via_mux_enum_get,
1993 .put = via_mux_enum_put,
1994};
1995
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001996static int create_input_src_ctls(struct hda_codec *codec, int count)
1997{
1998 struct via_spec *spec = codec->spec;
1999 struct snd_kcontrol_new *knew;
2000
2001 if (spec->num_inputs <= 1 || !count)
2002 return 0; /* no need for single src */
2003
2004 knew = via_clone_control(spec, &via_input_src_ctl);
2005 if (!knew)
2006 return -ENOMEM;
2007 knew->count = count;
2008 return 0;
2009}
2010
2011/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002012static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2013{
2014 struct hda_amp_list *list;
2015
2016 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2017 return;
2018 list = spec->loopback_list + spec->num_loopbacks;
2019 list->nid = mix;
2020 list->dir = HDA_INPUT;
2021 list->idx = idx;
2022 spec->num_loopbacks++;
2023 spec->loopback.amplist = spec->loopback_list;
2024}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002025
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002026static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002027 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002028{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002029 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002030}
2031
2032/* add the input-route to the given pin */
2033static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002034{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002035 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002036 int c, idx;
2037
2038 spec->inputs[spec->num_inputs].adc_idx = -1;
2039 spec->inputs[spec->num_inputs].pin = pin;
2040 for (c = 0; c < spec->num_adc_nids; c++) {
2041 if (spec->mux_nids[c]) {
2042 idx = get_connection_index(codec, spec->mux_nids[c],
2043 pin);
2044 if (idx < 0)
2045 continue;
2046 spec->inputs[spec->num_inputs].mux_idx = idx;
2047 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002048 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002049 continue;
2050 }
2051 spec->inputs[spec->num_inputs].adc_idx = c;
2052 /* Can primary ADC satisfy all inputs? */
2053 if (!spec->dyn_adc_switch &&
2054 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2055 snd_printd(KERN_INFO
2056 "via: dynamic ADC switching enabled\n");
2057 spec->dyn_adc_switch = 1;
2058 }
2059 return true;
2060 }
2061 return false;
2062}
2063
2064static int get_mux_nids(struct hda_codec *codec);
2065
2066/* parse input-routes; fill ADCs, MUXs and input-src entries */
2067static int parse_analog_inputs(struct hda_codec *codec)
2068{
2069 struct via_spec *spec = codec->spec;
2070 const struct auto_pin_cfg *cfg = &spec->autocfg;
2071 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002072
2073 err = via_fill_adcs(codec);
2074 if (err < 0)
2075 return err;
2076 err = get_mux_nids(codec);
2077 if (err < 0)
2078 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002079
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002080 /* fill all input-routes */
2081 for (i = 0; i < cfg->num_inputs; i++) {
2082 if (add_input_route(codec, cfg->inputs[i].pin))
2083 spec->inputs[spec->num_inputs++].label =
2084 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002085 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002086
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002087 /* check for internal loopback recording */
2088 if (spec->aa_mix_nid &&
2089 add_input_route(codec, spec->aa_mix_nid))
2090 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2091
2092 return 0;
2093}
2094
2095/* create analog-loopback volume/switch controls */
2096static int create_loopback_ctls(struct hda_codec *codec)
2097{
2098 struct via_spec *spec = codec->spec;
2099 const struct auto_pin_cfg *cfg = &spec->autocfg;
2100 const char *prev_label = NULL;
2101 int type_idx = 0;
2102 int i, j, err, idx;
2103
2104 if (!spec->aa_mix_nid)
2105 return 0;
2106
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002107 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002108 hda_nid_t pin = cfg->inputs[i].pin;
2109 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2110
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002111 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002112 type_idx++;
2113 else
2114 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002115 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002116 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2117 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002118 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002119 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002120 if (err < 0)
2121 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002122 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002123 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002124
2125 /* remember the label for smart51 control */
2126 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002127 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002128 spec->smart51_idxs[j] = idx;
2129 spec->smart51_labels[j] = label;
2130 break;
2131 }
2132 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002133 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002134 return 0;
2135}
2136
2137/* create mic-boost controls (if present) */
2138static int create_mic_boost_ctls(struct hda_codec *codec)
2139{
2140 struct via_spec *spec = codec->spec;
2141 const struct auto_pin_cfg *cfg = &spec->autocfg;
2142 int i, err;
2143
2144 for (i = 0; i < cfg->num_inputs; i++) {
2145 hda_nid_t pin = cfg->inputs[i].pin;
2146 unsigned int caps;
2147 const char *label;
2148 char name[32];
2149
2150 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2151 continue;
2152 caps = query_amp_caps(codec, pin, HDA_INPUT);
2153 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2154 continue;
2155 label = hda_get_autocfg_input_label(codec, cfg, i);
2156 snprintf(name, sizeof(name), "%s Boost Volume", label);
2157 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2158 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2159 if (err < 0)
2160 return err;
2161 }
2162 return 0;
2163}
2164
2165/* create capture and input-src controls for multiple streams */
2166static int create_multi_adc_ctls(struct hda_codec *codec)
2167{
2168 struct via_spec *spec = codec->spec;
2169 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002170
2171 /* create capture mixer elements */
2172 for (i = 0; i < spec->num_adc_nids; i++) {
2173 hda_nid_t adc = spec->adc_nids[i];
2174 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2175 "Capture Volume", i,
2176 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2177 HDA_INPUT));
2178 if (err < 0)
2179 return err;
2180 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2181 "Capture Switch", i,
2182 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2183 HDA_INPUT));
2184 if (err < 0)
2185 return err;
2186 }
2187
2188 /* input-source control */
2189 for (i = 0; i < spec->num_adc_nids; i++)
2190 if (!spec->mux_nids[i])
2191 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002192 err = create_input_src_ctls(codec, i);
2193 if (err < 0)
2194 return err;
2195 return 0;
2196}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002197
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002198/* bind capture volume/switch */
2199static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2200 HDA_BIND_VOL("Capture Volume", 0);
2201static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2202 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002203
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002204static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2205 struct hda_ctl_ops *ops)
2206{
2207 struct hda_bind_ctls *ctl;
2208 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002209
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002210 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2211 if (!ctl)
2212 return -ENOMEM;
2213 ctl->ops = ops;
2214 for (i = 0; i < spec->num_adc_nids; i++)
2215 ctl->values[i] =
2216 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2217 *ctl_ret = ctl;
2218 return 0;
2219}
2220
2221/* create capture and input-src controls for dynamic ADC-switch case */
2222static int create_dyn_adc_ctls(struct hda_codec *codec)
2223{
2224 struct via_spec *spec = codec->spec;
2225 struct snd_kcontrol_new *knew;
2226 int err;
2227
2228 /* set up the bind capture ctls */
2229 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2230 if (err < 0)
2231 return err;
2232 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2233 if (err < 0)
2234 return err;
2235
2236 /* create capture mixer elements */
2237 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2238 if (!knew)
2239 return -ENOMEM;
2240 knew->private_value = (long)spec->bind_cap_vol;
2241
2242 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2243 if (!knew)
2244 return -ENOMEM;
2245 knew->private_value = (long)spec->bind_cap_sw;
2246
2247 /* input-source control */
2248 err = create_input_src_ctls(codec, 1);
2249 if (err < 0)
2250 return err;
2251 return 0;
2252}
2253
2254/* parse and create capture-related stuff */
2255static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2256{
2257 struct via_spec *spec = codec->spec;
2258 int err;
2259
2260 err = parse_analog_inputs(codec);
2261 if (err < 0)
2262 return err;
2263 if (spec->dyn_adc_switch)
2264 err = create_dyn_adc_ctls(codec);
2265 else
2266 err = create_multi_adc_ctls(codec);
2267 if (err < 0)
2268 return err;
2269 err = create_loopback_ctls(codec);
2270 if (err < 0)
2271 return err;
2272 err = create_mic_boost_ctls(codec);
2273 if (err < 0)
2274 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002275 return 0;
2276}
2277
Harald Welte76d9b0d2008-09-09 15:50:37 +08002278static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2279{
2280 unsigned int def_conf;
2281 unsigned char seqassoc;
2282
Takashi Iwai2f334f92009-02-20 14:37:42 +01002283 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002284 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2285 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002286 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2287 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2288 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2289 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002290 }
2291
2292 return;
2293}
2294
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002295static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002296 struct snd_ctl_elem_value *ucontrol)
2297{
2298 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2299 struct via_spec *spec = codec->spec;
2300
2301 if (spec->codec_type != VT1708)
2302 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002303 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002304 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002305 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002306 return 0;
2307}
2308
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002309static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002310 struct snd_ctl_elem_value *ucontrol)
2311{
2312 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2313 struct via_spec *spec = codec->spec;
2314 int change;
2315
2316 if (spec->codec_type != VT1708)
2317 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002318 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002319 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002320 == !spec->vt1708_jack_detect;
2321 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002322 mute_aa_path(codec, 1);
2323 notify_aa_path_ctls(codec);
2324 }
2325 return change;
2326}
2327
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002328static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2329 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2330 .name = "Jack Detect",
2331 .count = 1,
2332 .info = snd_ctl_boolean_mono_info,
2333 .get = vt1708_jack_detect_get,
2334 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002335};
2336
Takashi Iwai12daef62011-06-18 17:45:49 +02002337static void fill_dig_outs(struct hda_codec *codec);
2338static void fill_dig_in(struct hda_codec *codec);
2339
2340static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002341{
2342 struct via_spec *spec = codec->spec;
2343 int err;
2344
2345 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2346 if (err < 0)
2347 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002348 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002349 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002350
Takashi Iwai4a796162011-06-17 17:53:38 +02002351 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002352 if (err < 0)
2353 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002354 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002355 if (err < 0)
2356 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002357 err = via_auto_create_speaker_ctls(codec);
2358 if (err < 0)
2359 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002360 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002361 if (err < 0)
2362 return err;
2363
2364 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2365
Takashi Iwai12daef62011-06-18 17:45:49 +02002366 fill_dig_outs(codec);
2367 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002368
Takashi Iwai603c4012008-07-30 15:01:44 +02002369 if (spec->kctls.list)
2370 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002371
Joseph Chanc577b8a2006-11-29 15:29:40 +01002372
Takashi Iwai8df2a312011-06-21 11:48:29 +02002373 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002374 err = via_hp_build(codec);
2375 if (err < 0)
2376 return err;
2377 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002378
Takashi Iwaif4a78282011-06-17 18:46:48 +02002379 err = via_smart51_build(codec);
2380 if (err < 0)
2381 return err;
2382
Takashi Iwai5d417622011-06-20 11:32:27 +02002383 /* assign slave outs */
2384 if (spec->slave_dig_outs[0])
2385 codec->slave_dig_outs = spec->slave_dig_outs;
2386
Joseph Chanc577b8a2006-11-29 15:29:40 +01002387 return 1;
2388}
2389
Takashi Iwai5d417622011-06-20 11:32:27 +02002390static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002391{
Lydia Wang25eaba22009-10-10 19:08:43 +08002392 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002393 if (spec->multiout.dig_out_nid)
2394 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2395 if (spec->slave_dig_outs[0])
2396 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2397}
Lydia Wang25eaba22009-10-10 19:08:43 +08002398
Takashi Iwai5d417622011-06-20 11:32:27 +02002399static void via_auto_init_dig_in(struct hda_codec *codec)
2400{
2401 struct via_spec *spec = codec->spec;
2402 if (!spec->dig_in_nid)
2403 return;
2404 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2405 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2406}
2407
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002408/* initialize the unsolicited events */
2409static void via_auto_init_unsol_event(struct hda_codec *codec)
2410{
2411 struct via_spec *spec = codec->spec;
2412 struct auto_pin_cfg *cfg = &spec->autocfg;
2413 unsigned int ev;
2414 int i;
2415
2416 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2417 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2418 AC_VERB_SET_UNSOLICITED_ENABLE,
2419 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2420
2421 if (cfg->speaker_pins[0])
2422 ev = VIA_LINE_EVENT;
2423 else
2424 ev = 0;
2425 for (i = 0; i < cfg->line_outs; i++) {
2426 if (cfg->line_out_pins[i] &&
2427 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002428 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002429 AC_VERB_SET_UNSOLICITED_ENABLE,
2430 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2431 }
2432
2433 for (i = 0; i < cfg->num_inputs; i++) {
2434 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2435 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2436 AC_VERB_SET_UNSOLICITED_ENABLE,
2437 AC_USRSP_EN | VIA_JACK_EVENT);
2438 }
2439}
2440
Takashi Iwai5d417622011-06-20 11:32:27 +02002441static int via_init(struct hda_codec *codec)
2442{
2443 struct via_spec *spec = codec->spec;
2444 int i;
2445
2446 for (i = 0; i < spec->num_iverbs; i++)
2447 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2448
Joseph Chanc577b8a2006-11-29 15:29:40 +01002449 via_auto_init_multi_out(codec);
2450 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002451 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002452 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002453 via_auto_init_dig_outs(codec);
2454 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002455
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002456 via_auto_init_unsol_event(codec);
2457
2458 via_hp_automute(codec);
2459 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002460
Joseph Chanc577b8a2006-11-29 15:29:40 +01002461 return 0;
2462}
2463
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002464static void vt1708_update_hp_jack_state(struct work_struct *work)
2465{
2466 struct via_spec *spec = container_of(work, struct via_spec,
2467 vt1708_hp_work.work);
2468 if (spec->codec_type != VT1708)
2469 return;
2470 /* if jack state toggled */
2471 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002472 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002473 spec->vt1708_hp_present ^= 1;
2474 via_hp_automute(spec->codec);
2475 }
2476 vt1708_start_hp_work(spec);
2477}
2478
Takashi Iwai337b9d02009-07-07 18:18:59 +02002479static int get_mux_nids(struct hda_codec *codec)
2480{
2481 struct via_spec *spec = codec->spec;
2482 hda_nid_t nid, conn[8];
2483 unsigned int type;
2484 int i, n;
2485
2486 for (i = 0; i < spec->num_adc_nids; i++) {
2487 nid = spec->adc_nids[i];
2488 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002489 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002490 if (type == AC_WID_PIN)
2491 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002492 n = snd_hda_get_connections(codec, nid, conn,
2493 ARRAY_SIZE(conn));
2494 if (n <= 0)
2495 break;
2496 if (n > 1) {
2497 spec->mux_nids[i] = nid;
2498 break;
2499 }
2500 nid = conn[0];
2501 }
2502 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002503 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002504}
2505
Joseph Chanc577b8a2006-11-29 15:29:40 +01002506static int patch_vt1708(struct hda_codec *codec)
2507{
2508 struct via_spec *spec;
2509 int err;
2510
2511 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002512 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002513 if (spec == NULL)
2514 return -ENOMEM;
2515
Takashi Iwai620e2b22011-06-17 17:19:19 +02002516 spec->aa_mix_nid = 0x17;
2517
Takashi Iwai12daef62011-06-18 17:45:49 +02002518 /* Add HP and CD pin config connect bit re-config action */
2519 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2520 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2521
Joseph Chanc577b8a2006-11-29 15:29:40 +01002522 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002523 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002524 if (err < 0) {
2525 via_free(codec);
2526 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002527 }
2528
Takashi Iwai12daef62011-06-18 17:45:49 +02002529 /* add jack detect on/off control */
2530 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2531 return -ENOMEM;
2532
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002533 /* disable 32bit format on VT1708 */
2534 if (codec->vendor_id == 0x11061708)
2535 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002536
Lydia Wange322a362011-06-29 13:52:02 +08002537 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2538
Joseph Chanc577b8a2006-11-29 15:29:40 +01002539 codec->patch_ops = via_patch_ops;
2540
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002541 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002542 return 0;
2543}
2544
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002545static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002546{
2547 struct via_spec *spec;
2548 int err;
2549
2550 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002551 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002552 if (spec == NULL)
2553 return -ENOMEM;
2554
Takashi Iwai620e2b22011-06-17 17:19:19 +02002555 spec->aa_mix_nid = 0x18;
2556
Takashi Iwai12daef62011-06-18 17:45:49 +02002557 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002558 if (err < 0) {
2559 via_free(codec);
2560 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002561 }
2562
Joseph Chanc577b8a2006-11-29 15:29:40 +01002563 codec->patch_ops = via_patch_ops;
2564
Josepch Chanf7278fd2007-12-13 16:40:40 +01002565 return 0;
2566}
2567
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002568static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2569{
2570 struct via_spec *spec = codec->spec;
2571 int imux_is_smixer;
2572 unsigned int parm;
2573 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002574 if ((spec->codec_type != VT1708B_4CH) &&
2575 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002576 is_8ch = 1;
2577
2578 /* SW0 (17h) = stereo mixer */
2579 imux_is_smixer =
2580 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2581 == ((spec->codec_type == VT1708S) ? 5 : 0));
2582 /* inputs */
2583 /* PW 1/2/5 (1ah/1bh/1eh) */
2584 parm = AC_PWRST_D3;
2585 set_pin_power_state(codec, 0x1a, &parm);
2586 set_pin_power_state(codec, 0x1b, &parm);
2587 set_pin_power_state(codec, 0x1e, &parm);
2588 if (imux_is_smixer)
2589 parm = AC_PWRST_D0;
2590 /* SW0 (17h), AIW 0/1 (13h/14h) */
2591 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2592 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2593 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2594
2595 /* outputs */
2596 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2597 parm = AC_PWRST_D3;
2598 set_pin_power_state(codec, 0x19, &parm);
2599 if (spec->smart51_enabled)
2600 set_pin_power_state(codec, 0x1b, &parm);
2601 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2602 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2603
2604 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2605 if (is_8ch) {
2606 parm = AC_PWRST_D3;
2607 set_pin_power_state(codec, 0x22, &parm);
2608 if (spec->smart51_enabled)
2609 set_pin_power_state(codec, 0x1a, &parm);
2610 snd_hda_codec_write(codec, 0x26, 0,
2611 AC_VERB_SET_POWER_STATE, parm);
2612 snd_hda_codec_write(codec, 0x24, 0,
2613 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002614 } else if (codec->vendor_id == 0x11064397) {
2615 /* PW7(23h), SW2(27h), AOW2(25h) */
2616 parm = AC_PWRST_D3;
2617 set_pin_power_state(codec, 0x23, &parm);
2618 if (spec->smart51_enabled)
2619 set_pin_power_state(codec, 0x1a, &parm);
2620 snd_hda_codec_write(codec, 0x27, 0,
2621 AC_VERB_SET_POWER_STATE, parm);
2622 snd_hda_codec_write(codec, 0x25, 0,
2623 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002624 }
2625
2626 /* PW 3/4/7 (1ch/1dh/23h) */
2627 parm = AC_PWRST_D3;
2628 /* force to D0 for internal Speaker */
2629 set_pin_power_state(codec, 0x1c, &parm);
2630 set_pin_power_state(codec, 0x1d, &parm);
2631 if (is_8ch)
2632 set_pin_power_state(codec, 0x23, &parm);
2633
2634 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2635 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2636 imux_is_smixer ? AC_PWRST_D0 : parm);
2637 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2638 if (is_8ch) {
2639 snd_hda_codec_write(codec, 0x25, 0,
2640 AC_VERB_SET_POWER_STATE, parm);
2641 snd_hda_codec_write(codec, 0x27, 0,
2642 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002643 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2644 snd_hda_codec_write(codec, 0x25, 0,
2645 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002646}
2647
Lydia Wang518bf3b2009-10-10 19:07:29 +08002648static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002649static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002650{
2651 struct via_spec *spec;
2652 int err;
2653
Lydia Wang518bf3b2009-10-10 19:07:29 +08002654 if (get_codec_type(codec) == VT1708BCE)
2655 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002656
Josepch Chanf7278fd2007-12-13 16:40:40 +01002657 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002658 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002659 if (spec == NULL)
2660 return -ENOMEM;
2661
Takashi Iwai620e2b22011-06-17 17:19:19 +02002662 spec->aa_mix_nid = 0x16;
2663
Josepch Chanf7278fd2007-12-13 16:40:40 +01002664 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002665 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002666 if (err < 0) {
2667 via_free(codec);
2668 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002669 }
2670
Josepch Chanf7278fd2007-12-13 16:40:40 +01002671 codec->patch_ops = via_patch_ops;
2672
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002673 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2674
Josepch Chanf7278fd2007-12-13 16:40:40 +01002675 return 0;
2676}
2677
Harald Welted949cac2008-09-09 15:56:01 +08002678/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002679static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002680 /* Enable Mic Boost Volume backdoor */
2681 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002682 /* don't bybass mixer */
2683 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002684 { }
2685};
2686
Takashi Iwai9da29272009-05-07 16:31:14 +02002687/* fill out digital output widgets; one for master and one for slave outputs */
2688static void fill_dig_outs(struct hda_codec *codec)
2689{
2690 struct via_spec *spec = codec->spec;
2691 int i;
2692
2693 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2694 hda_nid_t nid;
2695 int conn;
2696
2697 nid = spec->autocfg.dig_out_pins[i];
2698 if (!nid)
2699 continue;
2700 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2701 if (conn < 1)
2702 continue;
2703 if (!spec->multiout.dig_out_nid)
2704 spec->multiout.dig_out_nid = nid;
2705 else {
2706 spec->slave_dig_outs[0] = nid;
2707 break; /* at most two dig outs */
2708 }
2709 }
2710}
2711
Takashi Iwai12daef62011-06-18 17:45:49 +02002712static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002713{
2714 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002715 hda_nid_t dig_nid;
2716 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002717
Takashi Iwai12daef62011-06-18 17:45:49 +02002718 if (!spec->autocfg.dig_in_pin)
2719 return;
Harald Welted949cac2008-09-09 15:56:01 +08002720
Takashi Iwai12daef62011-06-18 17:45:49 +02002721 dig_nid = codec->start_nid;
2722 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2723 unsigned int wcaps = get_wcaps(codec, dig_nid);
2724 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2725 continue;
2726 if (!(wcaps & AC_WCAP_DIGITAL))
2727 continue;
2728 if (!(wcaps & AC_WCAP_CONN_LIST))
2729 continue;
2730 err = get_connection_index(codec, dig_nid,
2731 spec->autocfg.dig_in_pin);
2732 if (err >= 0) {
2733 spec->dig_in_nid = dig_nid;
2734 break;
2735 }
2736 }
Harald Welted949cac2008-09-09 15:56:01 +08002737}
2738
Lydia Wang6369bcf2009-10-10 19:08:31 +08002739static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2740 int offset, int num_steps, int step_size)
2741{
2742 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2743 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2744 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2745 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2746 (0 << AC_AMPCAP_MUTE_SHIFT));
2747}
2748
Harald Welted949cac2008-09-09 15:56:01 +08002749static int patch_vt1708S(struct hda_codec *codec)
2750{
2751 struct via_spec *spec;
2752 int err;
2753
2754 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002755 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002756 if (spec == NULL)
2757 return -ENOMEM;
2758
Takashi Iwai620e2b22011-06-17 17:19:19 +02002759 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002760 override_mic_boost(codec, 0x1a, 0, 3, 40);
2761 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002762
Harald Welted949cac2008-09-09 15:56:01 +08002763 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002764 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002765 if (err < 0) {
2766 via_free(codec);
2767 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002768 }
2769
Takashi Iwai096a8852011-06-20 12:09:02 +02002770 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002771
Harald Welted949cac2008-09-09 15:56:01 +08002772 codec->patch_ops = via_patch_ops;
2773
Lydia Wang518bf3b2009-10-10 19:07:29 +08002774 /* correct names for VT1708BCE */
2775 if (get_codec_type(codec) == VT1708BCE) {
2776 kfree(codec->chip_name);
2777 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2778 snprintf(codec->bus->card->mixername,
2779 sizeof(codec->bus->card->mixername),
2780 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002781 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002782 /* correct names for VT1705 */
2783 if (codec->vendor_id == 0x11064397) {
2784 kfree(codec->chip_name);
2785 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2786 snprintf(codec->bus->card->mixername,
2787 sizeof(codec->bus->card->mixername),
2788 "%s %s", codec->vendor_name, codec->chip_name);
2789 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002790 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002791 return 0;
2792}
2793
2794/* Patch for VT1702 */
2795
Takashi Iwai096a8852011-06-20 12:09:02 +02002796static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002797 /* mixer enable */
2798 {0x1, 0xF88, 0x3},
2799 /* GPIO 0~2 */
2800 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002801 { }
2802};
2803
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002804static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2805{
2806 int imux_is_smixer =
2807 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2808 unsigned int parm;
2809 /* inputs */
2810 /* PW 1/2/5 (14h/15h/18h) */
2811 parm = AC_PWRST_D3;
2812 set_pin_power_state(codec, 0x14, &parm);
2813 set_pin_power_state(codec, 0x15, &parm);
2814 set_pin_power_state(codec, 0x18, &parm);
2815 if (imux_is_smixer)
2816 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2817 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2818 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2819 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2820 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2821 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2822
2823 /* outputs */
2824 /* PW 3/4 (16h/17h) */
2825 parm = AC_PWRST_D3;
2826 set_pin_power_state(codec, 0x17, &parm);
2827 set_pin_power_state(codec, 0x16, &parm);
2828 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2829 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2830 imux_is_smixer ? AC_PWRST_D0 : parm);
2831 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2832 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2833}
2834
Harald Welted949cac2008-09-09 15:56:01 +08002835static int patch_vt1702(struct hda_codec *codec)
2836{
2837 struct via_spec *spec;
2838 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002839
2840 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002841 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002842 if (spec == NULL)
2843 return -ENOMEM;
2844
Takashi Iwai620e2b22011-06-17 17:19:19 +02002845 spec->aa_mix_nid = 0x1a;
2846
Takashi Iwai12daef62011-06-18 17:45:49 +02002847 /* limit AA path volume to 0 dB */
2848 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2849 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2850 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2851 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2852 (1 << AC_AMPCAP_MUTE_SHIFT));
2853
Harald Welted949cac2008-09-09 15:56:01 +08002854 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002855 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002856 if (err < 0) {
2857 via_free(codec);
2858 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002859 }
2860
Takashi Iwai096a8852011-06-20 12:09:02 +02002861 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002862
Harald Welted949cac2008-09-09 15:56:01 +08002863 codec->patch_ops = via_patch_ops;
2864
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002865 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002866 return 0;
2867}
2868
Lydia Wangeb7188c2009-10-10 19:08:34 +08002869/* Patch for VT1718S */
2870
Takashi Iwai096a8852011-06-20 12:09:02 +02002871static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002872 /* Enable MW0 adjust Gain 5 */
2873 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002874 /* Enable Boost Volume backdoor */
2875 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002876
Lydia Wangeb7188c2009-10-10 19:08:34 +08002877 { }
2878};
2879
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002880static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2881{
2882 struct via_spec *spec = codec->spec;
2883 int imux_is_smixer;
2884 unsigned int parm;
2885 /* MUX6 (1eh) = stereo mixer */
2886 imux_is_smixer =
2887 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2888 /* inputs */
2889 /* PW 5/6/7 (29h/2ah/2bh) */
2890 parm = AC_PWRST_D3;
2891 set_pin_power_state(codec, 0x29, &parm);
2892 set_pin_power_state(codec, 0x2a, &parm);
2893 set_pin_power_state(codec, 0x2b, &parm);
2894 if (imux_is_smixer)
2895 parm = AC_PWRST_D0;
2896 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2897 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2898 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2899 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2900 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2901
2902 /* outputs */
2903 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2904 parm = AC_PWRST_D3;
2905 set_pin_power_state(codec, 0x27, &parm);
2906 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2907 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2908
2909 /* PW2 (26h), AOW2 (ah) */
2910 parm = AC_PWRST_D3;
2911 set_pin_power_state(codec, 0x26, &parm);
2912 if (spec->smart51_enabled)
2913 set_pin_power_state(codec, 0x2b, &parm);
2914 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2915
2916 /* PW0 (24h), AOW0 (8h) */
2917 parm = AC_PWRST_D3;
2918 set_pin_power_state(codec, 0x24, &parm);
2919 if (!spec->hp_independent_mode) /* check for redirected HP */
2920 set_pin_power_state(codec, 0x28, &parm);
2921 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2922 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2923 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2924 imux_is_smixer ? AC_PWRST_D0 : parm);
2925
2926 /* PW1 (25h), AOW1 (9h) */
2927 parm = AC_PWRST_D3;
2928 set_pin_power_state(codec, 0x25, &parm);
2929 if (spec->smart51_enabled)
2930 set_pin_power_state(codec, 0x2a, &parm);
2931 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2932
2933 if (spec->hp_independent_mode) {
2934 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2935 parm = AC_PWRST_D3;
2936 set_pin_power_state(codec, 0x28, &parm);
2937 snd_hda_codec_write(codec, 0x1b, 0,
2938 AC_VERB_SET_POWER_STATE, parm);
2939 snd_hda_codec_write(codec, 0x34, 0,
2940 AC_VERB_SET_POWER_STATE, parm);
2941 snd_hda_codec_write(codec, 0xc, 0,
2942 AC_VERB_SET_POWER_STATE, parm);
2943 }
2944}
2945
Lydia Wangeb7188c2009-10-10 19:08:34 +08002946static int patch_vt1718S(struct hda_codec *codec)
2947{
2948 struct via_spec *spec;
2949 int err;
2950
2951 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002952 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002953 if (spec == NULL)
2954 return -ENOMEM;
2955
Takashi Iwai620e2b22011-06-17 17:19:19 +02002956 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002957 override_mic_boost(codec, 0x2b, 0, 3, 40);
2958 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002959
Lydia Wangeb7188c2009-10-10 19:08:34 +08002960 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002961 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002962 if (err < 0) {
2963 via_free(codec);
2964 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002965 }
2966
Takashi Iwai096a8852011-06-20 12:09:02 +02002967 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002968
Lydia Wangeb7188c2009-10-10 19:08:34 +08002969 codec->patch_ops = via_patch_ops;
2970
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002971 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2972
Lydia Wangeb7188c2009-10-10 19:08:34 +08002973 return 0;
2974}
Lydia Wangf3db4232009-10-10 19:08:41 +08002975
2976/* Patch for VT1716S */
2977
2978static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2979 struct snd_ctl_elem_info *uinfo)
2980{
2981 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2982 uinfo->count = 1;
2983 uinfo->value.integer.min = 0;
2984 uinfo->value.integer.max = 1;
2985 return 0;
2986}
2987
2988static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2989 struct snd_ctl_elem_value *ucontrol)
2990{
2991 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2992 int index = 0;
2993
2994 index = snd_hda_codec_read(codec, 0x26, 0,
2995 AC_VERB_GET_CONNECT_SEL, 0);
2996 if (index != -1)
2997 *ucontrol->value.integer.value = index;
2998
2999 return 0;
3000}
3001
3002static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3003 struct snd_ctl_elem_value *ucontrol)
3004{
3005 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3006 struct via_spec *spec = codec->spec;
3007 int index = *ucontrol->value.integer.value;
3008
3009 snd_hda_codec_write(codec, 0x26, 0,
3010 AC_VERB_SET_CONNECT_SEL, index);
3011 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003012 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003013 return 1;
3014}
3015
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003016static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003017 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3018 {
3019 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3020 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003021 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003022 .count = 1,
3023 .info = vt1716s_dmic_info,
3024 .get = vt1716s_dmic_get,
3025 .put = vt1716s_dmic_put,
3026 },
3027 {} /* end */
3028};
3029
3030
3031/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003032static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003033 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3034 { } /* end */
3035};
3036
Takashi Iwai096a8852011-06-20 12:09:02 +02003037static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003038 /* Enable Boost Volume backdoor */
3039 {0x1, 0xf8a, 0x80},
3040 /* don't bybass mixer */
3041 {0x1, 0xf88, 0xc0},
3042 /* Enable mono output */
3043 {0x1, 0xf90, 0x08},
3044 { }
3045};
3046
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003047static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3048{
3049 struct via_spec *spec = codec->spec;
3050 int imux_is_smixer;
3051 unsigned int parm;
3052 unsigned int mono_out, present;
3053 /* SW0 (17h) = stereo mixer */
3054 imux_is_smixer =
3055 (snd_hda_codec_read(codec, 0x17, 0,
3056 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3057 /* inputs */
3058 /* PW 1/2/5 (1ah/1bh/1eh) */
3059 parm = AC_PWRST_D3;
3060 set_pin_power_state(codec, 0x1a, &parm);
3061 set_pin_power_state(codec, 0x1b, &parm);
3062 set_pin_power_state(codec, 0x1e, &parm);
3063 if (imux_is_smixer)
3064 parm = AC_PWRST_D0;
3065 /* SW0 (17h), AIW0(13h) */
3066 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3067 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3068
3069 parm = AC_PWRST_D3;
3070 set_pin_power_state(codec, 0x1e, &parm);
3071 /* PW11 (22h) */
3072 if (spec->dmic_enabled)
3073 set_pin_power_state(codec, 0x22, &parm);
3074 else
3075 snd_hda_codec_write(codec, 0x22, 0,
3076 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3077
3078 /* SW2(26h), AIW1(14h) */
3079 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3080 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3081
3082 /* outputs */
3083 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3084 parm = AC_PWRST_D3;
3085 set_pin_power_state(codec, 0x19, &parm);
3086 /* Smart 5.1 PW2(1bh) */
3087 if (spec->smart51_enabled)
3088 set_pin_power_state(codec, 0x1b, &parm);
3089 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3090 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3091
3092 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3093 parm = AC_PWRST_D3;
3094 set_pin_power_state(codec, 0x23, &parm);
3095 /* Smart 5.1 PW1(1ah) */
3096 if (spec->smart51_enabled)
3097 set_pin_power_state(codec, 0x1a, &parm);
3098 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3099
3100 /* Smart 5.1 PW5(1eh) */
3101 if (spec->smart51_enabled)
3102 set_pin_power_state(codec, 0x1e, &parm);
3103 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3104
3105 /* Mono out */
3106 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3107 present = snd_hda_jack_detect(codec, 0x1c);
3108
3109 if (present)
3110 mono_out = 0;
3111 else {
3112 present = snd_hda_jack_detect(codec, 0x1d);
3113 if (!spec->hp_independent_mode && present)
3114 mono_out = 0;
3115 else
3116 mono_out = 1;
3117 }
3118 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3119 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3120 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3121 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3122
3123 /* PW 3/4 (1ch/1dh) */
3124 parm = AC_PWRST_D3;
3125 set_pin_power_state(codec, 0x1c, &parm);
3126 set_pin_power_state(codec, 0x1d, &parm);
3127 /* HP Independent Mode, power on AOW3 */
3128 if (spec->hp_independent_mode)
3129 snd_hda_codec_write(codec, 0x25, 0,
3130 AC_VERB_SET_POWER_STATE, parm);
3131
3132 /* force to D0 for internal Speaker */
3133 /* MW0 (16h), AOW0 (10h) */
3134 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3135 imux_is_smixer ? AC_PWRST_D0 : parm);
3136 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3137 mono_out ? AC_PWRST_D0 : parm);
3138}
3139
Lydia Wangf3db4232009-10-10 19:08:41 +08003140static int patch_vt1716S(struct hda_codec *codec)
3141{
3142 struct via_spec *spec;
3143 int err;
3144
3145 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003146 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003147 if (spec == NULL)
3148 return -ENOMEM;
3149
Takashi Iwai620e2b22011-06-17 17:19:19 +02003150 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003151 override_mic_boost(codec, 0x1a, 0, 3, 40);
3152 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003153
Lydia Wangf3db4232009-10-10 19:08:41 +08003154 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003155 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003156 if (err < 0) {
3157 via_free(codec);
3158 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003159 }
3160
Takashi Iwai096a8852011-06-20 12:09:02 +02003161 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003162
Lydia Wangf3db4232009-10-10 19:08:41 +08003163 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3164 spec->num_mixers++;
3165
3166 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3167
3168 codec->patch_ops = via_patch_ops;
3169
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003170 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003171 return 0;
3172}
Lydia Wang25eaba22009-10-10 19:08:43 +08003173
3174/* for vt2002P */
3175
Takashi Iwai096a8852011-06-20 12:09:02 +02003176static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003177 /* Class-D speaker related verbs */
3178 {0x1, 0xfe0, 0x4},
3179 {0x1, 0xfe9, 0x80},
3180 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003181 /* Enable Boost Volume backdoor */
3182 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003183 /* Enable AOW0 to MW9 */
3184 {0x1, 0xfb8, 0x88},
3185 { }
3186};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003187
Takashi Iwai096a8852011-06-20 12:09:02 +02003188static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003189 /* Enable Boost Volume backdoor */
3190 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003191 /* Enable AOW0 to MW9 */
3192 {0x1, 0xfb8, 0x88},
3193 { }
3194};
Lydia Wang25eaba22009-10-10 19:08:43 +08003195
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003196static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3197{
3198 struct via_spec *spec = codec->spec;
3199 int imux_is_smixer;
3200 unsigned int parm;
3201 unsigned int present;
3202 /* MUX9 (1eh) = stereo mixer */
3203 imux_is_smixer =
3204 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3205 /* inputs */
3206 /* PW 5/6/7 (29h/2ah/2bh) */
3207 parm = AC_PWRST_D3;
3208 set_pin_power_state(codec, 0x29, &parm);
3209 set_pin_power_state(codec, 0x2a, &parm);
3210 set_pin_power_state(codec, 0x2b, &parm);
3211 parm = AC_PWRST_D0;
3212 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3213 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3214 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3215 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3216 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3217
3218 /* outputs */
3219 /* AOW0 (8h)*/
3220 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3221
Lydia Wang118909562011-03-23 17:57:34 +08003222 if (spec->codec_type == VT1802) {
3223 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3224 parm = AC_PWRST_D3;
3225 set_pin_power_state(codec, 0x28, &parm);
3226 snd_hda_codec_write(codec, 0x18, 0,
3227 AC_VERB_SET_POWER_STATE, parm);
3228 snd_hda_codec_write(codec, 0x38, 0,
3229 AC_VERB_SET_POWER_STATE, parm);
3230 } else {
3231 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3232 parm = AC_PWRST_D3;
3233 set_pin_power_state(codec, 0x26, &parm);
3234 snd_hda_codec_write(codec, 0x1c, 0,
3235 AC_VERB_SET_POWER_STATE, parm);
3236 snd_hda_codec_write(codec, 0x37, 0,
3237 AC_VERB_SET_POWER_STATE, parm);
3238 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003239
Lydia Wang118909562011-03-23 17:57:34 +08003240 if (spec->codec_type == VT1802) {
3241 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3242 parm = AC_PWRST_D3;
3243 set_pin_power_state(codec, 0x25, &parm);
3244 snd_hda_codec_write(codec, 0x15, 0,
3245 AC_VERB_SET_POWER_STATE, parm);
3246 snd_hda_codec_write(codec, 0x35, 0,
3247 AC_VERB_SET_POWER_STATE, parm);
3248 } else {
3249 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3250 parm = AC_PWRST_D3;
3251 set_pin_power_state(codec, 0x25, &parm);
3252 snd_hda_codec_write(codec, 0x19, 0,
3253 AC_VERB_SET_POWER_STATE, parm);
3254 snd_hda_codec_write(codec, 0x35, 0,
3255 AC_VERB_SET_POWER_STATE, parm);
3256 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003257
3258 if (spec->hp_independent_mode)
3259 snd_hda_codec_write(codec, 0x9, 0,
3260 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3261
3262 /* Class-D */
3263 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3264 present = snd_hda_jack_detect(codec, 0x25);
3265
3266 parm = AC_PWRST_D3;
3267 set_pin_power_state(codec, 0x24, &parm);
3268 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003269 if (spec->codec_type == VT1802)
3270 snd_hda_codec_write(codec, 0x14, 0,
3271 AC_VERB_SET_POWER_STATE, parm);
3272 else
3273 snd_hda_codec_write(codec, 0x18, 0,
3274 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003275 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3276
3277 /* Mono Out */
3278 present = snd_hda_jack_detect(codec, 0x26);
3279
3280 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003281 if (spec->codec_type == VT1802) {
3282 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3283 snd_hda_codec_write(codec, 0x33, 0,
3284 AC_VERB_SET_POWER_STATE, parm);
3285 snd_hda_codec_write(codec, 0x1c, 0,
3286 AC_VERB_SET_POWER_STATE, parm);
3287 snd_hda_codec_write(codec, 0x3c, 0,
3288 AC_VERB_SET_POWER_STATE, parm);
3289 } else {
3290 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3291 snd_hda_codec_write(codec, 0x31, 0,
3292 AC_VERB_SET_POWER_STATE, parm);
3293 snd_hda_codec_write(codec, 0x17, 0,
3294 AC_VERB_SET_POWER_STATE, parm);
3295 snd_hda_codec_write(codec, 0x3b, 0,
3296 AC_VERB_SET_POWER_STATE, parm);
3297 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003298 /* MW9 (21h) */
3299 if (imux_is_smixer || !is_aa_path_mute(codec))
3300 snd_hda_codec_write(codec, 0x21, 0,
3301 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3302 else
3303 snd_hda_codec_write(codec, 0x21, 0,
3304 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3305}
Lydia Wang25eaba22009-10-10 19:08:43 +08003306
3307/* patch for vt2002P */
3308static int patch_vt2002P(struct hda_codec *codec)
3309{
3310 struct via_spec *spec;
3311 int err;
3312
3313 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003314 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003315 if (spec == NULL)
3316 return -ENOMEM;
3317
Takashi Iwai620e2b22011-06-17 17:19:19 +02003318 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003319 override_mic_boost(codec, 0x2b, 0, 3, 40);
3320 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003321
Lydia Wang25eaba22009-10-10 19:08:43 +08003322 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003323 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003324 if (err < 0) {
3325 via_free(codec);
3326 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003327 }
3328
Lydia Wang118909562011-03-23 17:57:34 +08003329 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003330 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003331 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003332 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003333
Lydia Wang25eaba22009-10-10 19:08:43 +08003334 codec->patch_ops = via_patch_ops;
3335
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003336 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003337 return 0;
3338}
Lydia Wangab6734e2009-10-10 19:08:46 +08003339
3340/* for vt1812 */
3341
Takashi Iwai096a8852011-06-20 12:09:02 +02003342static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003343 /* Enable Boost Volume backdoor */
3344 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003345 /* Enable AOW0 to MW9 */
3346 {0x1, 0xfb8, 0xa8},
3347 { }
3348};
3349
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003350static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3351{
3352 struct via_spec *spec = codec->spec;
3353 int imux_is_smixer =
3354 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3355 unsigned int parm;
3356 unsigned int present;
3357 /* MUX10 (1eh) = stereo mixer */
3358 imux_is_smixer =
3359 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3360 /* inputs */
3361 /* PW 5/6/7 (29h/2ah/2bh) */
3362 parm = AC_PWRST_D3;
3363 set_pin_power_state(codec, 0x29, &parm);
3364 set_pin_power_state(codec, 0x2a, &parm);
3365 set_pin_power_state(codec, 0x2b, &parm);
3366 parm = AC_PWRST_D0;
3367 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3368 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3369 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3370 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3371 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3372
3373 /* outputs */
3374 /* AOW0 (8h)*/
3375 snd_hda_codec_write(codec, 0x8, 0,
3376 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3377
3378 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3379 parm = AC_PWRST_D3;
3380 set_pin_power_state(codec, 0x28, &parm);
3381 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3382 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3383
3384 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3385 parm = AC_PWRST_D3;
3386 set_pin_power_state(codec, 0x25, &parm);
3387 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3388 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3389 if (spec->hp_independent_mode)
3390 snd_hda_codec_write(codec, 0x9, 0,
3391 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3392
3393 /* Internal Speaker */
3394 /* PW0 (24h), MW0(14h), MUX0(34h) */
3395 present = snd_hda_jack_detect(codec, 0x25);
3396
3397 parm = AC_PWRST_D3;
3398 set_pin_power_state(codec, 0x24, &parm);
3399 if (present) {
3400 snd_hda_codec_write(codec, 0x14, 0,
3401 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3402 snd_hda_codec_write(codec, 0x34, 0,
3403 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3404 } else {
3405 snd_hda_codec_write(codec, 0x14, 0,
3406 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3407 snd_hda_codec_write(codec, 0x34, 0,
3408 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3409 }
3410
3411
3412 /* Mono Out */
3413 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3414 present = snd_hda_jack_detect(codec, 0x28);
3415
3416 parm = AC_PWRST_D3;
3417 set_pin_power_state(codec, 0x31, &parm);
3418 if (present) {
3419 snd_hda_codec_write(codec, 0x1c, 0,
3420 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3421 snd_hda_codec_write(codec, 0x3c, 0,
3422 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3423 snd_hda_codec_write(codec, 0x3e, 0,
3424 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3425 } else {
3426 snd_hda_codec_write(codec, 0x1c, 0,
3427 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3428 snd_hda_codec_write(codec, 0x3c, 0,
3429 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3430 snd_hda_codec_write(codec, 0x3e, 0,
3431 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3432 }
3433
3434 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3435 parm = AC_PWRST_D3;
3436 set_pin_power_state(codec, 0x33, &parm);
3437 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3438 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3439
3440}
Lydia Wangab6734e2009-10-10 19:08:46 +08003441
3442/* patch for vt1812 */
3443static int patch_vt1812(struct hda_codec *codec)
3444{
3445 struct via_spec *spec;
3446 int err;
3447
3448 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003449 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003450 if (spec == NULL)
3451 return -ENOMEM;
3452
Takashi Iwai620e2b22011-06-17 17:19:19 +02003453 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003454 override_mic_boost(codec, 0x2b, 0, 3, 40);
3455 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003456
Lydia Wangab6734e2009-10-10 19:08:46 +08003457 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003458 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003459 if (err < 0) {
3460 via_free(codec);
3461 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003462 }
3463
Takashi Iwai096a8852011-06-20 12:09:02 +02003464 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003465
Lydia Wangab6734e2009-10-10 19:08:46 +08003466 codec->patch_ops = via_patch_ops;
3467
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003468 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003469 return 0;
3470}
3471
Joseph Chanc577b8a2006-11-29 15:29:40 +01003472/*
3473 * patch entries
3474 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003475static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003476 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3477 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3478 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3479 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3480 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003481 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003482 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003483 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003484 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003485 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003486 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003487 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003488 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003489 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003490 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003491 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003492 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003493 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003494 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003495 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003496 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003497 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003498 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003499 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003500 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003501 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003502 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003503 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003504 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003505 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003506 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003507 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003508 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003509 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003510 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003511 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003512 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003513 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003514 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003515 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003516 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003517 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003518 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003519 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003520 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003521 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003522 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003523 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003524 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003525 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003526 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003527 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003528 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003529 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003530 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003531 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003532 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003533 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003534 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003535 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003536 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003537 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003538 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003539 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003540 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003541 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003542 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003543 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003544 { .id = 0x11060428, .name = "VT1718S",
3545 .patch = patch_vt1718S},
3546 { .id = 0x11064428, .name = "VT1718S",
3547 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003548 { .id = 0x11060441, .name = "VT2020",
3549 .patch = patch_vt1718S},
3550 { .id = 0x11064441, .name = "VT1828S",
3551 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003552 { .id = 0x11060433, .name = "VT1716S",
3553 .patch = patch_vt1716S},
3554 { .id = 0x1106a721, .name = "VT1716S",
3555 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003556 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3557 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003558 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003559 { .id = 0x11060440, .name = "VT1818S",
3560 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003561 { .id = 0x11060446, .name = "VT1802",
3562 .patch = patch_vt2002P},
3563 { .id = 0x11068446, .name = "VT1802",
3564 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003565 {} /* terminator */
3566};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003567
3568MODULE_ALIAS("snd-hda-codec-id:1106*");
3569
3570static struct hda_codec_preset_list via_list = {
3571 .preset = snd_hda_preset_via,
3572 .owner = THIS_MODULE,
3573};
3574
3575MODULE_LICENSE("GPL");
3576MODULE_DESCRIPTION("VIA HD-audio codec");
3577
3578static int __init patch_via_init(void)
3579{
3580 return snd_hda_add_codec_preset(&via_list);
3581}
3582
3583static void __exit patch_via_exit(void)
3584{
3585 snd_hda_delete_codec_preset(&via_list);
3586}
3587
3588module_init(patch_via_init)
3589module_exit(patch_via_exit)