blob: 5232abc341f8131735e42eea4b6f540981ddcc13 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200110#define VIA_MAX_ADCS 3
111
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800112struct via_spec {
113 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200114 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800115 unsigned int num_mixers;
116
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200117 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118 unsigned int num_iverbs;
119
Takashi Iwai82673bc2011-06-17 16:24:21 +0200120 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200121 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200122 const struct hda_pcm_stream *stream_analog_playback;
123 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124
Takashi Iwai82673bc2011-06-17 16:24:21 +0200125 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200126 const struct hda_pcm_stream *stream_digital_playback;
127 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800128
129 /* playback */
130 struct hda_multi_out multiout;
131 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200132 hda_nid_t hp_dac_nid;
Takashi Iwai25250502011-06-30 17:24:47 +0200133 bool hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200134 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200136 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai4a796162011-06-17 17:53:38 +0200137 struct nid_path hp_path;
138 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200139 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200140
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800141 /* capture */
142 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200143 hda_nid_t adc_nids[VIA_MAX_ADCS];
144 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200145 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800146 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800147
148 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200149 bool dyn_adc_switch;
150 int num_inputs;
151 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200152 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800153
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200154 /* dynamic ADC switching */
155 hda_nid_t cur_adc;
156 unsigned int cur_adc_stream_tag;
157 unsigned int cur_adc_format;
158
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800159 /* PCM information */
160 struct hda_pcm pcm_rec[3];
161
162 /* dynamic controls, init_verbs and input_mux */
163 struct auto_pin_cfg autocfg;
164 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800165 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
166
167 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800169 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200170 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800171 enum VIA_HDA_CODEC codec_type;
172
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200173 /* smart51 setup */
174 unsigned int smart51_nums;
175 hda_nid_t smart51_pins[2];
176 int smart51_idxs[2];
177 const char *smart51_labels[2];
178 unsigned int smart51_enabled;
179
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800180 /* work to check hp jack state */
181 struct hda_codec *codec;
182 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200183 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800184 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800185
186 void (*set_widgets_power_state)(struct hda_codec *codec);
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200189 int num_loopbacks;
190 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200191
192 /* bind capture-volume */
193 struct hda_bind_ctls *bind_cap_vol;
194 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800195};
196
Lydia Wang0341ccd2011-03-22 16:25:03 +0800197static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100198static struct via_spec * via_new_spec(struct hda_codec *codec)
199{
200 struct via_spec *spec;
201
202 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
203 if (spec == NULL)
204 return NULL;
205
206 codec->spec = spec;
207 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800208 spec->codec_type = get_codec_type(codec);
209 /* VT1708BCE & VT1708S are almost same */
210 if (spec->codec_type == VT1708BCE)
211 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100212 return spec;
213}
214
Lydia Wang744ff5f2009-10-10 19:07:26 +0800215static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800216{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800217 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800218 u16 ven_id = vendor_id >> 16;
219 u16 dev_id = vendor_id & 0xffff;
220 enum VIA_HDA_CODEC codec_type;
221
222 /* get codec type */
223 if (ven_id != 0x1106)
224 codec_type = UNKNOWN;
225 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
226 codec_type = VT1708;
227 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
228 codec_type = VT1709_10CH;
229 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
230 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800231 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800232 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800233 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
234 codec_type = VT1708BCE;
235 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800236 codec_type = VT1708B_4CH;
237 else if ((dev_id & 0xfff) == 0x397
238 && (dev_id >> 12) < 8)
239 codec_type = VT1708S;
240 else if ((dev_id & 0xfff) == 0x398
241 && (dev_id >> 12) < 8)
242 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800243 else if ((dev_id & 0xfff) == 0x428
244 && (dev_id >> 12) < 8)
245 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800246 else if (dev_id == 0x0433 || dev_id == 0xa721)
247 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800248 else if (dev_id == 0x0441 || dev_id == 0x4441)
249 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800250 else if (dev_id == 0x0438 || dev_id == 0x4438)
251 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800252 else if (dev_id == 0x0448)
253 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800254 else if (dev_id == 0x0440)
255 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800256 else if ((dev_id & 0xfff) == 0x446)
257 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800258 else
259 codec_type = UNKNOWN;
260 return codec_type;
261};
262
Lydia Wangec7e7e42011-03-24 12:43:44 +0800263#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800264#define VIA_HP_EVENT 0x01
265#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200266#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800267
Joseph Chanc577b8a2006-11-29 15:29:40 +0100268enum {
269 VIA_CTL_WIDGET_VOL,
270 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800271 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100272};
273
Takashi Iwaiada509e2011-06-20 15:40:19 +0200274static void analog_low_current_mode(struct hda_codec *codec);
275static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800276
277static void vt1708_start_hp_work(struct via_spec *spec)
278{
279 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
280 return;
281 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200282 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800283 if (!delayed_work_pending(&spec->vt1708_hp_work))
284 schedule_delayed_work(&spec->vt1708_hp_work,
285 msecs_to_jiffies(100));
286}
287
288static void vt1708_stop_hp_work(struct via_spec *spec)
289{
290 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
291 return;
292 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
293 && !is_aa_path_mute(spec->codec))
294 return;
295 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200296 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100297 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800298}
Lydia Wangf5271102009-10-10 19:07:35 +0800299
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800300static void set_widgets_power_state(struct hda_codec *codec)
301{
302 struct via_spec *spec = codec->spec;
303 if (spec->set_widgets_power_state)
304 spec->set_widgets_power_state(codec);
305}
Lydia Wang25eaba22009-10-10 19:08:43 +0800306
Lydia Wangf5271102009-10-10 19:07:35 +0800307static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
308 struct snd_ctl_elem_value *ucontrol)
309{
310 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
311 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
312
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800313 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200314 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800315 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
316 if (is_aa_path_mute(codec))
317 vt1708_start_hp_work(codec->spec);
318 else
319 vt1708_stop_hp_work(codec->spec);
320 }
Lydia Wangf5271102009-10-10 19:07:35 +0800321 return change;
322}
323
324/* modify .put = snd_hda_mixer_amp_switch_put */
325#define ANALOG_INPUT_MUTE \
326 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
327 .name = NULL, \
328 .index = 0, \
329 .info = snd_hda_mixer_amp_switch_info, \
330 .get = snd_hda_mixer_amp_switch_get, \
331 .put = analog_input_switch_put, \
332 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
333
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200334static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100335 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
336 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800337 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100338};
339
Lydia Wangab6734e2009-10-10 19:08:46 +0800340
Joseph Chanc577b8a2006-11-29 15:29:40 +0100341/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200342static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
343 const struct snd_kcontrol_new *tmpl,
344 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100345{
346 struct snd_kcontrol_new *knew;
347
Takashi Iwai603c4012008-07-30 15:01:44 +0200348 snd_array_init(&spec->kctls, sizeof(*knew), 32);
349 knew = snd_array_new(&spec->kctls);
350 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200351 return NULL;
352 *knew = *tmpl;
353 if (!name)
354 name = tmpl->name;
355 if (name) {
356 knew->name = kstrdup(name, GFP_KERNEL);
357 if (!knew->name)
358 return NULL;
359 }
360 return knew;
361}
362
363static int __via_add_control(struct via_spec *spec, int type, const char *name,
364 int idx, unsigned long val)
365{
366 struct snd_kcontrol_new *knew;
367
368 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
369 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100370 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200371 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100372 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100373 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100375 return 0;
376}
377
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200378#define via_add_control(spec, type, name, val) \
379 __via_add_control(spec, type, name, 0, val)
380
Takashi Iwai291c9e32011-06-17 16:15:26 +0200381#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100382
Takashi Iwai603c4012008-07-30 15:01:44 +0200383static void via_free_kctls(struct hda_codec *codec)
384{
385 struct via_spec *spec = codec->spec;
386
387 if (spec->kctls.list) {
388 struct snd_kcontrol_new *kctl = spec->kctls.list;
389 int i;
390 for (i = 0; i < spec->kctls.used; i++)
391 kfree(kctl[i].name);
392 }
393 snd_array_free(&spec->kctls);
394}
395
Joseph Chanc577b8a2006-11-29 15:29:40 +0100396/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800397static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200398 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100399{
400 char name[32];
401 int err;
402
403 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200404 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100405 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
406 if (err < 0)
407 return err;
408 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200409 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100410 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
411 if (err < 0)
412 return err;
413 return 0;
414}
415
Takashi Iwai5d417622011-06-20 11:32:27 +0200416#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200417 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200418
Takashi Iwai8df2a312011-06-21 11:48:29 +0200419static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
420 unsigned int mask)
421{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200422 unsigned int caps;
423 if (!nid)
424 return false;
425 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200426 if (dir == HDA_INPUT)
427 caps &= AC_WCAP_IN_AMP;
428 else
429 caps &= AC_WCAP_OUT_AMP;
430 if (!caps)
431 return false;
432 if (query_amp_caps(codec, nid, dir) & mask)
433 return true;
434 return false;
435}
436
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200437#define have_mute(codec, nid, dir) \
438 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200439
Lydia Wangd69607b2011-07-08 14:02:52 +0800440static bool is_node_in_path(struct nid_path *path, hda_nid_t nid)
441{
442 int i;
443 if (!nid)
444 return false;
445 for (i = 0; i < path->depth; i++) {
446 if (path->path[i] == nid)
447 return true;
448 }
449 return false;
450}
451
452/* enable/disable the output-route mixers */
453static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
454 hda_nid_t mix_nid, int aa_mix_idx, bool enable)
455{
456 int i, num, val;
457 bool hp_path, front_path;
458 struct via_spec *spec = codec->spec;
459
460 if (!path)
461 return;
462 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
463 hp_path = is_node_in_path(path, spec->hp_dac_nid);
464 front_path = is_node_in_path(path, spec->multiout.dac_nids[0]);
465
466 for (i = 0; i < num; i++) {
467 if (i == aa_mix_idx) {
468 if (hp_path)
469 val = enable ? AMP_IN_MUTE(i) :
470 AMP_IN_UNMUTE(i);
471 else if (front_path)
472 val = AMP_IN_UNMUTE(i);
473 else
474 val = AMP_IN_MUTE(i);
475 } else {
476 if (hp_path)
477 val = enable ? AMP_IN_UNMUTE(i) :
478 AMP_IN_MUTE(i);
479 else if (front_path)
480 val = AMP_IN_MUTE(i);
481 else
482 val = AMP_IN_UNMUTE(i);
483 }
484 snd_hda_codec_write(codec, mix_nid, 0,
485 AC_VERB_SET_AMP_GAIN_MUTE, val);
486 }
487}
488
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200489/* enable/disable the output-route */
490static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
491 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200492{
Lydia Wangd69607b2011-07-08 14:02:52 +0800493 int i, val;
494 struct via_spec *spec = codec->spec;
495 hda_nid_t aa_mix_nid = spec->aa_mix_nid;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200496 for (i = 0; i < path->depth; i++) {
497 hda_nid_t src, dst;
498 int idx = path->idx[i];
499 src = path->path[i];
500 if (i < path->depth - 1)
501 dst = path->path[i + 1];
502 else
503 dst = 0;
504 if (enable && path->multi[i])
505 snd_hda_codec_write(codec, dst, 0,
506 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wangb89596a2011-07-04 17:01:33 +0800507 if (!force
508 && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
509 && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
Lydia Wange5e14682011-07-01 10:55:07 +0800510 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200511 if (have_mute(codec, dst, HDA_INPUT)) {
Lydia Wangd69607b2011-07-08 14:02:52 +0800512 if (dst == aa_mix_nid) {
513 val = enable ? AMP_IN_UNMUTE(idx) :
514 AMP_IN_MUTE(idx);
515 snd_hda_codec_write(codec, dst, 0,
516 AC_VERB_SET_AMP_GAIN_MUTE, val);
517 } else {
518 idx = get_connection_index(codec, dst,
519 aa_mix_nid);
520 if (idx >= 0) {
521 activate_output_mix(codec, path,
522 dst, idx, enable);
523 }
524 }
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200525 }
526 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
527 continue;
528 if (have_mute(codec, src, HDA_OUTPUT)) {
529 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
530 snd_hda_codec_write(codec, src, 0,
531 AC_VERB_SET_AMP_GAIN_MUTE, val);
532 }
533 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200534}
535
536/* set the given pin as output */
537static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
538 int pin_type)
539{
540 if (!pin)
541 return;
542 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
543 pin_type);
544 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
545 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200546 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100547}
548
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200549static void via_auto_init_output(struct hda_codec *codec,
550 struct nid_path *path, int pin_type,
Takashi Iwaibac4b922011-07-04 17:35:51 +0200551 bool with_aa_mix, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200552{
553 struct via_spec *spec = codec->spec;
554 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800555 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200556
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200557 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200558 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200559 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200560
561 init_output_pin(codec, pin, pin_type);
562 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
563 if (caps & AC_AMPCAP_MUTE) {
564 unsigned int val;
565 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
566 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
567 AMP_OUT_MUTE | val);
568 }
569
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200570 /* initialize the AA-path */
571 if (!spec->aa_mix_nid)
572 return;
Lydia Wangd69607b2011-07-08 14:02:52 +0800573 activate_output_path(codec, path, true, force);
Takashi Iwai5d417622011-06-20 11:32:27 +0200574}
575
Joseph Chanc577b8a2006-11-29 15:29:40 +0100576static void via_auto_init_multi_out(struct hda_codec *codec)
577{
578 struct via_spec *spec = codec->spec;
579 int i;
580
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200581 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200582 /* enable aa-mute only for the front channel */
583 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT,
584 i == 0, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585}
586
587static void via_auto_init_hp_out(struct hda_codec *codec)
588{
589 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100590
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200591 if (!spec->hp_dac_nid) {
Takashi Iwaibac4b922011-07-04 17:35:51 +0200592 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
593 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200594 return;
595 }
596 if (spec->hp_independent_mode) {
597 activate_output_path(codec, &spec->hp_dep_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200598 via_auto_init_output(codec, &spec->hp_path, PIN_HP,
599 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200600 } else {
601 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200602 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
603 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200604 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100605}
606
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200607static void via_auto_init_speaker_out(struct hda_codec *codec)
608{
609 struct via_spec *spec = codec->spec;
610
611 if (spec->autocfg.speaker_outs)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200612 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
613 true, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200614}
615
Takashi Iwaif4a78282011-06-17 18:46:48 +0200616static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200617static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200618
Joseph Chanc577b8a2006-11-29 15:29:40 +0100619static void via_auto_init_analog_input(struct hda_codec *codec)
620{
621 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200622 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200623 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200624 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200625 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100626
Takashi Iwai096a8852011-06-20 12:09:02 +0200627 /* init ADCs */
628 for (i = 0; i < spec->num_adc_nids; i++) {
629 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
630 AC_VERB_SET_AMP_GAIN_MUTE,
631 AMP_IN_UNMUTE(0));
632 }
633
634 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200635 for (i = 0; i < cfg->num_inputs; i++) {
636 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200637 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200638 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100639 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200640 ctl = PIN_VREF50;
641 else
642 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100643 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200644 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100645 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200646
647 /* init input-src */
648 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200649 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
650 if (spec->mux_nids[adc_idx]) {
651 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
652 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
653 AC_VERB_SET_CONNECT_SEL,
654 mux_idx);
655 }
656 if (spec->dyn_adc_switch)
657 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200658 }
659
660 /* init aa-mixer */
661 if (!spec->aa_mix_nid)
662 return;
663 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
664 ARRAY_SIZE(conn));
665 for (i = 0; i < num_conns; i++) {
666 unsigned int caps = get_wcaps(codec, conn[i]);
667 if (get_wcaps_type(caps) == AC_WID_PIN)
668 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
669 AC_VERB_SET_AMP_GAIN_MUTE,
670 AMP_IN_MUTE(i));
671 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100672}
Lydia Wangf5271102009-10-10 19:07:35 +0800673
674static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
675 unsigned int *affected_parm)
676{
677 unsigned parm;
678 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
679 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
680 >> AC_DEFCFG_MISC_SHIFT
681 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800682 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200683 unsigned present = 0;
684
685 no_presence |= spec->no_pin_power_ctl;
686 if (!no_presence)
687 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200688 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800689 || ((no_presence || present)
690 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800691 *affected_parm = AC_PWRST_D0; /* if it's connected */
692 parm = AC_PWRST_D0;
693 } else
694 parm = AC_PWRST_D3;
695
696 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
697}
698
Takashi Iwai24088a52011-06-17 16:59:21 +0200699static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
700 struct snd_ctl_elem_info *uinfo)
701{
702 static const char * const texts[] = {
703 "Disabled", "Enabled"
704 };
705
706 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
707 uinfo->count = 1;
708 uinfo->value.enumerated.items = 2;
709 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
710 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
711 strcpy(uinfo->value.enumerated.name,
712 texts[uinfo->value.enumerated.item]);
713 return 0;
714}
715
716static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
717 struct snd_ctl_elem_value *ucontrol)
718{
719 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
720 struct via_spec *spec = codec->spec;
721 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
722 return 0;
723}
724
725static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
726 struct snd_ctl_elem_value *ucontrol)
727{
728 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
729 struct via_spec *spec = codec->spec;
730 unsigned int val = !ucontrol->value.enumerated.item[0];
731
732 if (val == spec->no_pin_power_ctl)
733 return 0;
734 spec->no_pin_power_ctl = val;
735 set_widgets_power_state(codec);
736 return 1;
737}
738
739static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
740 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
741 .name = "Dynamic Power-Control",
742 .info = via_pin_power_ctl_info,
743 .get = via_pin_power_ctl_get,
744 .put = via_pin_power_ctl_put,
745};
746
747
Harald Welte0aa62ae2008-09-09 15:58:27 +0800748static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
749 struct snd_ctl_elem_info *uinfo)
750{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200751 static const char * const texts[] = { "OFF", "ON" };
752
753 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
754 uinfo->count = 1;
755 uinfo->value.enumerated.items = 2;
756 if (uinfo->value.enumerated.item >= 2)
757 uinfo->value.enumerated.item = 1;
758 strcpy(uinfo->value.enumerated.name,
759 texts[uinfo->value.enumerated.item]);
760 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800761}
762
763static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
764 struct snd_ctl_elem_value *ucontrol)
765{
766 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800767 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800768
Takashi Iwaiece8d042011-06-19 16:24:21 +0200769 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800770 return 0;
771}
772
Harald Welte0aa62ae2008-09-09 15:58:27 +0800773static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
774 struct snd_ctl_elem_value *ucontrol)
775{
776 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
777 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200778 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200779
Takashi Iwai25250502011-06-30 17:24:47 +0200780 /* no independent-hp status change during PCM playback is running */
781 if (spec->num_active_streams)
782 return -EBUSY;
783
784 cur = !!ucontrol->value.enumerated.item[0];
785 if (spec->hp_independent_mode == cur)
786 return 0;
787 spec->hp_independent_mode = cur;
788 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200789 activate_output_path(codec, &spec->hp_dep_path, false, false);
790 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200791 if (spec->hp_indep_shared)
792 activate_output_path(codec, &spec->out_path[HDA_SIDE],
793 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200794 } else {
795 activate_output_path(codec, &spec->hp_path, false, false);
796 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200797 if (spec->hp_indep_shared)
798 activate_output_path(codec, &spec->out_path[HDA_SIDE],
799 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200800 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800801
Lydia Wangce0e5a92011-03-22 16:22:37 +0800802 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800803 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200804 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200805 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800806}
807
Takashi Iwaiece8d042011-06-19 16:24:21 +0200808static const struct snd_kcontrol_new via_hp_mixer = {
809 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
810 .name = "Independent HP",
811 .info = via_independent_hp_info,
812 .get = via_independent_hp_get,
813 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800814};
815
Takashi Iwai3d83e572010-04-14 14:36:23 +0200816static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100817{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200818 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100819 struct snd_kcontrol_new *knew;
820 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100821
Takashi Iwaiece8d042011-06-19 16:24:21 +0200822 nid = spec->autocfg.hp_pins[0];
823 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200824 if (knew == NULL)
825 return -ENOMEM;
826
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100827 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100828
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100829 return 0;
830}
831
Lydia Wang1564b282009-10-10 19:07:52 +0800832static void notify_aa_path_ctls(struct hda_codec *codec)
833{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200834 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800835 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800836
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200837 for (i = 0; i < spec->smart51_nums; i++) {
838 struct snd_kcontrol *ctl;
839 struct snd_ctl_elem_id id;
840 memset(&id, 0, sizeof(id));
841 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
842 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800843 ctl = snd_hda_find_mixer_ctl(codec, id.name);
844 if (ctl)
845 snd_ctl_notify(codec->bus->card,
846 SNDRV_CTL_EVENT_MASK_VALUE,
847 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800848 }
849}
850
851static void mute_aa_path(struct hda_codec *codec, int mute)
852{
853 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200854 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800855 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200856
Lydia Wang1564b282009-10-10 19:07:52 +0800857 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200858 for (i = 0; i < spec->smart51_nums; i++) {
859 if (spec->smart51_idxs[i] < 0)
860 continue;
861 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
862 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800863 HDA_AMP_MUTE, val);
864 }
865}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200866
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200867static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
868{
869 struct via_spec *spec = codec->spec;
870 int i;
871
872 for (i = 0; i < spec->smart51_nums; i++)
873 if (spec->smart51_pins[i] == pin)
874 return true;
875 return false;
876}
877
Lydia Wang1564b282009-10-10 19:07:52 +0800878static int via_smart51_get(struct snd_kcontrol *kcontrol,
879 struct snd_ctl_elem_value *ucontrol)
880{
881 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
882 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800883
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200884 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800885 return 0;
886}
887
888static int via_smart51_put(struct snd_kcontrol *kcontrol,
889 struct snd_ctl_elem_value *ucontrol)
890{
891 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
892 struct via_spec *spec = codec->spec;
893 int out_in = *ucontrol->value.integer.value
894 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800895 int i;
896
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200897 for (i = 0; i < spec->smart51_nums; i++) {
898 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200899 unsigned int parm;
900
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200901 parm = snd_hda_codec_read(codec, nid, 0,
902 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
903 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
904 parm |= out_in;
905 snd_hda_codec_write(codec, nid, 0,
906 AC_VERB_SET_PIN_WIDGET_CONTROL,
907 parm);
908 if (out_in == AC_PINCTL_OUT_EN) {
909 mute_aa_path(codec, 1);
910 notify_aa_path_ctls(codec);
911 }
Lydia Wang1564b282009-10-10 19:07:52 +0800912 }
913 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800914 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800915 return 1;
916}
917
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200918static const struct snd_kcontrol_new via_smart51_mixer = {
919 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
920 .name = "Smart 5.1",
921 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200922 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200923 .get = via_smart51_get,
924 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800925};
926
Takashi Iwaif4a78282011-06-17 18:46:48 +0200927static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100928{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200929 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100930
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200931 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800932 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200933 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100934 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100935 return 0;
936}
937
Takashi Iwaiada509e2011-06-20 15:40:19 +0200938/* check AA path's mute status */
939static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800940{
Lydia Wangf5271102009-10-10 19:07:35 +0800941 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200942 const struct hda_amp_list *p;
943 int i, ch, v;
944
945 for (i = 0; i < spec->num_loopbacks; i++) {
946 p = &spec->loopback_list[i];
947 for (ch = 0; ch < 2; ch++) {
948 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
949 p->idx);
950 if (!(v & HDA_AMP_MUTE) && v > 0)
951 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800952 }
953 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200954 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800955}
956
957/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200958static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800959{
960 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200961 bool enable;
962 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800963
Takashi Iwaiada509e2011-06-20 15:40:19 +0200964 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800965
966 /* decide low current mode's verb & parameter */
967 switch (spec->codec_type) {
968 case VT1708B_8CH:
969 case VT1708B_4CH:
970 verb = 0xf70;
971 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
972 break;
973 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800974 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800975 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800976 verb = 0xf73;
977 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
978 break;
979 case VT1702:
980 verb = 0xf73;
981 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
982 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800983 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800984 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800985 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800986 verb = 0xf93;
987 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
988 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800989 default:
990 return; /* other codecs are not supported */
991 }
992 /* send verb */
993 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
994}
995
Joseph Chanc577b8a2006-11-29 15:29:40 +0100996/*
997 * generic initialization of ADC, input mixers and output mixers
998 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200999static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001000 /* power down jack detect function */
1001 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001002 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001003};
1004
Takashi Iwaiada509e2011-06-20 15:40:19 +02001005static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001006{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001007 struct via_spec *spec = codec->spec;
1008
1009 if (active)
1010 spec->num_active_streams++;
1011 else
1012 spec->num_active_streams--;
1013 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001014}
1015
Takashi Iwaiece8d042011-06-19 16:24:21 +02001016static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001017 struct hda_codec *codec,
1018 struct snd_pcm_substream *substream)
1019{
1020 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001021 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001022 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001023
Takashi Iwai25250502011-06-30 17:24:47 +02001024 spec->multiout.hp_nid = 0;
1025 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1026 if (!spec->hp_independent_mode) {
1027 if (!spec->hp_indep_shared)
1028 spec->multiout.hp_nid = spec->hp_dac_nid;
1029 } else {
1030 if (spec->hp_indep_shared)
1031 spec->multiout.num_dacs = cfg->line_outs - 1;
1032 }
1033 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001034 set_stream_active(codec, true);
1035 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1036 hinfo);
1037 if (err < 0) {
1038 spec->multiout.hp_nid = 0;
1039 set_stream_active(codec, false);
1040 return err;
1041 }
1042 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001043}
1044
Takashi Iwaiece8d042011-06-19 16:24:21 +02001045static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001046 struct hda_codec *codec,
1047 struct snd_pcm_substream *substream)
1048{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001049 struct via_spec *spec = codec->spec;
1050
1051 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001052 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001053 return 0;
1054}
1055
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001056static int via_playback_hp_pcm_open(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 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001063 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001064 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1065 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001066 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001067 return 0;
1068}
1069
1070static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1071 struct hda_codec *codec,
1072 struct snd_pcm_substream *substream)
1073{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001074 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001075 return 0;
1076}
1077
1078static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1079 struct hda_codec *codec,
1080 unsigned int stream_tag,
1081 unsigned int format,
1082 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001083{
1084 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001085
Takashi Iwaiece8d042011-06-19 16:24:21 +02001086 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1087 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001088 vt1708_start_hp_work(spec);
1089 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001090}
1091
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001092static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1093 struct hda_codec *codec,
1094 unsigned int stream_tag,
1095 unsigned int format,
1096 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001097{
1098 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001099
Takashi Iwaiece8d042011-06-19 16:24:21 +02001100 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1101 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001102 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001103 return 0;
1104}
1105
1106static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1107 struct hda_codec *codec,
1108 struct snd_pcm_substream *substream)
1109{
1110 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001111
Takashi Iwaiece8d042011-06-19 16:24:21 +02001112 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001113 vt1708_stop_hp_work(spec);
1114 return 0;
1115}
1116
1117static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1118 struct hda_codec *codec,
1119 struct snd_pcm_substream *substream)
1120{
1121 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001122
Takashi Iwaiece8d042011-06-19 16:24:21 +02001123 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001124 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001125 return 0;
1126}
1127
Joseph Chanc577b8a2006-11-29 15:29:40 +01001128/*
1129 * Digital out
1130 */
1131static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1132 struct hda_codec *codec,
1133 struct snd_pcm_substream *substream)
1134{
1135 struct via_spec *spec = codec->spec;
1136 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1137}
1138
1139static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1140 struct hda_codec *codec,
1141 struct snd_pcm_substream *substream)
1142{
1143 struct via_spec *spec = codec->spec;
1144 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1145}
1146
Harald Welte5691ec72008-09-15 22:42:26 +08001147static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001148 struct hda_codec *codec,
1149 unsigned int stream_tag,
1150 unsigned int format,
1151 struct snd_pcm_substream *substream)
1152{
1153 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001154 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1155 stream_tag, format, substream);
1156}
Harald Welte5691ec72008-09-15 22:42:26 +08001157
Takashi Iwai9da29272009-05-07 16:31:14 +02001158static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1159 struct hda_codec *codec,
1160 struct snd_pcm_substream *substream)
1161{
1162 struct via_spec *spec = codec->spec;
1163 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001164 return 0;
1165}
1166
Joseph Chanc577b8a2006-11-29 15:29:40 +01001167/*
1168 * Analog capture
1169 */
1170static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1171 struct hda_codec *codec,
1172 unsigned int stream_tag,
1173 unsigned int format,
1174 struct snd_pcm_substream *substream)
1175{
1176 struct via_spec *spec = codec->spec;
1177
1178 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1179 stream_tag, 0, format);
1180 return 0;
1181}
1182
1183static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1184 struct hda_codec *codec,
1185 struct snd_pcm_substream *substream)
1186{
1187 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001188 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001189 return 0;
1190}
1191
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001192/* analog capture with dynamic ADC switching */
1193static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1194 struct hda_codec *codec,
1195 unsigned int stream_tag,
1196 unsigned int format,
1197 struct snd_pcm_substream *substream)
1198{
1199 struct via_spec *spec = codec->spec;
1200 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1201
1202 spec->cur_adc = spec->adc_nids[adc_idx];
1203 spec->cur_adc_stream_tag = stream_tag;
1204 spec->cur_adc_format = format;
1205 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1206 return 0;
1207}
1208
1209static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1210 struct hda_codec *codec,
1211 struct snd_pcm_substream *substream)
1212{
1213 struct via_spec *spec = codec->spec;
1214
1215 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1216 spec->cur_adc = 0;
1217 return 0;
1218}
1219
1220/* re-setup the stream if running; called from input-src put */
1221static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1222{
1223 struct via_spec *spec = codec->spec;
1224 int adc_idx = spec->inputs[cur].adc_idx;
1225 hda_nid_t adc = spec->adc_nids[adc_idx];
1226
1227 if (spec->cur_adc && spec->cur_adc != adc) {
1228 /* stream is running, let's swap the current ADC */
1229 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1230 spec->cur_adc = adc;
1231 snd_hda_codec_setup_stream(codec, adc,
1232 spec->cur_adc_stream_tag, 0,
1233 spec->cur_adc_format);
1234 return true;
1235 }
1236 return false;
1237}
1238
Takashi Iwai9af74212011-06-18 16:17:45 +02001239static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001240 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001241 .channels_min = 2,
1242 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001243 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001244 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001245 .open = via_playback_multi_pcm_open,
1246 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001247 .prepare = via_playback_multi_pcm_prepare,
1248 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001249 },
1250};
1251
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001252static const struct hda_pcm_stream via_pcm_hp_playback = {
1253 .substreams = 1,
1254 .channels_min = 2,
1255 .channels_max = 2,
1256 /* NID is set in via_build_pcms */
1257 .ops = {
1258 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001259 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001260 .prepare = via_playback_hp_pcm_prepare,
1261 .cleanup = via_playback_hp_pcm_cleanup
1262 },
1263};
1264
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001265static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001266 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001267 .channels_min = 2,
1268 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001269 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001270 /* We got noisy outputs on the right channel on VT1708 when
1271 * 24bit samples are used. Until any workaround is found,
1272 * disable the 24bit format, so far.
1273 */
1274 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1275 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001276 .open = via_playback_multi_pcm_open,
1277 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001278 .prepare = via_playback_multi_pcm_prepare,
1279 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001280 },
1281};
1282
Takashi Iwai9af74212011-06-18 16:17:45 +02001283static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001284 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001285 .channels_min = 2,
1286 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001287 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001288 .ops = {
1289 .prepare = via_capture_pcm_prepare,
1290 .cleanup = via_capture_pcm_cleanup
1291 },
1292};
1293
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001294static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1295 .substreams = 1,
1296 .channels_min = 2,
1297 .channels_max = 2,
1298 /* NID is set in via_build_pcms */
1299 .ops = {
1300 .prepare = via_dyn_adc_capture_pcm_prepare,
1301 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1302 },
1303};
1304
Takashi Iwai9af74212011-06-18 16:17:45 +02001305static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001306 .substreams = 1,
1307 .channels_min = 2,
1308 .channels_max = 2,
1309 /* NID is set in via_build_pcms */
1310 .ops = {
1311 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001312 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001313 .prepare = via_dig_playback_pcm_prepare,
1314 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001315 },
1316};
1317
Takashi Iwai9af74212011-06-18 16:17:45 +02001318static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001319 .substreams = 1,
1320 .channels_min = 2,
1321 .channels_max = 2,
1322};
1323
Takashi Iwai370bafb2011-06-20 12:47:45 +02001324/*
1325 * slave controls for virtual master
1326 */
1327static const char * const via_slave_vols[] = {
1328 "Front Playback Volume",
1329 "Surround Playback Volume",
1330 "Center Playback Volume",
1331 "LFE Playback Volume",
1332 "Side Playback Volume",
1333 "Headphone Playback Volume",
1334 "Speaker Playback Volume",
1335 NULL,
1336};
1337
1338static const char * const via_slave_sws[] = {
1339 "Front Playback Switch",
1340 "Surround Playback Switch",
1341 "Center Playback Switch",
1342 "LFE Playback Switch",
1343 "Side Playback Switch",
1344 "Headphone Playback Switch",
1345 "Speaker Playback Switch",
1346 NULL,
1347};
1348
Joseph Chanc577b8a2006-11-29 15:29:40 +01001349static int via_build_controls(struct hda_codec *codec)
1350{
1351 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001352 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001353 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001354
Takashi Iwai24088a52011-06-17 16:59:21 +02001355 if (spec->set_widgets_power_state)
1356 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1357 return -ENOMEM;
1358
Joseph Chanc577b8a2006-11-29 15:29:40 +01001359 for (i = 0; i < spec->num_mixers; i++) {
1360 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1361 if (err < 0)
1362 return err;
1363 }
1364
1365 if (spec->multiout.dig_out_nid) {
1366 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001367 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001368 spec->multiout.dig_out_nid);
1369 if (err < 0)
1370 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001371 err = snd_hda_create_spdif_share_sw(codec,
1372 &spec->multiout);
1373 if (err < 0)
1374 return err;
1375 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001376 }
1377 if (spec->dig_in_nid) {
1378 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1379 if (err < 0)
1380 return err;
1381 }
Lydia Wang17314372009-10-10 19:07:37 +08001382
Takashi Iwai370bafb2011-06-20 12:47:45 +02001383 /* if we have no master control, let's create it */
1384 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1385 unsigned int vmaster_tlv[4];
1386 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1387 HDA_OUTPUT, vmaster_tlv);
1388 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1389 vmaster_tlv, via_slave_vols);
1390 if (err < 0)
1391 return err;
1392 }
1393 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1394 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1395 NULL, via_slave_sws);
1396 if (err < 0)
1397 return err;
1398 }
1399
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001400 /* assign Capture Source enums to NID */
1401 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1402 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001403 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001404 if (err < 0)
1405 return err;
1406 }
1407
Lydia Wang17314372009-10-10 19:07:37 +08001408 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001409 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001410 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001411
Takashi Iwai603c4012008-07-30 15:01:44 +02001412 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001413 return 0;
1414}
1415
1416static int via_build_pcms(struct hda_codec *codec)
1417{
1418 struct via_spec *spec = codec->spec;
1419 struct hda_pcm *info = spec->pcm_rec;
1420
1421 codec->num_pcms = 1;
1422 codec->pcm_info = info;
1423
Takashi Iwai82673bc2011-06-17 16:24:21 +02001424 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1425 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001426 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001427
1428 if (!spec->stream_analog_playback)
1429 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001430 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001431 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001432 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1433 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1435 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001436
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001437 if (!spec->stream_analog_capture) {
1438 if (spec->dyn_adc_switch)
1439 spec->stream_analog_capture =
1440 &via_pcm_dyn_adc_analog_capture;
1441 else
1442 spec->stream_analog_capture = &via_pcm_analog_capture;
1443 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001444 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1445 *spec->stream_analog_capture;
1446 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001447 if (!spec->dyn_adc_switch)
1448 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1449 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001450
1451 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1452 codec->num_pcms++;
1453 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001454 snprintf(spec->stream_name_digital,
1455 sizeof(spec->stream_name_digital),
1456 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001457 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001458 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001459 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001460 if (!spec->stream_digital_playback)
1461 spec->stream_digital_playback =
1462 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001463 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001464 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001465 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1466 spec->multiout.dig_out_nid;
1467 }
1468 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001469 if (!spec->stream_digital_capture)
1470 spec->stream_digital_capture =
1471 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001472 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001473 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001474 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1475 spec->dig_in_nid;
1476 }
1477 }
1478
Takashi Iwaiece8d042011-06-19 16:24:21 +02001479 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001480 codec->num_pcms++;
1481 info++;
1482 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1483 "%s HP", codec->chip_name);
1484 info->name = spec->stream_name_hp;
1485 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1486 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001487 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001488 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001489 return 0;
1490}
1491
1492static void via_free(struct hda_codec *codec)
1493{
1494 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001495
1496 if (!spec)
1497 return;
1498
Takashi Iwai603c4012008-07-30 15:01:44 +02001499 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001500 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001501 kfree(spec->bind_cap_vol);
1502 kfree(spec->bind_cap_sw);
1503 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001504}
1505
Takashi Iwai64be2852011-06-17 16:51:39 +02001506/* mute/unmute outputs */
1507static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1508 hda_nid_t *pins, bool mute)
1509{
1510 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001511 for (i = 0; i < num_pins; i++) {
1512 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1513 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1514 if (parm & AC_PINCTL_IN_EN)
1515 continue;
1516 if (mute)
1517 parm &= ~AC_PINCTL_OUT_EN;
1518 else
1519 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001520 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001521 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1522 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001523}
1524
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001525/* mute internal speaker if line-out is plugged */
1526static void via_line_automute(struct hda_codec *codec, int present)
1527{
1528 struct via_spec *spec = codec->spec;
1529
1530 if (!spec->autocfg.speaker_outs)
1531 return;
1532 if (!present)
1533 present = snd_hda_jack_detect(codec,
1534 spec->autocfg.line_out_pins[0]);
1535 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1536 spec->autocfg.speaker_pins,
1537 present);
1538}
1539
Harald Welte69e52a82008-09-09 15:57:32 +08001540/* mute internal speaker if HP is plugged */
1541static void via_hp_automute(struct hda_codec *codec)
1542{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001543 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001544 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001545 struct via_spec *spec = codec->spec;
1546
Takashi Iwai6e969d92011-07-11 11:28:13 +02001547 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001548 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001549
1550 if (spec->smart51_enabled)
1551 nums = spec->autocfg.line_outs + spec->smart51_nums;
1552 else
1553 nums = spec->autocfg.line_outs;
1554 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1555
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001556 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001557}
1558
Harald Welte69e52a82008-09-09 15:57:32 +08001559static void via_gpio_control(struct hda_codec *codec)
1560{
1561 unsigned int gpio_data;
1562 unsigned int vol_counter;
1563 unsigned int vol;
1564 unsigned int master_vol;
1565
1566 struct via_spec *spec = codec->spec;
1567
1568 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1569 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1570
1571 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1572 0xF84, 0) & 0x3F0000) >> 16;
1573
1574 vol = vol_counter & 0x1F;
1575 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1576 AC_VERB_GET_AMP_GAIN_MUTE,
1577 AC_AMP_GET_INPUT);
1578
1579 if (gpio_data == 0x02) {
1580 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001581 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1582 AC_VERB_SET_PIN_WIDGET_CONTROL,
1583 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001584 if (vol_counter & 0x20) {
1585 /* decrease volume */
1586 if (vol > master_vol)
1587 vol = master_vol;
1588 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1589 0, HDA_AMP_VOLMASK,
1590 master_vol-vol);
1591 } else {
1592 /* increase volume */
1593 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1594 HDA_AMP_VOLMASK,
1595 ((master_vol+vol) > 0x2A) ? 0x2A :
1596 (master_vol+vol));
1597 }
1598 } else if (!(gpio_data & 0x02)) {
1599 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001600 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1601 AC_VERB_SET_PIN_WIDGET_CONTROL,
1602 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001603 }
1604}
1605
1606/* unsolicited event for jack sensing */
1607static void via_unsol_event(struct hda_codec *codec,
1608 unsigned int res)
1609{
1610 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001611
Lydia Wanga34df192009-10-10 19:08:01 +08001612 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001613 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001614
1615 res &= ~VIA_JACK_EVENT;
1616
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001617 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001618 via_hp_automute(codec);
1619 else if (res == VIA_GPIO_EVENT)
1620 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001621}
1622
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001623#ifdef SND_HDA_NEEDS_RESUME
1624static int via_suspend(struct hda_codec *codec, pm_message_t state)
1625{
1626 struct via_spec *spec = codec->spec;
1627 vt1708_stop_hp_work(spec);
1628 return 0;
1629}
1630#endif
1631
Takashi Iwaicb53c622007-08-10 17:21:45 +02001632#ifdef CONFIG_SND_HDA_POWER_SAVE
1633static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1634{
1635 struct via_spec *spec = codec->spec;
1636 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1637}
1638#endif
1639
Joseph Chanc577b8a2006-11-29 15:29:40 +01001640/*
1641 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001642
1643static int via_init(struct hda_codec *codec);
1644
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001645static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001646 .build_controls = via_build_controls,
1647 .build_pcms = via_build_pcms,
1648 .init = via_init,
1649 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001650 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001651#ifdef SND_HDA_NEEDS_RESUME
1652 .suspend = via_suspend,
1653#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001654#ifdef CONFIG_SND_HDA_POWER_SAVE
1655 .check_power_status = via_check_power_status,
1656#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001657};
1658
Takashi Iwai4a796162011-06-17 17:53:38 +02001659static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001660{
Takashi Iwai4a796162011-06-17 17:53:38 +02001661 struct via_spec *spec = codec->spec;
1662 int i;
1663
1664 for (i = 0; i < spec->multiout.num_dacs; i++) {
1665 if (spec->multiout.dac_nids[i] == dac)
1666 return false;
1667 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001668 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001669 return false;
1670 return true;
1671}
1672
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001673static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001674 hda_nid_t target_dac, struct nid_path *path,
1675 int depth, int wid_type)
1676{
1677 hda_nid_t conn[8];
1678 int i, nums;
1679
1680 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1681 for (i = 0; i < nums; i++) {
1682 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1683 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001684 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1685 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001686 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001687 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001688 return false;
1689 for (i = 0; i < nums; i++) {
1690 unsigned int type;
1691 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1692 if (type == AC_WID_AUD_OUT ||
1693 (wid_type != -1 && type != wid_type))
1694 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001695 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001696 path, depth + 1, AC_WID_AUD_SEL))
1697 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001698 }
1699 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001700
1701 found:
1702 path->path[path->depth] = conn[i];
1703 path->idx[path->depth] = i;
1704 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1705 path->multi[path->depth] = 1;
1706 path->depth++;
1707 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001708}
1709
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001710static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1711 hda_nid_t target_dac, struct nid_path *path)
1712{
1713 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1714 path->path[path->depth] = nid;
1715 path->depth++;
1716 return true;
1717 }
1718 return false;
1719}
1720
Takashi Iwai4a796162011-06-17 17:53:38 +02001721static int via_auto_fill_dac_nids(struct hda_codec *codec)
1722{
1723 struct via_spec *spec = codec->spec;
1724 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001725 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001726 hda_nid_t nid;
1727
Joseph Chanc577b8a2006-11-29 15:29:40 +01001728 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001729 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001730 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001731 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001732 if (!nid)
1733 continue;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001734 if (parse_output_path(codec, nid, 0, &spec->out_path[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001735 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Lydia Wang5c9a5612011-07-08 14:03:43 +08001736 dac_num++;
1737 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001739 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001740 return 0;
1741}
1742
Takashi Iwai4a796162011-06-17 17:53:38 +02001743static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001744 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001745{
Takashi Iwai4a796162011-06-17 17:53:38 +02001746 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001747 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001748 hda_nid_t dac, pin, sel, nid;
1749 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001750
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001751 dac = check_dac ? path->path[0] : 0;
1752 pin = path->path[path->depth - 1];
1753 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001754
Takashi Iwai8df2a312011-06-21 11:48:29 +02001755 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001756 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001757 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001758 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001759 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1760 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001761 else
1762 nid = 0;
1763 if (nid) {
1764 sprintf(name, "%s Playback Volume", pfx);
1765 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001766 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001767 if (err < 0)
1768 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001769 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001770 }
1771
Takashi Iwai8df2a312011-06-21 11:48:29 +02001772 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001773 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001774 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001775 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001776 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1777 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001778 else
1779 nid = 0;
1780 if (nid) {
1781 sprintf(name, "%s Playback Switch", pfx);
1782 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1783 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1784 if (err < 0)
1785 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001786 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001787 }
1788 return 0;
1789}
1790
Takashi Iwaif4a78282011-06-17 18:46:48 +02001791static void mangle_smart51(struct hda_codec *codec)
1792{
1793 struct via_spec *spec = codec->spec;
1794 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001795 struct auto_pin_cfg_item *ins = cfg->inputs;
1796 int i, j, nums, attr;
1797 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001798
Takashi Iwai0f98c242011-06-21 12:51:33 +02001799 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1800 nums = 0;
1801 for (i = 0; i < cfg->num_inputs; i++) {
1802 unsigned int def;
1803 if (ins[i].type > AUTO_PIN_LINE_IN)
1804 continue;
1805 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1806 if (snd_hda_get_input_pin_attr(def) != attr)
1807 continue;
1808 for (j = 0; j < nums; j++)
1809 if (ins[pins[j]].type < ins[i].type) {
1810 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001811 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001812 break;
1813 }
1814 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001815 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001816 }
1817 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001818 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001819 for (i = 0; i < nums; i++) {
1820 hda_nid_t pin = ins[pins[i]].pin;
1821 spec->smart51_pins[spec->smart51_nums++] = pin;
1822 cfg->line_out_pins[cfg->line_outs++] = pin;
1823 if (cfg->line_outs == 3)
1824 break;
1825 }
1826 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001827 }
1828}
1829
Takashi Iwai4a796162011-06-17 17:53:38 +02001830/* add playback controls from the parsed DAC table */
1831static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1832{
1833 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001834 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001835 static const char * const chname[4] = {
1836 "Front", "Surround", "C/LFE", "Side"
1837 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001838 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001839 int old_line_outs;
1840
1841 /* check smart51 */
1842 old_line_outs = cfg->line_outs;
1843 if (cfg->line_outs == 1)
1844 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001845
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001846 err = via_auto_fill_dac_nids(codec);
1847 if (err < 0)
1848 return err;
1849
Lydia Wang5c9a5612011-07-08 14:03:43 +08001850 if (spec->multiout.num_dacs < 3) {
1851 spec->smart51_nums = 0;
1852 cfg->line_outs = old_line_outs;
1853 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001854 for (i = 0; i < cfg->line_outs; i++) {
1855 hda_nid_t pin, dac;
1856 pin = cfg->line_out_pins[i];
1857 dac = spec->multiout.dac_nids[i];
1858 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001859 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001860 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001861 err = create_ch_ctls(codec, "Center", 1, true,
1862 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001863 if (err < 0)
1864 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001865 err = create_ch_ctls(codec, "LFE", 2, true,
1866 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001867 if (err < 0)
1868 return err;
1869 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001870 const char *pfx = chname[i];
1871 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1872 cfg->line_outs == 1)
1873 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001874 err = create_ch_ctls(codec, pfx, 3, true,
1875 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001876 if (err < 0)
1877 return err;
1878 }
1879 }
1880
Takashi Iwai4a796162011-06-17 17:53:38 +02001881 idx = get_connection_index(codec, spec->aa_mix_nid,
1882 spec->multiout.dac_nids[0]);
1883 if (idx >= 0) {
1884 /* add control to mixer */
1885 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1886 "PCM Playback Volume",
1887 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1888 idx, HDA_INPUT));
1889 if (err < 0)
1890 return err;
1891 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1892 "PCM Playback Switch",
1893 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1894 idx, HDA_INPUT));
1895 if (err < 0)
1896 return err;
1897 }
1898
Takashi Iwaif4a78282011-06-17 18:46:48 +02001899 cfg->line_outs = old_line_outs;
1900
Joseph Chanc577b8a2006-11-29 15:29:40 +01001901 return 0;
1902}
1903
Takashi Iwai4a796162011-06-17 17:53:38 +02001904static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001905{
Takashi Iwai4a796162011-06-17 17:53:38 +02001906 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001907 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001908 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001909 int err;
1910
1911 if (!pin)
1912 return 0;
1913
Takashi Iwai8df2a312011-06-21 11:48:29 +02001914 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001915 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001916 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1917 parse_output_path(codec, pin,
1918 spec->multiout.dac_nids[HDA_SIDE],
1919 &spec->hp_path)) {
1920 spec->hp_dac_nid = spec->hp_path.path[0];
1921 spec->hp_indep_shared = true;
Lydia Wanga2a870c2011-07-08 14:04:33 +08001922 } else if (spec->multiout.dac_nids[HDA_CLFE] &&
1923 parse_output_path(codec, pin,
1924 spec->multiout.dac_nids[HDA_CLFE],
1925 &spec->hp_path)) {
1926 spec->hp_dac_nid = spec->hp_path.path[0];
1927 spec->hp_indep_shared = true;
Takashi Iwai25250502011-06-30 17:24:47 +02001928 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001929
Takashi Iwaiece8d042011-06-19 16:24:21 +02001930 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001931 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001932 !spec->hp_dac_nid)
1933 return 0;
1934
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001935 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001936 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001937 check_dac = true;
1938 } else {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001939 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001940 check_dac = false;
1941 }
1942 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001943 if (err < 0)
1944 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001945 if (spec->hp_dac_nid) {
1946 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1947 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1948 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001949
Joseph Chanc577b8a2006-11-29 15:29:40 +01001950 return 0;
1951}
1952
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001953static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1954{
1955 struct via_spec *spec = codec->spec;
1956 hda_nid_t pin, dac;
1957
1958 pin = spec->autocfg.speaker_pins[0];
1959 if (!spec->autocfg.speaker_outs || !pin)
1960 return 0;
1961
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001962 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1963 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001964 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001965 return create_ch_ctls(codec, "Speaker", 3, true,
1966 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001967 }
1968 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001969 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001970 return create_ch_ctls(codec, "Speaker", 3, false,
1971 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001972
1973 return 0;
1974}
1975
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001976/* look for ADCs */
1977static int via_fill_adcs(struct hda_codec *codec)
1978{
1979 struct via_spec *spec = codec->spec;
1980 hda_nid_t nid = codec->start_nid;
1981 int i;
1982
1983 for (i = 0; i < codec->num_nodes; i++, nid++) {
1984 unsigned int wcaps = get_wcaps(codec, nid);
1985 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1986 continue;
1987 if (wcaps & AC_WCAP_DIGITAL)
1988 continue;
1989 if (!(wcaps & AC_WCAP_CONN_LIST))
1990 continue;
1991 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1992 return -ENOMEM;
1993 spec->adc_nids[spec->num_adc_nids++] = nid;
1994 }
1995 return 0;
1996}
1997
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001998/* input-src control */
1999static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2000 struct snd_ctl_elem_info *uinfo)
2001{
2002 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2003 struct via_spec *spec = codec->spec;
2004
2005 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2006 uinfo->count = 1;
2007 uinfo->value.enumerated.items = spec->num_inputs;
2008 if (uinfo->value.enumerated.item >= spec->num_inputs)
2009 uinfo->value.enumerated.item = spec->num_inputs - 1;
2010 strcpy(uinfo->value.enumerated.name,
2011 spec->inputs[uinfo->value.enumerated.item].label);
2012 return 0;
2013}
2014
2015static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2016 struct snd_ctl_elem_value *ucontrol)
2017{
2018 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2019 struct via_spec *spec = codec->spec;
2020 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2021
2022 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2023 return 0;
2024}
2025
2026static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2027 struct snd_ctl_elem_value *ucontrol)
2028{
2029 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2030 struct via_spec *spec = codec->spec;
2031 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2032 hda_nid_t mux;
2033 int cur;
2034
2035 cur = ucontrol->value.enumerated.item[0];
2036 if (cur < 0 || cur >= spec->num_inputs)
2037 return -EINVAL;
2038 if (spec->cur_mux[idx] == cur)
2039 return 0;
2040 spec->cur_mux[idx] = cur;
2041 if (spec->dyn_adc_switch) {
2042 int adc_idx = spec->inputs[cur].adc_idx;
2043 mux = spec->mux_nids[adc_idx];
2044 via_dyn_adc_pcm_resetup(codec, cur);
2045 } else {
2046 mux = spec->mux_nids[idx];
2047 if (snd_BUG_ON(!mux))
2048 return -EINVAL;
2049 }
2050
2051 if (mux) {
2052 /* switch to D0 beofre change index */
2053 if (snd_hda_codec_read(codec, mux, 0,
2054 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2055 snd_hda_codec_write(codec, mux, 0,
2056 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2057 snd_hda_codec_write(codec, mux, 0,
2058 AC_VERB_SET_CONNECT_SEL,
2059 spec->inputs[cur].mux_idx);
2060 }
2061
2062 /* update jack power state */
2063 set_widgets_power_state(codec);
2064 return 0;
2065}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002066
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002067static const struct snd_kcontrol_new via_input_src_ctl = {
2068 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2069 /* The multiple "Capture Source" controls confuse alsamixer
2070 * So call somewhat different..
2071 */
2072 /* .name = "Capture Source", */
2073 .name = "Input Source",
2074 .info = via_mux_enum_info,
2075 .get = via_mux_enum_get,
2076 .put = via_mux_enum_put,
2077};
2078
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002079static int create_input_src_ctls(struct hda_codec *codec, int count)
2080{
2081 struct via_spec *spec = codec->spec;
2082 struct snd_kcontrol_new *knew;
2083
2084 if (spec->num_inputs <= 1 || !count)
2085 return 0; /* no need for single src */
2086
2087 knew = via_clone_control(spec, &via_input_src_ctl);
2088 if (!knew)
2089 return -ENOMEM;
2090 knew->count = count;
2091 return 0;
2092}
2093
2094/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002095static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2096{
2097 struct hda_amp_list *list;
2098
2099 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2100 return;
2101 list = spec->loopback_list + spec->num_loopbacks;
2102 list->nid = mix;
2103 list->dir = HDA_INPUT;
2104 list->idx = idx;
2105 spec->num_loopbacks++;
2106 spec->loopback.amplist = spec->loopback_list;
2107}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002108
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002109static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002110 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002111{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002112 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002113}
2114
2115/* add the input-route to the given pin */
2116static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002117{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002118 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002119 int c, idx;
2120
2121 spec->inputs[spec->num_inputs].adc_idx = -1;
2122 spec->inputs[spec->num_inputs].pin = pin;
2123 for (c = 0; c < spec->num_adc_nids; c++) {
2124 if (spec->mux_nids[c]) {
2125 idx = get_connection_index(codec, spec->mux_nids[c],
2126 pin);
2127 if (idx < 0)
2128 continue;
2129 spec->inputs[spec->num_inputs].mux_idx = idx;
2130 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002131 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002132 continue;
2133 }
2134 spec->inputs[spec->num_inputs].adc_idx = c;
2135 /* Can primary ADC satisfy all inputs? */
2136 if (!spec->dyn_adc_switch &&
2137 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2138 snd_printd(KERN_INFO
2139 "via: dynamic ADC switching enabled\n");
2140 spec->dyn_adc_switch = 1;
2141 }
2142 return true;
2143 }
2144 return false;
2145}
2146
2147static int get_mux_nids(struct hda_codec *codec);
2148
2149/* parse input-routes; fill ADCs, MUXs and input-src entries */
2150static int parse_analog_inputs(struct hda_codec *codec)
2151{
2152 struct via_spec *spec = codec->spec;
2153 const struct auto_pin_cfg *cfg = &spec->autocfg;
2154 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002155
2156 err = via_fill_adcs(codec);
2157 if (err < 0)
2158 return err;
2159 err = get_mux_nids(codec);
2160 if (err < 0)
2161 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002162
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002163 /* fill all input-routes */
2164 for (i = 0; i < cfg->num_inputs; i++) {
2165 if (add_input_route(codec, cfg->inputs[i].pin))
2166 spec->inputs[spec->num_inputs++].label =
2167 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002168 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002169
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002170 /* check for internal loopback recording */
2171 if (spec->aa_mix_nid &&
2172 add_input_route(codec, spec->aa_mix_nid))
2173 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2174
2175 return 0;
2176}
2177
2178/* create analog-loopback volume/switch controls */
2179static int create_loopback_ctls(struct hda_codec *codec)
2180{
2181 struct via_spec *spec = codec->spec;
2182 const struct auto_pin_cfg *cfg = &spec->autocfg;
2183 const char *prev_label = NULL;
2184 int type_idx = 0;
2185 int i, j, err, idx;
2186
2187 if (!spec->aa_mix_nid)
2188 return 0;
2189
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002190 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002191 hda_nid_t pin = cfg->inputs[i].pin;
2192 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2193
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002194 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002195 type_idx++;
2196 else
2197 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002198 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002199 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2200 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002201 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002202 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002203 if (err < 0)
2204 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002205 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002206 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002207
2208 /* remember the label for smart51 control */
2209 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002210 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002211 spec->smart51_idxs[j] = idx;
2212 spec->smart51_labels[j] = label;
2213 break;
2214 }
2215 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002216 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002217 return 0;
2218}
2219
2220/* create mic-boost controls (if present) */
2221static int create_mic_boost_ctls(struct hda_codec *codec)
2222{
2223 struct via_spec *spec = codec->spec;
2224 const struct auto_pin_cfg *cfg = &spec->autocfg;
2225 int i, err;
2226
2227 for (i = 0; i < cfg->num_inputs; i++) {
2228 hda_nid_t pin = cfg->inputs[i].pin;
2229 unsigned int caps;
2230 const char *label;
2231 char name[32];
2232
2233 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2234 continue;
2235 caps = query_amp_caps(codec, pin, HDA_INPUT);
2236 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2237 continue;
2238 label = hda_get_autocfg_input_label(codec, cfg, i);
2239 snprintf(name, sizeof(name), "%s Boost Volume", label);
2240 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2241 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2242 if (err < 0)
2243 return err;
2244 }
2245 return 0;
2246}
2247
2248/* create capture and input-src controls for multiple streams */
2249static int create_multi_adc_ctls(struct hda_codec *codec)
2250{
2251 struct via_spec *spec = codec->spec;
2252 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002253
2254 /* create capture mixer elements */
2255 for (i = 0; i < spec->num_adc_nids; i++) {
2256 hda_nid_t adc = spec->adc_nids[i];
2257 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2258 "Capture Volume", i,
2259 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2260 HDA_INPUT));
2261 if (err < 0)
2262 return err;
2263 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2264 "Capture Switch", i,
2265 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2266 HDA_INPUT));
2267 if (err < 0)
2268 return err;
2269 }
2270
2271 /* input-source control */
2272 for (i = 0; i < spec->num_adc_nids; i++)
2273 if (!spec->mux_nids[i])
2274 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002275 err = create_input_src_ctls(codec, i);
2276 if (err < 0)
2277 return err;
2278 return 0;
2279}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002280
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002281/* bind capture volume/switch */
2282static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2283 HDA_BIND_VOL("Capture Volume", 0);
2284static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2285 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002286
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002287static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2288 struct hda_ctl_ops *ops)
2289{
2290 struct hda_bind_ctls *ctl;
2291 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002292
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002293 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2294 if (!ctl)
2295 return -ENOMEM;
2296 ctl->ops = ops;
2297 for (i = 0; i < spec->num_adc_nids; i++)
2298 ctl->values[i] =
2299 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2300 *ctl_ret = ctl;
2301 return 0;
2302}
2303
2304/* create capture and input-src controls for dynamic ADC-switch case */
2305static int create_dyn_adc_ctls(struct hda_codec *codec)
2306{
2307 struct via_spec *spec = codec->spec;
2308 struct snd_kcontrol_new *knew;
2309 int err;
2310
2311 /* set up the bind capture ctls */
2312 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2313 if (err < 0)
2314 return err;
2315 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2316 if (err < 0)
2317 return err;
2318
2319 /* create capture mixer elements */
2320 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2321 if (!knew)
2322 return -ENOMEM;
2323 knew->private_value = (long)spec->bind_cap_vol;
2324
2325 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2326 if (!knew)
2327 return -ENOMEM;
2328 knew->private_value = (long)spec->bind_cap_sw;
2329
2330 /* input-source control */
2331 err = create_input_src_ctls(codec, 1);
2332 if (err < 0)
2333 return err;
2334 return 0;
2335}
2336
2337/* parse and create capture-related stuff */
2338static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2339{
2340 struct via_spec *spec = codec->spec;
2341 int err;
2342
2343 err = parse_analog_inputs(codec);
2344 if (err < 0)
2345 return err;
2346 if (spec->dyn_adc_switch)
2347 err = create_dyn_adc_ctls(codec);
2348 else
2349 err = create_multi_adc_ctls(codec);
2350 if (err < 0)
2351 return err;
2352 err = create_loopback_ctls(codec);
2353 if (err < 0)
2354 return err;
2355 err = create_mic_boost_ctls(codec);
2356 if (err < 0)
2357 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002358 return 0;
2359}
2360
Harald Welte76d9b0d2008-09-09 15:50:37 +08002361static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2362{
2363 unsigned int def_conf;
2364 unsigned char seqassoc;
2365
Takashi Iwai2f334f92009-02-20 14:37:42 +01002366 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002367 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2368 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002369 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2370 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2371 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2372 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002373 }
2374
2375 return;
2376}
2377
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002378static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002379 struct snd_ctl_elem_value *ucontrol)
2380{
2381 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2382 struct via_spec *spec = codec->spec;
2383
2384 if (spec->codec_type != VT1708)
2385 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002386 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002387 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002388 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002389 return 0;
2390}
2391
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002392static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002393 struct snd_ctl_elem_value *ucontrol)
2394{
2395 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2396 struct via_spec *spec = codec->spec;
2397 int change;
2398
2399 if (spec->codec_type != VT1708)
2400 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002401 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002402 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002403 == !spec->vt1708_jack_detect;
2404 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002405 mute_aa_path(codec, 1);
2406 notify_aa_path_ctls(codec);
2407 }
2408 return change;
2409}
2410
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002411static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2412 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2413 .name = "Jack Detect",
2414 .count = 1,
2415 .info = snd_ctl_boolean_mono_info,
2416 .get = vt1708_jack_detect_get,
2417 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002418};
2419
Takashi Iwai12daef62011-06-18 17:45:49 +02002420static void fill_dig_outs(struct hda_codec *codec);
2421static void fill_dig_in(struct hda_codec *codec);
2422
2423static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002424{
2425 struct via_spec *spec = codec->spec;
2426 int err;
2427
2428 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2429 if (err < 0)
2430 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002431 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002432 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002433
Takashi Iwai4a796162011-06-17 17:53:38 +02002434 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002435 if (err < 0)
2436 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002437 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002438 if (err < 0)
2439 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002440 err = via_auto_create_speaker_ctls(codec);
2441 if (err < 0)
2442 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002443 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002444 if (err < 0)
2445 return err;
2446
2447 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2448
Takashi Iwai12daef62011-06-18 17:45:49 +02002449 fill_dig_outs(codec);
2450 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002451
Takashi Iwai603c4012008-07-30 15:01:44 +02002452 if (spec->kctls.list)
2453 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002454
Joseph Chanc577b8a2006-11-29 15:29:40 +01002455
Takashi Iwai8df2a312011-06-21 11:48:29 +02002456 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002457 err = via_hp_build(codec);
2458 if (err < 0)
2459 return err;
2460 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002461
Takashi Iwaif4a78282011-06-17 18:46:48 +02002462 err = via_smart51_build(codec);
2463 if (err < 0)
2464 return err;
2465
Takashi Iwai5d417622011-06-20 11:32:27 +02002466 /* assign slave outs */
2467 if (spec->slave_dig_outs[0])
2468 codec->slave_dig_outs = spec->slave_dig_outs;
2469
Joseph Chanc577b8a2006-11-29 15:29:40 +01002470 return 1;
2471}
2472
Takashi Iwai5d417622011-06-20 11:32:27 +02002473static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002474{
Lydia Wang25eaba22009-10-10 19:08:43 +08002475 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002476 if (spec->multiout.dig_out_nid)
2477 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2478 if (spec->slave_dig_outs[0])
2479 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2480}
Lydia Wang25eaba22009-10-10 19:08:43 +08002481
Takashi Iwai5d417622011-06-20 11:32:27 +02002482static void via_auto_init_dig_in(struct hda_codec *codec)
2483{
2484 struct via_spec *spec = codec->spec;
2485 if (!spec->dig_in_nid)
2486 return;
2487 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2488 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2489}
2490
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002491/* initialize the unsolicited events */
2492static void via_auto_init_unsol_event(struct hda_codec *codec)
2493{
2494 struct via_spec *spec = codec->spec;
2495 struct auto_pin_cfg *cfg = &spec->autocfg;
2496 unsigned int ev;
2497 int i;
2498
2499 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2500 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2501 AC_VERB_SET_UNSOLICITED_ENABLE,
2502 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2503
2504 if (cfg->speaker_pins[0])
2505 ev = VIA_LINE_EVENT;
2506 else
2507 ev = 0;
2508 for (i = 0; i < cfg->line_outs; i++) {
2509 if (cfg->line_out_pins[i] &&
2510 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002511 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002512 AC_VERB_SET_UNSOLICITED_ENABLE,
2513 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2514 }
2515
2516 for (i = 0; i < cfg->num_inputs; i++) {
2517 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2518 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2519 AC_VERB_SET_UNSOLICITED_ENABLE,
2520 AC_USRSP_EN | VIA_JACK_EVENT);
2521 }
2522}
2523
Takashi Iwai5d417622011-06-20 11:32:27 +02002524static int via_init(struct hda_codec *codec)
2525{
2526 struct via_spec *spec = codec->spec;
2527 int i;
2528
2529 for (i = 0; i < spec->num_iverbs; i++)
2530 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2531
Joseph Chanc577b8a2006-11-29 15:29:40 +01002532 via_auto_init_multi_out(codec);
2533 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002534 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002535 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002536 via_auto_init_dig_outs(codec);
2537 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002538
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002539 via_auto_init_unsol_event(codec);
2540
2541 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002542
Joseph Chanc577b8a2006-11-29 15:29:40 +01002543 return 0;
2544}
2545
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002546static void vt1708_update_hp_jack_state(struct work_struct *work)
2547{
2548 struct via_spec *spec = container_of(work, struct via_spec,
2549 vt1708_hp_work.work);
2550 if (spec->codec_type != VT1708)
2551 return;
2552 /* if jack state toggled */
2553 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002554 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002555 spec->vt1708_hp_present ^= 1;
2556 via_hp_automute(spec->codec);
2557 }
2558 vt1708_start_hp_work(spec);
2559}
2560
Takashi Iwai337b9d02009-07-07 18:18:59 +02002561static int get_mux_nids(struct hda_codec *codec)
2562{
2563 struct via_spec *spec = codec->spec;
2564 hda_nid_t nid, conn[8];
2565 unsigned int type;
2566 int i, n;
2567
2568 for (i = 0; i < spec->num_adc_nids; i++) {
2569 nid = spec->adc_nids[i];
2570 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002571 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002572 if (type == AC_WID_PIN)
2573 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002574 n = snd_hda_get_connections(codec, nid, conn,
2575 ARRAY_SIZE(conn));
2576 if (n <= 0)
2577 break;
2578 if (n > 1) {
2579 spec->mux_nids[i] = nid;
2580 break;
2581 }
2582 nid = conn[0];
2583 }
2584 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002585 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002586}
2587
Joseph Chanc577b8a2006-11-29 15:29:40 +01002588static int patch_vt1708(struct hda_codec *codec)
2589{
2590 struct via_spec *spec;
2591 int err;
2592
2593 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002594 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002595 if (spec == NULL)
2596 return -ENOMEM;
2597
Takashi Iwai620e2b22011-06-17 17:19:19 +02002598 spec->aa_mix_nid = 0x17;
2599
Takashi Iwai12daef62011-06-18 17:45:49 +02002600 /* Add HP and CD pin config connect bit re-config action */
2601 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2602 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2603
Joseph Chanc577b8a2006-11-29 15:29:40 +01002604 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002605 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002606 if (err < 0) {
2607 via_free(codec);
2608 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002609 }
2610
Takashi Iwai12daef62011-06-18 17:45:49 +02002611 /* add jack detect on/off control */
2612 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2613 return -ENOMEM;
2614
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002615 /* disable 32bit format on VT1708 */
2616 if (codec->vendor_id == 0x11061708)
2617 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002618
Lydia Wange322a362011-06-29 13:52:02 +08002619 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2620
Joseph Chanc577b8a2006-11-29 15:29:40 +01002621 codec->patch_ops = via_patch_ops;
2622
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002623 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002624 return 0;
2625}
2626
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002627static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002628{
2629 struct via_spec *spec;
2630 int err;
2631
2632 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002633 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002634 if (spec == NULL)
2635 return -ENOMEM;
2636
Takashi Iwai620e2b22011-06-17 17:19:19 +02002637 spec->aa_mix_nid = 0x18;
2638
Takashi Iwai12daef62011-06-18 17:45:49 +02002639 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002640 if (err < 0) {
2641 via_free(codec);
2642 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002643 }
2644
Joseph Chanc577b8a2006-11-29 15:29:40 +01002645 codec->patch_ops = via_patch_ops;
2646
Josepch Chanf7278fd2007-12-13 16:40:40 +01002647 return 0;
2648}
2649
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002650static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2651{
2652 struct via_spec *spec = codec->spec;
2653 int imux_is_smixer;
2654 unsigned int parm;
2655 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002656 if ((spec->codec_type != VT1708B_4CH) &&
2657 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002658 is_8ch = 1;
2659
2660 /* SW0 (17h) = stereo mixer */
2661 imux_is_smixer =
2662 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2663 == ((spec->codec_type == VT1708S) ? 5 : 0));
2664 /* inputs */
2665 /* PW 1/2/5 (1ah/1bh/1eh) */
2666 parm = AC_PWRST_D3;
2667 set_pin_power_state(codec, 0x1a, &parm);
2668 set_pin_power_state(codec, 0x1b, &parm);
2669 set_pin_power_state(codec, 0x1e, &parm);
2670 if (imux_is_smixer)
2671 parm = AC_PWRST_D0;
2672 /* SW0 (17h), AIW 0/1 (13h/14h) */
2673 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2674 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2675 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2676
2677 /* outputs */
2678 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2679 parm = AC_PWRST_D3;
2680 set_pin_power_state(codec, 0x19, &parm);
2681 if (spec->smart51_enabled)
2682 set_pin_power_state(codec, 0x1b, &parm);
2683 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2684 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2685
2686 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2687 if (is_8ch) {
2688 parm = AC_PWRST_D3;
2689 set_pin_power_state(codec, 0x22, &parm);
2690 if (spec->smart51_enabled)
2691 set_pin_power_state(codec, 0x1a, &parm);
2692 snd_hda_codec_write(codec, 0x26, 0,
2693 AC_VERB_SET_POWER_STATE, parm);
2694 snd_hda_codec_write(codec, 0x24, 0,
2695 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002696 } else if (codec->vendor_id == 0x11064397) {
2697 /* PW7(23h), SW2(27h), AOW2(25h) */
2698 parm = AC_PWRST_D3;
2699 set_pin_power_state(codec, 0x23, &parm);
2700 if (spec->smart51_enabled)
2701 set_pin_power_state(codec, 0x1a, &parm);
2702 snd_hda_codec_write(codec, 0x27, 0,
2703 AC_VERB_SET_POWER_STATE, parm);
2704 snd_hda_codec_write(codec, 0x25, 0,
2705 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002706 }
2707
2708 /* PW 3/4/7 (1ch/1dh/23h) */
2709 parm = AC_PWRST_D3;
2710 /* force to D0 for internal Speaker */
2711 set_pin_power_state(codec, 0x1c, &parm);
2712 set_pin_power_state(codec, 0x1d, &parm);
2713 if (is_8ch)
2714 set_pin_power_state(codec, 0x23, &parm);
2715
2716 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2717 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2718 imux_is_smixer ? AC_PWRST_D0 : parm);
2719 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2720 if (is_8ch) {
2721 snd_hda_codec_write(codec, 0x25, 0,
2722 AC_VERB_SET_POWER_STATE, parm);
2723 snd_hda_codec_write(codec, 0x27, 0,
2724 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002725 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2726 snd_hda_codec_write(codec, 0x25, 0,
2727 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002728}
2729
Lydia Wang518bf3b2009-10-10 19:07:29 +08002730static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002731static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002732{
2733 struct via_spec *spec;
2734 int err;
2735
Lydia Wang518bf3b2009-10-10 19:07:29 +08002736 if (get_codec_type(codec) == VT1708BCE)
2737 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002738
Josepch Chanf7278fd2007-12-13 16:40:40 +01002739 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002740 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002741 if (spec == NULL)
2742 return -ENOMEM;
2743
Takashi Iwai620e2b22011-06-17 17:19:19 +02002744 spec->aa_mix_nid = 0x16;
2745
Josepch Chanf7278fd2007-12-13 16:40:40 +01002746 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002747 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002748 if (err < 0) {
2749 via_free(codec);
2750 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002751 }
2752
Josepch Chanf7278fd2007-12-13 16:40:40 +01002753 codec->patch_ops = via_patch_ops;
2754
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002755 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2756
Josepch Chanf7278fd2007-12-13 16:40:40 +01002757 return 0;
2758}
2759
Harald Welted949cac2008-09-09 15:56:01 +08002760/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002761static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002762 /* Enable Mic Boost Volume backdoor */
2763 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002764 /* don't bybass mixer */
2765 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002766 { }
2767};
2768
Takashi Iwai9da29272009-05-07 16:31:14 +02002769/* fill out digital output widgets; one for master and one for slave outputs */
2770static void fill_dig_outs(struct hda_codec *codec)
2771{
2772 struct via_spec *spec = codec->spec;
2773 int i;
2774
2775 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2776 hda_nid_t nid;
2777 int conn;
2778
2779 nid = spec->autocfg.dig_out_pins[i];
2780 if (!nid)
2781 continue;
2782 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2783 if (conn < 1)
2784 continue;
2785 if (!spec->multiout.dig_out_nid)
2786 spec->multiout.dig_out_nid = nid;
2787 else {
2788 spec->slave_dig_outs[0] = nid;
2789 break; /* at most two dig outs */
2790 }
2791 }
2792}
2793
Takashi Iwai12daef62011-06-18 17:45:49 +02002794static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002795{
2796 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002797 hda_nid_t dig_nid;
2798 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002799
Takashi Iwai12daef62011-06-18 17:45:49 +02002800 if (!spec->autocfg.dig_in_pin)
2801 return;
Harald Welted949cac2008-09-09 15:56:01 +08002802
Takashi Iwai12daef62011-06-18 17:45:49 +02002803 dig_nid = codec->start_nid;
2804 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2805 unsigned int wcaps = get_wcaps(codec, dig_nid);
2806 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2807 continue;
2808 if (!(wcaps & AC_WCAP_DIGITAL))
2809 continue;
2810 if (!(wcaps & AC_WCAP_CONN_LIST))
2811 continue;
2812 err = get_connection_index(codec, dig_nid,
2813 spec->autocfg.dig_in_pin);
2814 if (err >= 0) {
2815 spec->dig_in_nid = dig_nid;
2816 break;
2817 }
2818 }
Harald Welted949cac2008-09-09 15:56:01 +08002819}
2820
Lydia Wang6369bcf2009-10-10 19:08:31 +08002821static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2822 int offset, int num_steps, int step_size)
2823{
2824 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2825 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2826 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2827 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2828 (0 << AC_AMPCAP_MUTE_SHIFT));
2829}
2830
Harald Welted949cac2008-09-09 15:56:01 +08002831static int patch_vt1708S(struct hda_codec *codec)
2832{
2833 struct via_spec *spec;
2834 int err;
2835
2836 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002837 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002838 if (spec == NULL)
2839 return -ENOMEM;
2840
Takashi Iwai620e2b22011-06-17 17:19:19 +02002841 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002842 override_mic_boost(codec, 0x1a, 0, 3, 40);
2843 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002844
Harald Welted949cac2008-09-09 15:56:01 +08002845 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002846 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002847 if (err < 0) {
2848 via_free(codec);
2849 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002850 }
2851
Takashi Iwai096a8852011-06-20 12:09:02 +02002852 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002853
Harald Welted949cac2008-09-09 15:56:01 +08002854 codec->patch_ops = via_patch_ops;
2855
Lydia Wang518bf3b2009-10-10 19:07:29 +08002856 /* correct names for VT1708BCE */
2857 if (get_codec_type(codec) == VT1708BCE) {
2858 kfree(codec->chip_name);
2859 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2860 snprintf(codec->bus->card->mixername,
2861 sizeof(codec->bus->card->mixername),
2862 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002863 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002864 /* correct names for VT1705 */
2865 if (codec->vendor_id == 0x11064397) {
2866 kfree(codec->chip_name);
2867 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2868 snprintf(codec->bus->card->mixername,
2869 sizeof(codec->bus->card->mixername),
2870 "%s %s", codec->vendor_name, codec->chip_name);
2871 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002872 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002873 return 0;
2874}
2875
2876/* Patch for VT1702 */
2877
Takashi Iwai096a8852011-06-20 12:09:02 +02002878static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002879 /* mixer enable */
2880 {0x1, 0xF88, 0x3},
2881 /* GPIO 0~2 */
2882 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002883 { }
2884};
2885
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002886static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2887{
2888 int imux_is_smixer =
2889 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2890 unsigned int parm;
2891 /* inputs */
2892 /* PW 1/2/5 (14h/15h/18h) */
2893 parm = AC_PWRST_D3;
2894 set_pin_power_state(codec, 0x14, &parm);
2895 set_pin_power_state(codec, 0x15, &parm);
2896 set_pin_power_state(codec, 0x18, &parm);
2897 if (imux_is_smixer)
2898 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2899 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2900 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2901 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2902 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2903 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2904
2905 /* outputs */
2906 /* PW 3/4 (16h/17h) */
2907 parm = AC_PWRST_D3;
2908 set_pin_power_state(codec, 0x17, &parm);
2909 set_pin_power_state(codec, 0x16, &parm);
2910 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2911 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2912 imux_is_smixer ? AC_PWRST_D0 : parm);
2913 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2914 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2915}
2916
Harald Welted949cac2008-09-09 15:56:01 +08002917static int patch_vt1702(struct hda_codec *codec)
2918{
2919 struct via_spec *spec;
2920 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002921
2922 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002923 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002924 if (spec == NULL)
2925 return -ENOMEM;
2926
Takashi Iwai620e2b22011-06-17 17:19:19 +02002927 spec->aa_mix_nid = 0x1a;
2928
Takashi Iwai12daef62011-06-18 17:45:49 +02002929 /* limit AA path volume to 0 dB */
2930 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2931 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2932 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2933 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2934 (1 << AC_AMPCAP_MUTE_SHIFT));
2935
Harald Welted949cac2008-09-09 15:56:01 +08002936 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002937 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002938 if (err < 0) {
2939 via_free(codec);
2940 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002941 }
2942
Takashi Iwai096a8852011-06-20 12:09:02 +02002943 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002944
Harald Welted949cac2008-09-09 15:56:01 +08002945 codec->patch_ops = via_patch_ops;
2946
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002947 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002948 return 0;
2949}
2950
Lydia Wangeb7188c2009-10-10 19:08:34 +08002951/* Patch for VT1718S */
2952
Takashi Iwai096a8852011-06-20 12:09:02 +02002953static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002954 /* Enable MW0 adjust Gain 5 */
2955 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002956 /* Enable Boost Volume backdoor */
2957 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002958
Lydia Wangeb7188c2009-10-10 19:08:34 +08002959 { }
2960};
2961
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002962static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2963{
2964 struct via_spec *spec = codec->spec;
2965 int imux_is_smixer;
2966 unsigned int parm;
2967 /* MUX6 (1eh) = stereo mixer */
2968 imux_is_smixer =
2969 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2970 /* inputs */
2971 /* PW 5/6/7 (29h/2ah/2bh) */
2972 parm = AC_PWRST_D3;
2973 set_pin_power_state(codec, 0x29, &parm);
2974 set_pin_power_state(codec, 0x2a, &parm);
2975 set_pin_power_state(codec, 0x2b, &parm);
2976 if (imux_is_smixer)
2977 parm = AC_PWRST_D0;
2978 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2979 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2980 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2981 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2982 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2983
2984 /* outputs */
2985 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2986 parm = AC_PWRST_D3;
2987 set_pin_power_state(codec, 0x27, &parm);
2988 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2989 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2990
2991 /* PW2 (26h), AOW2 (ah) */
2992 parm = AC_PWRST_D3;
2993 set_pin_power_state(codec, 0x26, &parm);
2994 if (spec->smart51_enabled)
2995 set_pin_power_state(codec, 0x2b, &parm);
2996 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2997
2998 /* PW0 (24h), AOW0 (8h) */
2999 parm = AC_PWRST_D3;
3000 set_pin_power_state(codec, 0x24, &parm);
3001 if (!spec->hp_independent_mode) /* check for redirected HP */
3002 set_pin_power_state(codec, 0x28, &parm);
3003 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3004 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3005 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3006 imux_is_smixer ? AC_PWRST_D0 : parm);
3007
3008 /* PW1 (25h), AOW1 (9h) */
3009 parm = AC_PWRST_D3;
3010 set_pin_power_state(codec, 0x25, &parm);
3011 if (spec->smart51_enabled)
3012 set_pin_power_state(codec, 0x2a, &parm);
3013 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3014
3015 if (spec->hp_independent_mode) {
3016 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3017 parm = AC_PWRST_D3;
3018 set_pin_power_state(codec, 0x28, &parm);
3019 snd_hda_codec_write(codec, 0x1b, 0,
3020 AC_VERB_SET_POWER_STATE, parm);
3021 snd_hda_codec_write(codec, 0x34, 0,
3022 AC_VERB_SET_POWER_STATE, parm);
3023 snd_hda_codec_write(codec, 0xc, 0,
3024 AC_VERB_SET_POWER_STATE, parm);
3025 }
3026}
3027
Takashi Iwai30b45032011-07-11 17:05:04 +02003028/* Add a connection to the primary DAC from AA-mixer for some codecs
3029 * This isn't listed from the raw info, but the chip has a secret connection.
3030 */
3031static int add_secret_dac_path(struct hda_codec *codec)
3032{
3033 struct via_spec *spec = codec->spec;
3034 int i, nums;
3035 hda_nid_t conn[8];
3036 hda_nid_t nid;
3037
3038 if (!spec->aa_mix_nid)
3039 return 0;
3040 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3041 ARRAY_SIZE(conn) - 1);
3042 for (i = 0; i < nums; i++) {
3043 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3044 return 0;
3045 }
3046
3047 /* find the primary DAC and add to the connection list */
3048 nid = codec->start_nid;
3049 for (i = 0; i < codec->num_nodes; i++, nid++) {
3050 unsigned int caps = get_wcaps(codec, nid);
3051 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3052 !(caps & AC_WCAP_DIGITAL)) {
3053 conn[nums++] = nid;
3054 return snd_hda_override_conn_list(codec,
3055 spec->aa_mix_nid,
3056 nums, conn);
3057 }
3058 }
3059 return 0;
3060}
3061
3062
Lydia Wangeb7188c2009-10-10 19:08:34 +08003063static int patch_vt1718S(struct hda_codec *codec)
3064{
3065 struct via_spec *spec;
3066 int err;
3067
3068 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003069 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003070 if (spec == NULL)
3071 return -ENOMEM;
3072
Takashi Iwai620e2b22011-06-17 17:19:19 +02003073 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003074 override_mic_boost(codec, 0x2b, 0, 3, 40);
3075 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003076 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003077
Lydia Wangeb7188c2009-10-10 19:08:34 +08003078 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003079 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003080 if (err < 0) {
3081 via_free(codec);
3082 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003083 }
3084
Takashi Iwai096a8852011-06-20 12:09:02 +02003085 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003086
Lydia Wangeb7188c2009-10-10 19:08:34 +08003087 codec->patch_ops = via_patch_ops;
3088
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003089 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3090
Lydia Wangeb7188c2009-10-10 19:08:34 +08003091 return 0;
3092}
Lydia Wangf3db4232009-10-10 19:08:41 +08003093
3094/* Patch for VT1716S */
3095
3096static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3097 struct snd_ctl_elem_info *uinfo)
3098{
3099 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3100 uinfo->count = 1;
3101 uinfo->value.integer.min = 0;
3102 uinfo->value.integer.max = 1;
3103 return 0;
3104}
3105
3106static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3107 struct snd_ctl_elem_value *ucontrol)
3108{
3109 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3110 int index = 0;
3111
3112 index = snd_hda_codec_read(codec, 0x26, 0,
3113 AC_VERB_GET_CONNECT_SEL, 0);
3114 if (index != -1)
3115 *ucontrol->value.integer.value = index;
3116
3117 return 0;
3118}
3119
3120static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3121 struct snd_ctl_elem_value *ucontrol)
3122{
3123 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3124 struct via_spec *spec = codec->spec;
3125 int index = *ucontrol->value.integer.value;
3126
3127 snd_hda_codec_write(codec, 0x26, 0,
3128 AC_VERB_SET_CONNECT_SEL, index);
3129 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003130 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003131 return 1;
3132}
3133
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003134static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003135 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3136 {
3137 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3138 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003139 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003140 .count = 1,
3141 .info = vt1716s_dmic_info,
3142 .get = vt1716s_dmic_get,
3143 .put = vt1716s_dmic_put,
3144 },
3145 {} /* end */
3146};
3147
3148
3149/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003150static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003151 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3152 { } /* end */
3153};
3154
Takashi Iwai096a8852011-06-20 12:09:02 +02003155static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003156 /* Enable Boost Volume backdoor */
3157 {0x1, 0xf8a, 0x80},
3158 /* don't bybass mixer */
3159 {0x1, 0xf88, 0xc0},
3160 /* Enable mono output */
3161 {0x1, 0xf90, 0x08},
3162 { }
3163};
3164
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003165static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3166{
3167 struct via_spec *spec = codec->spec;
3168 int imux_is_smixer;
3169 unsigned int parm;
3170 unsigned int mono_out, present;
3171 /* SW0 (17h) = stereo mixer */
3172 imux_is_smixer =
3173 (snd_hda_codec_read(codec, 0x17, 0,
3174 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3175 /* inputs */
3176 /* PW 1/2/5 (1ah/1bh/1eh) */
3177 parm = AC_PWRST_D3;
3178 set_pin_power_state(codec, 0x1a, &parm);
3179 set_pin_power_state(codec, 0x1b, &parm);
3180 set_pin_power_state(codec, 0x1e, &parm);
3181 if (imux_is_smixer)
3182 parm = AC_PWRST_D0;
3183 /* SW0 (17h), AIW0(13h) */
3184 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3185 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3186
3187 parm = AC_PWRST_D3;
3188 set_pin_power_state(codec, 0x1e, &parm);
3189 /* PW11 (22h) */
3190 if (spec->dmic_enabled)
3191 set_pin_power_state(codec, 0x22, &parm);
3192 else
3193 snd_hda_codec_write(codec, 0x22, 0,
3194 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3195
3196 /* SW2(26h), AIW1(14h) */
3197 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3198 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3199
3200 /* outputs */
3201 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3202 parm = AC_PWRST_D3;
3203 set_pin_power_state(codec, 0x19, &parm);
3204 /* Smart 5.1 PW2(1bh) */
3205 if (spec->smart51_enabled)
3206 set_pin_power_state(codec, 0x1b, &parm);
3207 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3208 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3209
3210 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3211 parm = AC_PWRST_D3;
3212 set_pin_power_state(codec, 0x23, &parm);
3213 /* Smart 5.1 PW1(1ah) */
3214 if (spec->smart51_enabled)
3215 set_pin_power_state(codec, 0x1a, &parm);
3216 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3217
3218 /* Smart 5.1 PW5(1eh) */
3219 if (spec->smart51_enabled)
3220 set_pin_power_state(codec, 0x1e, &parm);
3221 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3222
3223 /* Mono out */
3224 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3225 present = snd_hda_jack_detect(codec, 0x1c);
3226
3227 if (present)
3228 mono_out = 0;
3229 else {
3230 present = snd_hda_jack_detect(codec, 0x1d);
3231 if (!spec->hp_independent_mode && present)
3232 mono_out = 0;
3233 else
3234 mono_out = 1;
3235 }
3236 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3237 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3238 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3239 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3240
3241 /* PW 3/4 (1ch/1dh) */
3242 parm = AC_PWRST_D3;
3243 set_pin_power_state(codec, 0x1c, &parm);
3244 set_pin_power_state(codec, 0x1d, &parm);
3245 /* HP Independent Mode, power on AOW3 */
3246 if (spec->hp_independent_mode)
3247 snd_hda_codec_write(codec, 0x25, 0,
3248 AC_VERB_SET_POWER_STATE, parm);
3249
3250 /* force to D0 for internal Speaker */
3251 /* MW0 (16h), AOW0 (10h) */
3252 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3253 imux_is_smixer ? AC_PWRST_D0 : parm);
3254 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3255 mono_out ? AC_PWRST_D0 : parm);
3256}
3257
Lydia Wangf3db4232009-10-10 19:08:41 +08003258static int patch_vt1716S(struct hda_codec *codec)
3259{
3260 struct via_spec *spec;
3261 int err;
3262
3263 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003264 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003265 if (spec == NULL)
3266 return -ENOMEM;
3267
Takashi Iwai620e2b22011-06-17 17:19:19 +02003268 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003269 override_mic_boost(codec, 0x1a, 0, 3, 40);
3270 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003271
Lydia Wangf3db4232009-10-10 19:08:41 +08003272 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003273 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003274 if (err < 0) {
3275 via_free(codec);
3276 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003277 }
3278
Takashi Iwai096a8852011-06-20 12:09:02 +02003279 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003280
Lydia Wangf3db4232009-10-10 19:08:41 +08003281 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3282 spec->num_mixers++;
3283
3284 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3285
3286 codec->patch_ops = via_patch_ops;
3287
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003288 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003289 return 0;
3290}
Lydia Wang25eaba22009-10-10 19:08:43 +08003291
3292/* for vt2002P */
3293
Takashi Iwai096a8852011-06-20 12:09:02 +02003294static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003295 /* Class-D speaker related verbs */
3296 {0x1, 0xfe0, 0x4},
3297 {0x1, 0xfe9, 0x80},
3298 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003299 /* Enable Boost Volume backdoor */
3300 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003301 /* Enable AOW0 to MW9 */
3302 {0x1, 0xfb8, 0x88},
3303 { }
3304};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003305
Takashi Iwai096a8852011-06-20 12:09:02 +02003306static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003307 /* Enable Boost Volume backdoor */
3308 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003309 /* Enable AOW0 to MW9 */
3310 {0x1, 0xfb8, 0x88},
3311 { }
3312};
Lydia Wang25eaba22009-10-10 19:08:43 +08003313
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003314static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3315{
3316 struct via_spec *spec = codec->spec;
3317 int imux_is_smixer;
3318 unsigned int parm;
3319 unsigned int present;
3320 /* MUX9 (1eh) = stereo mixer */
3321 imux_is_smixer =
3322 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3323 /* inputs */
3324 /* PW 5/6/7 (29h/2ah/2bh) */
3325 parm = AC_PWRST_D3;
3326 set_pin_power_state(codec, 0x29, &parm);
3327 set_pin_power_state(codec, 0x2a, &parm);
3328 set_pin_power_state(codec, 0x2b, &parm);
3329 parm = AC_PWRST_D0;
3330 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3331 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3332 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3333 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3334 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3335
3336 /* outputs */
3337 /* AOW0 (8h)*/
3338 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3339
Lydia Wang118909562011-03-23 17:57:34 +08003340 if (spec->codec_type == VT1802) {
3341 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3342 parm = AC_PWRST_D3;
3343 set_pin_power_state(codec, 0x28, &parm);
3344 snd_hda_codec_write(codec, 0x18, 0,
3345 AC_VERB_SET_POWER_STATE, parm);
3346 snd_hda_codec_write(codec, 0x38, 0,
3347 AC_VERB_SET_POWER_STATE, parm);
3348 } else {
3349 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3350 parm = AC_PWRST_D3;
3351 set_pin_power_state(codec, 0x26, &parm);
3352 snd_hda_codec_write(codec, 0x1c, 0,
3353 AC_VERB_SET_POWER_STATE, parm);
3354 snd_hda_codec_write(codec, 0x37, 0,
3355 AC_VERB_SET_POWER_STATE, parm);
3356 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003357
Lydia Wang118909562011-03-23 17:57:34 +08003358 if (spec->codec_type == VT1802) {
3359 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3360 parm = AC_PWRST_D3;
3361 set_pin_power_state(codec, 0x25, &parm);
3362 snd_hda_codec_write(codec, 0x15, 0,
3363 AC_VERB_SET_POWER_STATE, parm);
3364 snd_hda_codec_write(codec, 0x35, 0,
3365 AC_VERB_SET_POWER_STATE, parm);
3366 } else {
3367 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3368 parm = AC_PWRST_D3;
3369 set_pin_power_state(codec, 0x25, &parm);
3370 snd_hda_codec_write(codec, 0x19, 0,
3371 AC_VERB_SET_POWER_STATE, parm);
3372 snd_hda_codec_write(codec, 0x35, 0,
3373 AC_VERB_SET_POWER_STATE, parm);
3374 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003375
3376 if (spec->hp_independent_mode)
3377 snd_hda_codec_write(codec, 0x9, 0,
3378 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3379
3380 /* Class-D */
3381 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3382 present = snd_hda_jack_detect(codec, 0x25);
3383
3384 parm = AC_PWRST_D3;
3385 set_pin_power_state(codec, 0x24, &parm);
3386 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003387 if (spec->codec_type == VT1802)
3388 snd_hda_codec_write(codec, 0x14, 0,
3389 AC_VERB_SET_POWER_STATE, parm);
3390 else
3391 snd_hda_codec_write(codec, 0x18, 0,
3392 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003393 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3394
3395 /* Mono Out */
3396 present = snd_hda_jack_detect(codec, 0x26);
3397
3398 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003399 if (spec->codec_type == VT1802) {
3400 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3401 snd_hda_codec_write(codec, 0x33, 0,
3402 AC_VERB_SET_POWER_STATE, parm);
3403 snd_hda_codec_write(codec, 0x1c, 0,
3404 AC_VERB_SET_POWER_STATE, parm);
3405 snd_hda_codec_write(codec, 0x3c, 0,
3406 AC_VERB_SET_POWER_STATE, parm);
3407 } else {
3408 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3409 snd_hda_codec_write(codec, 0x31, 0,
3410 AC_VERB_SET_POWER_STATE, parm);
3411 snd_hda_codec_write(codec, 0x17, 0,
3412 AC_VERB_SET_POWER_STATE, parm);
3413 snd_hda_codec_write(codec, 0x3b, 0,
3414 AC_VERB_SET_POWER_STATE, parm);
3415 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003416 /* MW9 (21h) */
3417 if (imux_is_smixer || !is_aa_path_mute(codec))
3418 snd_hda_codec_write(codec, 0x21, 0,
3419 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3420 else
3421 snd_hda_codec_write(codec, 0x21, 0,
3422 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3423}
Lydia Wang25eaba22009-10-10 19:08:43 +08003424
3425/* patch for vt2002P */
3426static int patch_vt2002P(struct hda_codec *codec)
3427{
3428 struct via_spec *spec;
3429 int err;
3430
3431 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003432 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003433 if (spec == NULL)
3434 return -ENOMEM;
3435
Takashi Iwai620e2b22011-06-17 17:19:19 +02003436 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003437 override_mic_boost(codec, 0x2b, 0, 3, 40);
3438 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003439 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003440
Lydia Wang25eaba22009-10-10 19:08:43 +08003441 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003442 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003443 if (err < 0) {
3444 via_free(codec);
3445 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003446 }
3447
Lydia Wang118909562011-03-23 17:57:34 +08003448 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003449 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003450 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003451 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003452
Lydia Wang25eaba22009-10-10 19:08:43 +08003453 codec->patch_ops = via_patch_ops;
3454
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003455 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003456 return 0;
3457}
Lydia Wangab6734e2009-10-10 19:08:46 +08003458
3459/* for vt1812 */
3460
Takashi Iwai096a8852011-06-20 12:09:02 +02003461static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003462 /* Enable Boost Volume backdoor */
3463 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003464 /* Enable AOW0 to MW9 */
3465 {0x1, 0xfb8, 0xa8},
3466 { }
3467};
3468
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003469static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3470{
3471 struct via_spec *spec = codec->spec;
3472 int imux_is_smixer =
3473 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3474 unsigned int parm;
3475 unsigned int present;
3476 /* MUX10 (1eh) = stereo mixer */
3477 imux_is_smixer =
3478 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3479 /* inputs */
3480 /* PW 5/6/7 (29h/2ah/2bh) */
3481 parm = AC_PWRST_D3;
3482 set_pin_power_state(codec, 0x29, &parm);
3483 set_pin_power_state(codec, 0x2a, &parm);
3484 set_pin_power_state(codec, 0x2b, &parm);
3485 parm = AC_PWRST_D0;
3486 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3487 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3488 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3489 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3490 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3491
3492 /* outputs */
3493 /* AOW0 (8h)*/
3494 snd_hda_codec_write(codec, 0x8, 0,
3495 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3496
3497 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3498 parm = AC_PWRST_D3;
3499 set_pin_power_state(codec, 0x28, &parm);
3500 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3501 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3502
3503 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3504 parm = AC_PWRST_D3;
3505 set_pin_power_state(codec, 0x25, &parm);
3506 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3507 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3508 if (spec->hp_independent_mode)
3509 snd_hda_codec_write(codec, 0x9, 0,
3510 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3511
3512 /* Internal Speaker */
3513 /* PW0 (24h), MW0(14h), MUX0(34h) */
3514 present = snd_hda_jack_detect(codec, 0x25);
3515
3516 parm = AC_PWRST_D3;
3517 set_pin_power_state(codec, 0x24, &parm);
3518 if (present) {
3519 snd_hda_codec_write(codec, 0x14, 0,
3520 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3521 snd_hda_codec_write(codec, 0x34, 0,
3522 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3523 } else {
3524 snd_hda_codec_write(codec, 0x14, 0,
3525 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3526 snd_hda_codec_write(codec, 0x34, 0,
3527 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3528 }
3529
3530
3531 /* Mono Out */
3532 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3533 present = snd_hda_jack_detect(codec, 0x28);
3534
3535 parm = AC_PWRST_D3;
3536 set_pin_power_state(codec, 0x31, &parm);
3537 if (present) {
3538 snd_hda_codec_write(codec, 0x1c, 0,
3539 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3540 snd_hda_codec_write(codec, 0x3c, 0,
3541 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3542 snd_hda_codec_write(codec, 0x3e, 0,
3543 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3544 } else {
3545 snd_hda_codec_write(codec, 0x1c, 0,
3546 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3547 snd_hda_codec_write(codec, 0x3c, 0,
3548 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3549 snd_hda_codec_write(codec, 0x3e, 0,
3550 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3551 }
3552
3553 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3554 parm = AC_PWRST_D3;
3555 set_pin_power_state(codec, 0x33, &parm);
3556 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3557 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3558
3559}
Lydia Wangab6734e2009-10-10 19:08:46 +08003560
3561/* patch for vt1812 */
3562static int patch_vt1812(struct hda_codec *codec)
3563{
3564 struct via_spec *spec;
3565 int err;
3566
3567 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003568 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003569 if (spec == NULL)
3570 return -ENOMEM;
3571
Takashi Iwai620e2b22011-06-17 17:19:19 +02003572 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003573 override_mic_boost(codec, 0x2b, 0, 3, 40);
3574 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003575 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003576
Lydia Wangab6734e2009-10-10 19:08:46 +08003577 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003578 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003579 if (err < 0) {
3580 via_free(codec);
3581 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003582 }
3583
Takashi Iwai096a8852011-06-20 12:09:02 +02003584 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003585
Lydia Wangab6734e2009-10-10 19:08:46 +08003586 codec->patch_ops = via_patch_ops;
3587
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003588 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003589 return 0;
3590}
3591
Joseph Chanc577b8a2006-11-29 15:29:40 +01003592/*
3593 * patch entries
3594 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003595static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003596 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3597 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3598 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3599 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3600 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003601 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003602 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003603 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003604 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003605 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003606 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003607 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003608 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003609 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003610 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003611 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003612 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003613 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003614 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003615 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003616 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003617 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003618 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003619 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003620 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003621 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003622 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003623 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003624 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003625 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003626 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003627 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003628 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003629 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003630 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003631 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003632 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003633 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003634 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003635 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003636 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003637 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003638 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003639 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003640 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003641 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003642 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003643 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003644 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003645 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003646 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003647 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003648 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003649 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003650 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003651 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003652 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003653 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003654 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003655 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003656 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003657 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003658 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003659 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003660 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003661 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003662 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003663 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003664 { .id = 0x11060428, .name = "VT1718S",
3665 .patch = patch_vt1718S},
3666 { .id = 0x11064428, .name = "VT1718S",
3667 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003668 { .id = 0x11060441, .name = "VT2020",
3669 .patch = patch_vt1718S},
3670 { .id = 0x11064441, .name = "VT1828S",
3671 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003672 { .id = 0x11060433, .name = "VT1716S",
3673 .patch = patch_vt1716S},
3674 { .id = 0x1106a721, .name = "VT1716S",
3675 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003676 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3677 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003678 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003679 { .id = 0x11060440, .name = "VT1818S",
3680 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003681 { .id = 0x11060446, .name = "VT1802",
3682 .patch = patch_vt2002P},
3683 { .id = 0x11068446, .name = "VT1802",
3684 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003685 {} /* terminator */
3686};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003687
3688MODULE_ALIAS("snd-hda-codec-id:1106*");
3689
3690static struct hda_codec_preset_list via_list = {
3691 .preset = snd_hda_preset_via,
3692 .owner = THIS_MODULE,
3693};
3694
3695MODULE_LICENSE("GPL");
3696MODULE_DESCRIPTION("VIA HD-audio codec");
3697
3698static int __init patch_via_init(void)
3699{
3700 return snd_hda_add_codec_preset(&via_list);
3701}
3702
3703static void __exit patch_via_exit(void)
3704{
3705 snd_hda_delete_codec_preset(&via_list);
3706}
3707
3708module_init(patch_via_init)
3709module_exit(patch_via_exit)