blob: 9cb14b42dfff48f890ee10871dea7fffb14b7054 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040026#include <linux/module.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010027
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <sound/core.h>
29#include "hda_codec.h"
30#include "hda_local.h"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010031#include "hda_beep.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020032#include "hda_jack.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020034struct ad198x_spec {
Takashi Iwai498f5b12011-05-02 11:33:15 +020035 const struct snd_kcontrol_new *mixers[6];
Takashi Iwai985be542005-11-02 18:26:49 +010036 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010037 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Raymond Yau28220842011-02-08 19:58:25 +080038 const struct hda_verb *init_verbs[6]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010039 * don't forget NULL termination!
40 */
41 unsigned int num_init_verbs;
42
43 /* playback */
44 struct hda_multi_out multiout; /* playback set-up
45 * max_channels, dacs must be set
46 * dig_out_nid and hp_nid are optional
47 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010048 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020049 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010050
Takashi Iwai498f5b12011-05-02 11:33:15 +020051 const hda_nid_t *alt_dac_nid;
52 const struct hda_pcm_stream *stream_analog_alt_playback;
Raymond Yau34588702011-09-23 19:03:25 +080053 int independent_hp;
54 int num_active_streams;
Raymond Yauc66ddf32011-01-17 11:19:03 +010055
Takashi Iwai985be542005-11-02 18:26:49 +010056 /* capture */
57 unsigned int num_adc_nids;
Takashi Iwai498f5b12011-05-02 11:33:15 +020058 const hda_nid_t *adc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010059 hda_nid_t dig_in_nid; /* digital-in NID; optional */
60
61 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020062 const struct hda_input_mux *input_mux;
Takashi Iwai498f5b12011-05-02 11:33:15 +020063 const hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010064 unsigned int cur_mux[3];
65
66 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010067 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010068 int num_channel_mode;
69
70 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020071 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010072
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020073 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010074
75 /* dynamic controls, init_verbs and input_mux */
76 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020077 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010078 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020079 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020080
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010081 unsigned int jack_present: 1;
82 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
83 unsigned int inv_eapd: 1; /* inverted EAPD implementation */
84 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020085
Takashi Iwaicb53c622007-08-10 17:21:45 +020086#ifdef CONFIG_SND_HDA_POWER_SAVE
87 struct hda_loopback_check loopback;
88#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010089 /* for virtual master */
90 hda_nid_t vmaster_nid;
Takashi Iwaiea734962011-01-17 11:29:34 +010091 const char * const *slave_vols;
92 const char * const *slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093};
94
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020095/*
96 * input MUX handling (common part)
97 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +010098static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020099{
100 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
101 struct ad198x_spec *spec = codec->spec;
102
103 return snd_hda_input_mux_info(spec->input_mux, uinfo);
104}
105
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100106static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200107{
108 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
109 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100110 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200111
Takashi Iwai985be542005-11-02 18:26:49 +0100112 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200113 return 0;
114}
115
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100116static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200117{
118 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
119 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100120 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200121
122 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100123 spec->capsrc_nids[adc_idx],
124 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200125}
126
127/*
128 * initialization (common callbacks)
129 */
130static int ad198x_init(struct hda_codec *codec)
131{
132 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100133 int i;
134
135 for (i = 0; i < spec->num_init_verbs; i++)
136 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200137 return 0;
138}
139
Takashi Iwaiea734962011-01-17 11:29:34 +0100140static const char * const ad_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100141 "Front Playback Volume",
142 "Surround Playback Volume",
143 "Center Playback Volume",
144 "LFE Playback Volume",
145 "Side Playback Volume",
146 "Headphone Playback Volume",
147 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100148 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100149 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100150 NULL
151};
152
Takashi Iwaiea734962011-01-17 11:29:34 +0100153static const char * const ad_slave_sws[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100154 "Front Playback Switch",
155 "Surround Playback Switch",
156 "Center Playback Switch",
157 "LFE Playback Switch",
158 "Side Playback Switch",
159 "Headphone Playback Switch",
160 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100161 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100162 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100163 NULL
164};
165
Takashi Iwaiea734962011-01-17 11:29:34 +0100166static const char * const ad1988_6stack_fp_slave_vols[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100167 "Front Playback Volume",
168 "Surround Playback Volume",
169 "Center Playback Volume",
170 "LFE Playback Volume",
171 "Side Playback Volume",
172 "IEC958 Playback Volume",
173 NULL
174};
175
Takashi Iwaiea734962011-01-17 11:29:34 +0100176static const char * const ad1988_6stack_fp_slave_sws[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100177 "Front Playback Switch",
178 "Surround Playback Switch",
179 "Center Playback Switch",
180 "LFE Playback Switch",
181 "Side Playback Switch",
182 "IEC958 Playback Switch",
183 NULL
184};
Takashi Iwai603c4012008-07-30 15:01:44 +0200185static void ad198x_free_kctls(struct hda_codec *codec);
186
Takashi Iwai67d634c2009-11-16 15:35:59 +0100187#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100188/* additional beep mixers; the actual parameters are overwritten at build */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200189static const struct snd_kcontrol_new ad_beep_mixer[] = {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100190 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200191 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100192 { } /* end */
193};
194
Takashi Iwai498f5b12011-05-02 11:33:15 +0200195static const struct snd_kcontrol_new ad_beep2_mixer[] = {
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100196 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
197 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
198 { } /* end */
199};
200
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100201#define set_beep_amp(spec, nid, idx, dir) \
202 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100203#else
204#define set_beep_amp(spec, nid, idx, dir) /* NOP */
205#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100206
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200207static int ad198x_build_controls(struct hda_codec *codec)
208{
209 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100210 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100211 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200212 int err;
213
Takashi Iwai985be542005-11-02 18:26:49 +0100214 for (i = 0; i < spec->num_mixers; i++) {
215 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
216 if (err < 0)
217 return err;
218 }
219 if (spec->multiout.dig_out_nid) {
Stephen Warren74b654c2011-06-01 11:14:18 -0600220 err = snd_hda_create_spdif_out_ctls(codec,
221 spec->multiout.dig_out_nid,
222 spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100223 if (err < 0)
224 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100225 err = snd_hda_create_spdif_share_sw(codec,
226 &spec->multiout);
227 if (err < 0)
228 return err;
229 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100230 }
231 if (spec->dig_in_nid) {
232 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
233 if (err < 0)
234 return err;
235 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100236
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100237 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100238#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100239 if (spec->beep_amp) {
Takashi Iwai498f5b12011-05-02 11:33:15 +0200240 const struct snd_kcontrol_new *knew;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100241 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
242 for ( ; knew->name; knew++) {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100243 struct snd_kcontrol *kctl;
244 kctl = snd_ctl_new1(knew, codec);
245 if (!kctl)
246 return -ENOMEM;
247 kctl->private_value = spec->beep_amp;
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100248 err = snd_hda_ctl_add(codec, 0, kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100249 if (err < 0)
250 return err;
251 }
252 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100253#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100254
Takashi Iwai2134ea42008-01-10 16:53:55 +0100255 /* if we have no master control, let's create it */
256 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100257 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100258 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100259 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100260 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100261 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100262 (spec->slave_vols ?
263 spec->slave_vols : ad_slave_vols));
264 if (err < 0)
265 return err;
266 }
267 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
268 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
269 NULL,
270 (spec->slave_sws ?
271 spec->slave_sws : ad_slave_sws));
272 if (err < 0)
273 return err;
274 }
275
Takashi Iwai603c4012008-07-30 15:01:44 +0200276 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100277
278 /* assign Capture Source enums to NID */
279 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
280 if (!kctl)
281 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
282 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100283 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100284 if (err < 0)
285 return err;
286 }
287
288 /* assign IEC958 enums to NID */
289 kctl = snd_hda_find_mixer_ctl(codec,
290 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
291 if (kctl) {
292 err = snd_hda_add_nid(codec, kctl, 0,
293 spec->multiout.dig_out_nid);
294 if (err < 0)
295 return err;
296 }
297
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200298 return 0;
299}
300
Takashi Iwaicb53c622007-08-10 17:21:45 +0200301#ifdef CONFIG_SND_HDA_POWER_SAVE
302static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
303{
304 struct ad198x_spec *spec = codec->spec;
305 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
306}
307#endif
308
Raymond Yau34588702011-09-23 19:03:25 +0800309static void activate_ctl(struct hda_codec *codec, const char *name, int active)
310{
311 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
312 if (ctl) {
313 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
314 ctl->vd[0].access |= active ? 0 :
315 SNDRV_CTL_ELEM_ACCESS_INACTIVE;
316 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
317 ctl->vd[0].access |= active ?
318 SNDRV_CTL_ELEM_ACCESS_WRITE : 0;
319 snd_ctl_notify(codec->bus->card,
320 SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
321 }
322}
323
324static void set_stream_active(struct hda_codec *codec, bool active)
325{
326 struct ad198x_spec *spec = codec->spec;
327 if (active)
328 spec->num_active_streams++;
329 else
330 spec->num_active_streams--;
331 activate_ctl(codec, "Independent HP", spec->num_active_streams == 0);
332}
333
334static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol,
335 struct snd_ctl_elem_info *uinfo)
336{
337 static const char * const texts[] = { "OFF", "ON", NULL};
338 int index;
339 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
340 uinfo->count = 1;
341 uinfo->value.enumerated.items = 2;
342 index = uinfo->value.enumerated.item;
343 if (index >= 2)
344 index = 1;
345 strcpy(uinfo->value.enumerated.name, texts[index]);
346 return 0;
347}
348
349static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol,
350 struct snd_ctl_elem_value *ucontrol)
351{
352 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
353 struct ad198x_spec *spec = codec->spec;
354 ucontrol->value.enumerated.item[0] = spec->independent_hp;
355 return 0;
356}
357
358static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol,
359 struct snd_ctl_elem_value *ucontrol)
360{
361 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
362 struct ad198x_spec *spec = codec->spec;
363 unsigned int select = ucontrol->value.enumerated.item[0];
364 if (spec->independent_hp != select) {
365 spec->independent_hp = select;
366 if (spec->independent_hp)
367 spec->multiout.hp_nid = 0;
368 else
369 spec->multiout.hp_nid = spec->alt_dac_nid[0];
370 return 1;
371 }
372 return 0;
373}
374
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200375/*
376 * Analog playback callbacks
377 */
378static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
379 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100380 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200381{
382 struct ad198x_spec *spec = codec->spec;
Raymond Yau34588702011-09-23 19:03:25 +0800383 int err;
384 set_stream_active(codec, true);
385 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
Takashi Iwai9a081602008-02-12 18:37:26 +0100386 hinfo);
Raymond Yau34588702011-09-23 19:03:25 +0800387 if (err < 0) {
388 set_stream_active(codec, false);
389 return err;
390 }
391 return 0;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200392}
393
394static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
395 struct hda_codec *codec,
396 unsigned int stream_tag,
397 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100398 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200399{
400 struct ad198x_spec *spec = codec->spec;
401 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
402 format, substream);
403}
404
405static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
406 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100407 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200408{
409 struct ad198x_spec *spec = codec->spec;
410 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
411}
412
Raymond Yau34588702011-09-23 19:03:25 +0800413static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo,
414 struct hda_codec *codec,
415 struct snd_pcm_substream *substream)
416{
417 set_stream_active(codec, false);
418 return 0;
419}
420
421static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
422 struct hda_codec *codec,
423 struct snd_pcm_substream *substream)
424{
425 struct ad198x_spec *spec = codec->spec;
426 if (!spec->independent_hp)
427 return -EBUSY;
428 set_stream_active(codec, true);
429 return 0;
430}
431
432static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
433 struct hda_codec *codec,
434 struct snd_pcm_substream *substream)
435{
436 set_stream_active(codec, false);
437 return 0;
438}
439
Takashi Iwai498f5b12011-05-02 11:33:15 +0200440static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100441 .substreams = 1,
442 .channels_min = 2,
443 .channels_max = 2,
Raymond Yau34588702011-09-23 19:03:25 +0800444 .ops = {
445 .open = ad1988_alt_playback_pcm_open,
446 .close = ad1988_alt_playback_pcm_close
447 },
Raymond Yauc66ddf32011-01-17 11:19:03 +0100448};
449
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200450/*
451 * Digital out
452 */
453static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
454 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100455 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200456{
457 struct ad198x_spec *spec = codec->spec;
458 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
459}
460
461static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
462 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100463 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200464{
465 struct ad198x_spec *spec = codec->spec;
466 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
467}
468
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200469static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
470 struct hda_codec *codec,
471 unsigned int stream_tag,
472 unsigned int format,
473 struct snd_pcm_substream *substream)
474{
475 struct ad198x_spec *spec = codec->spec;
476 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
477 format, substream);
478}
479
Takashi Iwai9411e212009-02-13 11:32:28 +0100480static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
481 struct hda_codec *codec,
482 struct snd_pcm_substream *substream)
483{
484 struct ad198x_spec *spec = codec->spec;
485 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
486}
487
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200488/*
489 * Analog capture
490 */
491static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
492 struct hda_codec *codec,
493 unsigned int stream_tag,
494 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100495 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200496{
497 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100498 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
499 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200500 return 0;
501}
502
503static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
504 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100505 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200506{
507 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100508 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200509 return 0;
510}
511
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200512/*
513 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200514static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200515 .substreams = 1,
516 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100517 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200518 .nid = 0, /* fill later */
519 .ops = {
520 .open = ad198x_playback_pcm_open,
521 .prepare = ad198x_playback_pcm_prepare,
Raymond Yau34588702011-09-23 19:03:25 +0800522 .cleanup = ad198x_playback_pcm_cleanup,
523 .close = ad198x_playback_pcm_close
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200524 },
525};
526
Takashi Iwai498f5b12011-05-02 11:33:15 +0200527static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100528 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200529 .channels_min = 2,
530 .channels_max = 2,
531 .nid = 0, /* fill later */
532 .ops = {
533 .prepare = ad198x_capture_pcm_prepare,
534 .cleanup = ad198x_capture_pcm_cleanup
535 },
536};
537
Takashi Iwai498f5b12011-05-02 11:33:15 +0200538static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200539 .substreams = 1,
540 .channels_min = 2,
541 .channels_max = 2,
542 .nid = 0, /* fill later */
543 .ops = {
544 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200545 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100546 .prepare = ad198x_dig_playback_pcm_prepare,
547 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200548 },
549};
550
Takashi Iwai498f5b12011-05-02 11:33:15 +0200551static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100552 .substreams = 1,
553 .channels_min = 2,
554 .channels_max = 2,
555 /* NID is set in alc_build_pcms */
556};
557
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200558static int ad198x_build_pcms(struct hda_codec *codec)
559{
560 struct ad198x_spec *spec = codec->spec;
561 struct hda_pcm *info = spec->pcm_rec;
562
563 codec->num_pcms = 1;
564 codec->pcm_info = info;
565
566 info->name = "AD198x Analog";
567 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
568 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
569 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
570 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100571 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
572 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200573
574 if (spec->multiout.dig_out_nid) {
575 info++;
576 codec->num_pcms++;
577 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100578 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200579 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
580 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100581 if (spec->dig_in_nid) {
582 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
583 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
584 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200585 }
586
Raymond Yauc66ddf32011-01-17 11:19:03 +0100587 if (spec->alt_dac_nid && spec->stream_analog_alt_playback) {
588 codec->num_pcms++;
589 info = spec->pcm_rec + 2;
590 info->name = "AD198x Headphone";
591 info->pcm_type = HDA_PCM_TYPE_AUDIO;
592 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
593 *spec->stream_analog_alt_playback;
594 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
595 spec->alt_dac_nid[0];
596 }
597
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200598 return 0;
599}
600
Takashi Iwai603c4012008-07-30 15:01:44 +0200601static void ad198x_free_kctls(struct hda_codec *codec)
602{
603 struct ad198x_spec *spec = codec->spec;
604
605 if (spec->kctls.list) {
606 struct snd_kcontrol_new *kctl = spec->kctls.list;
607 int i;
608 for (i = 0; i < spec->kctls.used; i++)
609 kfree(kctl[i].name);
610 }
611 snd_array_free(&spec->kctls);
612}
613
Daniel T Chenea52bf22009-12-27 18:48:29 -0500614static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
615 hda_nid_t hp)
616{
617 struct ad198x_spec *spec = codec->spec;
Raymond Yaua01ef052011-06-01 15:09:48 +0800618 if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
619 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500620 !spec->inv_eapd ? 0x00 : 0x02);
Raymond Yaua01ef052011-06-01 15:09:48 +0800621 if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
622 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500623 !spec->inv_eapd ? 0x00 : 0x02);
624}
625
626static void ad198x_power_eapd(struct hda_codec *codec)
627{
628 /* We currently only handle front, HP */
629 switch (codec->vendor_id) {
630 case 0x11d41882:
631 case 0x11d4882a:
632 case 0x11d41884:
633 case 0x11d41984:
634 case 0x11d41883:
635 case 0x11d4184a:
636 case 0x11d4194a:
637 case 0x11d4194b:
Takashi Iwai4dffbe02011-06-03 10:05:02 +0200638 case 0x11d41988:
639 case 0x11d4198b:
640 case 0x11d4989a:
641 case 0x11d4989b:
Daniel T Chenea52bf22009-12-27 18:48:29 -0500642 ad198x_power_eapd_write(codec, 0x12, 0x11);
643 break;
644 case 0x11d41981:
645 case 0x11d41983:
646 ad198x_power_eapd_write(codec, 0x05, 0x06);
647 break;
648 case 0x11d41986:
649 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
650 break;
Daniel T Chenea52bf22009-12-27 18:48:29 -0500651 }
652}
653
Takashi Iwai0da26922011-04-26 15:18:33 +0200654static void ad198x_shutup(struct hda_codec *codec)
655{
656 snd_hda_shutup_pins(codec);
657 ad198x_power_eapd(codec);
658}
659
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200660static void ad198x_free(struct hda_codec *codec)
661{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100662 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100663
Takashi Iwai603c4012008-07-30 15:01:44 +0200664 if (!spec)
665 return;
666
Daniel T Chenea52bf22009-12-27 18:48:29 -0500667 ad198x_shutup(codec);
Takashi Iwai603c4012008-07-30 15:01:44 +0200668 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100669 kfree(spec);
670 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200671}
672
Takashi Iwai2a439522011-07-26 09:52:50 +0200673#ifdef CONFIG_PM
Daniel T Chenea52bf22009-12-27 18:48:29 -0500674static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
675{
676 ad198x_shutup(codec);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500677 return 0;
678}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500679#endif
680
Takashi Iwai498f5b12011-05-02 11:33:15 +0200681static const struct hda_codec_ops ad198x_patch_ops = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200682 .build_controls = ad198x_build_controls,
683 .build_pcms = ad198x_build_pcms,
684 .init = ad198x_init,
685 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200686#ifdef CONFIG_SND_HDA_POWER_SAVE
687 .check_power_status = ad198x_check_power_status,
688#endif
Takashi Iwai2a439522011-07-26 09:52:50 +0200689#ifdef CONFIG_PM
Daniel T Chenea52bf22009-12-27 18:48:29 -0500690 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500691#endif
692 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200693};
694
695
696/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100697 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100698 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100699 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200700#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100701
702static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
703 struct snd_ctl_elem_value *ucontrol)
704{
705 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
706 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100707 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100708 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
709 else
710 ucontrol->value.integer.value[0] = spec->cur_eapd;
711 return 0;
712}
713
714static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
715 struct snd_ctl_elem_value *ucontrol)
716{
717 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
718 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100719 hda_nid_t nid = kcontrol->private_value & 0xff;
720 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100721 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100722 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100723 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200724 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100725 return 0;
726 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200727 snd_hda_codec_write_cache(codec, nid,
728 0, AC_VERB_SET_EAPD_BTLENABLE,
729 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100730 return 1;
731}
732
Takashi Iwai9230d212006-03-13 13:49:49 +0100733static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
734 struct snd_ctl_elem_info *uinfo);
735static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
736 struct snd_ctl_elem_value *ucontrol);
737static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
738 struct snd_ctl_elem_value *ucontrol);
739
740
Takashi Iwai18a815d2006-03-01 19:54:39 +0100741/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200742 * AD1986A specific
743 */
744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745#define AD1986A_SPDIF_OUT 0x02
746#define AD1986A_FRONT_DAC 0x03
747#define AD1986A_SURR_DAC 0x04
748#define AD1986A_CLFE_DAC 0x05
749#define AD1986A_ADC 0x06
750
Takashi Iwai498f5b12011-05-02 11:33:15 +0200751static const hda_nid_t ad1986a_dac_nids[3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
753};
Takashi Iwai498f5b12011-05-02 11:33:15 +0200754static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
755static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Takashi Iwai498f5b12011-05-02 11:33:15 +0200757static const struct hda_input_mux ad1986a_capture_source = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 .num_items = 7,
759 .items = {
760 { "Mic", 0x0 },
761 { "CD", 0x1 },
762 { "Aux", 0x3 },
763 { "Line", 0x4 },
764 { "Mix", 0x5 },
765 { "Mono", 0x6 },
766 { "Phone", 0x7 },
767 },
768};
769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Takashi Iwai498f5b12011-05-02 11:33:15 +0200771static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200772 .ops = &snd_hda_bind_vol,
773 .values = {
774 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
775 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
776 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
777 0
778 },
779};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
Takashi Iwai498f5b12011-05-02 11:33:15 +0200781static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200782 .ops = &snd_hda_bind_sw,
783 .values = {
784 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
785 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
786 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
787 0
788 },
789};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 * mixers
793 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200794static const struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200795 /*
796 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
797 */
798 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
799 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
801 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
802 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
803 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
804 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
805 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
806 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
807 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
808 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
809 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
810 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
811 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
812 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
813 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
814 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
815 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
816 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
817 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100818 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
820 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
821 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
822 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
823 {
824 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
825 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200826 .info = ad198x_mux_enum_info,
827 .get = ad198x_mux_enum_get,
828 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 },
830 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
831 { } /* end */
832};
833
Takashi Iwai9230d212006-03-13 13:49:49 +0100834/* additional mixers for 3stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200835static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100836 {
837 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
838 .name = "Channel Mode",
839 .info = ad198x_ch_mode_info,
840 .get = ad198x_ch_mode_get,
841 .put = ad198x_ch_mode_put,
842 },
843 { } /* end */
844};
845
846/* laptop model - 2ch only */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200847static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
Takashi Iwai9230d212006-03-13 13:49:49 +0100848
Takashi Iwai20a45e82007-08-15 22:20:45 +0200849/* master controls both pins 0x1a and 0x1b */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200850static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200851 .ops = &snd_hda_bind_vol,
852 .values = {
853 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
854 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
855 0,
856 },
857};
858
Takashi Iwai498f5b12011-05-02 11:33:15 +0200859static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200860 .ops = &snd_hda_bind_sw,
861 .values = {
862 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
863 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
864 0,
865 },
866};
867
Takashi Iwai498f5b12011-05-02 11:33:15 +0200868static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100869 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
870 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200871 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
872 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100873 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
874 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
875 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
876 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
877 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
878 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
879 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
880 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100881 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100882 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100883 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
884 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
885 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
886 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
887 {
888 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
889 .name = "Capture Source",
890 .info = ad198x_mux_enum_info,
891 .get = ad198x_mux_enum_get,
892 .put = ad198x_mux_enum_put,
893 },
894 { } /* end */
895};
896
Takashi Iwai825aa972006-03-17 10:50:49 +0100897/* laptop-eapd model - 2ch only */
898
Takashi Iwai498f5b12011-05-02 11:33:15 +0200899static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100900 .num_items = 3,
901 .items = {
902 { "Mic", 0x0 },
903 { "Internal Mic", 0x4 },
904 { "Mix", 0x5 },
905 },
906};
907
Takashi Iwai498f5b12011-05-02 11:33:15 +0200908static const struct hda_input_mux ad1986a_automic_capture_source = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100909 .num_items = 2,
910 .items = {
911 { "Mic", 0x0 },
912 { "Mix", 0x5 },
913 },
914};
915
Takashi Iwai498f5b12011-05-02 11:33:15 +0200916static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200917 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
918 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200919 { } /* end */
920};
921
Takashi Iwai498f5b12011-05-02 11:33:15 +0200922static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100923 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
924 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100925 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
926 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100927 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100928 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
929 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
930 {
931 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
932 .name = "Capture Source",
933 .info = ad198x_mux_enum_info,
934 .get = ad198x_mux_enum_get,
935 .put = ad198x_mux_enum_put,
936 },
937 {
938 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
939 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100940 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100941 .info = ad198x_eapd_info,
942 .get = ad198x_eapd_get,
943 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100944 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100945 },
946 { } /* end */
947};
948
Takashi Iwai498f5b12011-05-02 11:33:15 +0200949static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
Takashi Iwai16d11a82009-06-24 14:07:53 +0200950 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
951 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100952 { } /* end */
953};
954
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100955/* re-connect the mic boost input according to the jack sensing */
956static void ad1986a_automic(struct hda_codec *codec)
957{
958 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100959 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100960 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
961 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100962 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100963}
964
965#define AD1986A_MIC_EVENT 0x36
966
967static void ad1986a_automic_unsol_event(struct hda_codec *codec,
968 unsigned int res)
969{
970 if ((res >> 26) != AD1986A_MIC_EVENT)
971 return;
972 ad1986a_automic(codec);
973}
974
975static int ad1986a_automic_init(struct hda_codec *codec)
976{
977 ad198x_init(codec);
978 ad1986a_automic(codec);
979 return 0;
980}
981
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200982/* laptop-automute - 2ch only */
983
984static void ad1986a_update_hp(struct hda_codec *codec)
985{
986 struct ad198x_spec *spec = codec->spec;
987 unsigned int mute;
988
989 if (spec->jack_present)
990 mute = HDA_AMP_MUTE; /* mute internal speaker */
991 else
992 /* unmute internal speaker if necessary */
993 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
994 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
995 HDA_AMP_MUTE, mute);
996}
997
998static void ad1986a_hp_automute(struct hda_codec *codec)
999{
1000 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001001
Takashi Iwaid56757a2009-11-18 08:00:14 +01001002 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +02001003 if (spec->inv_jack_detect)
1004 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001005 ad1986a_update_hp(codec);
1006}
1007
1008#define AD1986A_HP_EVENT 0x37
1009
1010static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
1011{
1012 if ((res >> 26) != AD1986A_HP_EVENT)
1013 return;
1014 ad1986a_hp_automute(codec);
1015}
1016
1017static int ad1986a_hp_init(struct hda_codec *codec)
1018{
1019 ad198x_init(codec);
1020 ad1986a_hp_automute(codec);
1021 return 0;
1022}
1023
1024/* bind hp and internal speaker mute (with plug check) */
1025static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1026 struct snd_ctl_elem_value *ucontrol)
1027{
1028 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1029 long *valp = ucontrol->value.integer.value;
1030 int change;
1031
1032 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
1033 HDA_AMP_MUTE,
1034 valp[0] ? 0 : HDA_AMP_MUTE);
1035 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
1036 HDA_AMP_MUTE,
1037 valp[1] ? 0 : HDA_AMP_MUTE);
1038 if (change)
1039 ad1986a_update_hp(codec);
1040 return change;
1041}
1042
Takashi Iwai498f5b12011-05-02 11:33:15 +02001043static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001044 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
1045 {
1046 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1047 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01001048 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001049 .info = snd_hda_mixer_amp_switch_info,
1050 .get = snd_hda_mixer_amp_switch_get,
1051 .put = ad1986a_hp_master_sw_put,
1052 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
1053 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001054 { } /* end */
1055};
1056
Takashi Iwai16d11a82009-06-24 14:07:53 +02001057
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058/*
1059 * initialization verbs
1060 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001061static const struct hda_verb ad1986a_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 /* Front, Surround, CLFE DAC; mute as default */
1063 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1064 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1065 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1066 /* Downmix - off */
1067 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1068 /* HP, Line-Out, Surround, CLFE selectors */
1069 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
1070 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
1071 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1072 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1073 /* Mono selector */
1074 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
1075 /* Mic selector: Mic 1/2 pin */
1076 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1077 /* Line-in selector: Line-in */
1078 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
1079 /* Mic 1/2 swap */
1080 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
1081 /* Record selector: mic */
1082 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
1083 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
1084 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1085 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1086 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1087 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1088 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1089 /* PC beep */
1090 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
1091 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
1092 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1093 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1094 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1095 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1096 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001097 /* HP Pin */
1098 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1099 /* Front, Surround, CLFE Pins */
1100 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1101 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1102 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1103 /* Mono Pin */
1104 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1105 /* Mic Pin */
1106 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1107 /* Line, Aux, CD, Beep-In Pin */
1108 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1109 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1110 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1111 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1112 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 { } /* end */
1114};
1115
Takashi Iwai498f5b12011-05-02 11:33:15 +02001116static const struct hda_verb ad1986a_ch2_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001117 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001118 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1119 /* Line-in selectors */
1120 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001121 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001122 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1123 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
1124 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001125 { } /* end */
1126};
1127
Takashi Iwai498f5b12011-05-02 11:33:15 +02001128static const struct hda_verb ad1986a_ch4_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001129 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001130 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1131 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001132 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001133 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1134 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001135 { } /* end */
1136};
1137
Takashi Iwai498f5b12011-05-02 11:33:15 +02001138static const struct hda_verb ad1986a_ch6_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001139 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001140 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1141 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001142 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001143 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1144 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001145 { } /* end */
1146};
1147
Takashi Iwai498f5b12011-05-02 11:33:15 +02001148static const struct hda_channel_mode ad1986a_modes[3] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001149 { 2, ad1986a_ch2_init },
1150 { 4, ad1986a_ch4_init },
1151 { 6, ad1986a_ch6_init },
1152};
1153
Takashi Iwai825aa972006-03-17 10:50:49 +01001154/* eapd initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001155static const struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001156 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +01001157 {}
1158};
1159
Takashi Iwai498f5b12011-05-02 11:33:15 +02001160static const struct hda_verb ad1986a_automic_verbs[] = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001161 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1162 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1163 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1164 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1165 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1166 {}
1167};
1168
Tobin Davisf36090f2007-01-08 11:07:12 +01001169/* Ultra initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001170static const struct hda_verb ad1986a_ultra_init[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001171 /* eapd initialization */
1172 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1173 /* CLFE -> Mic in */
1174 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1175 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1176 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1177 { } /* end */
1178};
1179
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001180/* pin sensing on HP jack */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001181static const struct hda_verb ad1986a_hp_init_verbs[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001182 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1183 {}
1184};
1185
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001186static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1187 unsigned int res)
1188{
1189 switch (res >> 26) {
1190 case AD1986A_HP_EVENT:
1191 ad1986a_hp_automute(codec);
1192 break;
1193 case AD1986A_MIC_EVENT:
1194 ad1986a_automic(codec);
1195 break;
1196 }
1197}
1198
1199static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1200{
1201 ad198x_init(codec);
1202 ad1986a_hp_automute(codec);
1203 ad1986a_automic(codec);
1204 return 0;
1205}
1206
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001207
Takashi Iwai9230d212006-03-13 13:49:49 +01001208/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001209enum {
1210 AD1986A_6STACK,
1211 AD1986A_3STACK,
1212 AD1986A_LAPTOP,
1213 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001214 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001215 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001216 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001217 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001218 AD1986A_MODELS
1219};
Takashi Iwai9230d212006-03-13 13:49:49 +01001220
Takashi Iwaiea734962011-01-17 11:29:34 +01001221static const char * const ad1986a_models[AD1986A_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001222 [AD1986A_6STACK] = "6stack",
1223 [AD1986A_3STACK] = "3stack",
1224 [AD1986A_LAPTOP] = "laptop",
1225 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001226 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001227 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001228 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001229 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001230};
1231
Takashi Iwai498f5b12011-05-02 11:33:15 +02001232static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001233 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001234 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001235 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001236 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001237 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1238 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1239 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1240 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001241 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001242 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001243 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1244 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1245 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1246 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1247 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001248 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001249 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001250 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001251 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001252 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001253 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001254 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001255 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001256 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001257 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001258 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001259 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001260 {}
1261};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
Takashi Iwaicb53c622007-08-10 17:21:45 +02001263#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001264static const struct hda_amp_list ad1986a_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001265 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1266 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1267 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1268 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1269 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1270 { } /* end */
1271};
1272#endif
1273
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001274static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1275{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001276 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001277 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1278}
1279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280static int patch_ad1986a(struct hda_codec *codec)
1281{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001282 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001283 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001285 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 if (spec == NULL)
1287 return -ENOMEM;
1288
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 codec->spec = spec;
1290
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001291 err = snd_hda_attach_beep_device(codec, 0x19);
1292 if (err < 0) {
1293 ad198x_free(codec);
1294 return err;
1295 }
1296 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1297
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 spec->multiout.max_channels = 6;
1299 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1300 spec->multiout.dac_nids = ad1986a_dac_nids;
1301 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001302 spec->num_adc_nids = 1;
1303 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001304 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001305 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001306 spec->num_mixers = 1;
1307 spec->mixers[0] = ad1986a_mixers;
1308 spec->num_init_verbs = 1;
1309 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001310#ifdef CONFIG_SND_HDA_POWER_SAVE
1311 spec->loopback.amplist = ad1986a_loopbacks;
1312#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001313 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001314 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001316 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317
Takashi Iwai9230d212006-03-13 13:49:49 +01001318 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001319 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1320 ad1986a_models,
1321 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001322 switch (board_config) {
1323 case AD1986A_3STACK:
1324 spec->num_mixers = 2;
1325 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001326 spec->num_init_verbs = 2;
1327 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001328 spec->channel_mode = ad1986a_modes;
1329 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001330 spec->need_dac_fix = 1;
1331 spec->multiout.max_channels = 2;
1332 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001333 break;
1334 case AD1986A_LAPTOP:
1335 spec->mixers[0] = ad1986a_laptop_mixers;
1336 spec->multiout.max_channels = 2;
1337 spec->multiout.num_dacs = 1;
1338 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1339 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001340 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001341 spec->num_mixers = 3;
1342 spec->mixers[0] = ad1986a_laptop_master_mixers;
1343 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1344 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001345 spec->num_init_verbs = 2;
1346 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1347 spec->multiout.max_channels = 2;
1348 spec->multiout.num_dacs = 1;
1349 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1350 if (!is_jack_available(codec, 0x25))
1351 spec->multiout.dig_out_nid = 0;
1352 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1353 break;
1354 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001355 spec->num_mixers = 2;
1356 spec->mixers[0] = ad1986a_laptop_master_mixers;
1357 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001358 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001359 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001360 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001361 spec->multiout.max_channels = 2;
1362 spec->multiout.num_dacs = 1;
1363 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001364 if (!is_jack_available(codec, 0x25))
1365 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001366 spec->input_mux = &ad1986a_automic_capture_source;
1367 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1368 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001369 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001370 case AD1986A_SAMSUNG_P50:
1371 spec->num_mixers = 2;
1372 spec->mixers[0] = ad1986a_automute_master_mixers;
1373 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1374 spec->num_init_verbs = 4;
1375 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1376 spec->init_verbs[2] = ad1986a_automic_verbs;
1377 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1378 spec->multiout.max_channels = 2;
1379 spec->multiout.num_dacs = 1;
1380 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1381 if (!is_jack_available(codec, 0x25))
1382 spec->multiout.dig_out_nid = 0;
1383 spec->input_mux = &ad1986a_automic_capture_source;
1384 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1385 codec->patch_ops.init = ad1986a_samsung_p50_init;
1386 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001387 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001388 spec->num_mixers = 3;
1389 spec->mixers[0] = ad1986a_automute_master_mixers;
1390 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1391 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001392 spec->num_init_verbs = 3;
1393 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1394 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1395 spec->multiout.max_channels = 2;
1396 spec->multiout.num_dacs = 1;
1397 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001398 if (!is_jack_available(codec, 0x25))
1399 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001400 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1401 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1402 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001403 /* Lenovo N100 seems to report the reversed bit
1404 * for HP jack-sensing
1405 */
1406 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001407 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001408 case AD1986A_ULTRA:
1409 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1410 spec->num_init_verbs = 2;
1411 spec->init_verbs[1] = ad1986a_ultra_init;
1412 spec->multiout.max_channels = 2;
1413 spec->multiout.num_dacs = 1;
1414 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1415 spec->multiout.dig_out_nid = 0;
1416 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001417 }
1418
Takashi Iwaid29240c2007-10-26 12:35:56 +02001419 /* AD1986A has a hardware problem that it can't share a stream
1420 * with multiple output pins. The copy of front to surrounds
1421 * causes noisy or silent outputs at a certain timing, e.g.
1422 * changing the volume.
1423 * So, let's disable the shared stream.
1424 */
1425 spec->multiout.no_share_stream = 1;
1426
Takashi Iwai729d55b2009-12-25 22:49:01 +01001427 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001428 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001429
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 return 0;
1431}
1432
1433/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001434 * AD1983 specific
1435 */
1436
1437#define AD1983_SPDIF_OUT 0x02
1438#define AD1983_DAC 0x03
1439#define AD1983_ADC 0x04
1440
Takashi Iwai498f5b12011-05-02 11:33:15 +02001441static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
1442static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
1443static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001444
Takashi Iwai498f5b12011-05-02 11:33:15 +02001445static const struct hda_input_mux ad1983_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001446 .num_items = 4,
1447 .items = {
1448 { "Mic", 0x0 },
1449 { "Line", 0x1 },
1450 { "Mix", 0x2 },
1451 { "Mix Mono", 0x3 },
1452 },
1453};
1454
1455/*
1456 * SPDIF playback route
1457 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001458static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001459{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001460 static const char * const texts[] = { "PCM", "ADC" };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001461
1462 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1463 uinfo->count = 1;
1464 uinfo->value.enumerated.items = 2;
1465 if (uinfo->value.enumerated.item > 1)
1466 uinfo->value.enumerated.item = 1;
1467 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1468 return 0;
1469}
1470
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001471static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001472{
1473 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1474 struct ad198x_spec *spec = codec->spec;
1475
1476 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1477 return 0;
1478}
1479
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001480static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001481{
1482 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1483 struct ad198x_spec *spec = codec->spec;
1484
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001485 if (ucontrol->value.enumerated.item[0] > 1)
1486 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001487 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1488 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001489 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1490 AC_VERB_SET_CONNECT_SEL,
1491 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001492 return 1;
1493 }
1494 return 0;
1495}
1496
Takashi Iwai498f5b12011-05-02 11:33:15 +02001497static const struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001498 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1499 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1508 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1509 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001510 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001511 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1512 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1513 {
1514 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1515 .name = "Capture Source",
1516 .info = ad198x_mux_enum_info,
1517 .get = ad198x_mux_enum_get,
1518 .put = ad198x_mux_enum_put,
1519 },
1520 {
1521 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001522 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001523 .info = ad1983_spdif_route_info,
1524 .get = ad1983_spdif_route_get,
1525 .put = ad1983_spdif_route_put,
1526 },
1527 { } /* end */
1528};
1529
Takashi Iwai498f5b12011-05-02 11:33:15 +02001530static const struct hda_verb ad1983_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001531 /* Front, HP, Mono; mute as default */
1532 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1533 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1534 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1535 /* Beep, PCM, Mic, Line-In: mute */
1536 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1537 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1538 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1539 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1540 /* Front, HP selectors; from Mix */
1541 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1542 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1543 /* Mono selector; from Mix */
1544 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1545 /* Mic selector; Mic */
1546 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1547 /* Line-in selector: Line-in */
1548 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1549 /* Mic boost: 0dB */
1550 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1551 /* Record selector: mic */
1552 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1553 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1554 /* SPDIF route: PCM */
1555 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1556 /* Front Pin */
1557 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1558 /* HP Pin */
1559 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1560 /* Mono Pin */
1561 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1562 /* Mic Pin */
1563 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1564 /* Line Pin */
1565 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1566 { } /* end */
1567};
1568
Takashi Iwaicb53c622007-08-10 17:21:45 +02001569#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001570static const struct hda_amp_list ad1983_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001571 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1572 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1573 { } /* end */
1574};
1575#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001576
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001577static int patch_ad1983(struct hda_codec *codec)
1578{
1579 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001580 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001581
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001582 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001583 if (spec == NULL)
1584 return -ENOMEM;
1585
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001586 codec->spec = spec;
1587
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001588 err = snd_hda_attach_beep_device(codec, 0x10);
1589 if (err < 0) {
1590 ad198x_free(codec);
1591 return err;
1592 }
1593 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1594
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001595 spec->multiout.max_channels = 2;
1596 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1597 spec->multiout.dac_nids = ad1983_dac_nids;
1598 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001599 spec->num_adc_nids = 1;
1600 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001601 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001602 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001603 spec->num_mixers = 1;
1604 spec->mixers[0] = ad1983_mixers;
1605 spec->num_init_verbs = 1;
1606 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001607 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001608#ifdef CONFIG_SND_HDA_POWER_SAVE
1609 spec->loopback.amplist = ad1983_loopbacks;
1610#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001611 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001612
1613 codec->patch_ops = ad198x_patch_ops;
1614
Takashi Iwai729d55b2009-12-25 22:49:01 +01001615 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001616 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001617
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001618 return 0;
1619}
1620
1621
1622/*
1623 * AD1981 HD specific
1624 */
1625
1626#define AD1981_SPDIF_OUT 0x02
1627#define AD1981_DAC 0x03
1628#define AD1981_ADC 0x04
1629
Takashi Iwai498f5b12011-05-02 11:33:15 +02001630static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
1631static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
1632static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001633
1634/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001635static const struct hda_input_mux ad1981_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001636 .num_items = 7,
1637 .items = {
1638 { "Front Mic", 0x0 },
1639 { "Line", 0x1 },
1640 { "Mix", 0x2 },
1641 { "Mix Mono", 0x3 },
1642 { "CD", 0x4 },
1643 { "Mic", 0x6 },
1644 { "Aux", 0x7 },
1645 },
1646};
1647
Takashi Iwai498f5b12011-05-02 11:33:15 +02001648static const struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001649 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1650 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1652 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1653 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1654 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1655 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1656 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1657 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1658 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1659 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1660 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1661 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1662 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1663 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1664 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1665 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1666 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001667 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1668 HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001669 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1670 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1671 {
1672 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1673 .name = "Capture Source",
1674 .info = ad198x_mux_enum_info,
1675 .get = ad198x_mux_enum_get,
1676 .put = ad198x_mux_enum_put,
1677 },
1678 /* identical with AD1983 */
1679 {
1680 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001681 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001682 .info = ad1983_spdif_route_info,
1683 .get = ad1983_spdif_route_get,
1684 .put = ad1983_spdif_route_put,
1685 },
1686 { } /* end */
1687};
1688
Takashi Iwai498f5b12011-05-02 11:33:15 +02001689static const struct hda_verb ad1981_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001690 /* Front, HP, Mono; mute as default */
1691 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1692 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1693 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1694 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1695 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1696 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1697 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1698 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1699 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1700 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1701 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1702 /* Front, HP selectors; from Mix */
1703 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1704 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1705 /* Mono selector; from Mix */
1706 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1707 /* Mic Mixer; select Front Mic */
1708 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1709 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1710 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001711 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1712 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001713 /* Record selector: Front mic */
1714 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1715 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1716 /* SPDIF route: PCM */
1717 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1718 /* Front Pin */
1719 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1720 /* HP Pin */
1721 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1722 /* Mono Pin */
1723 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1724 /* Front & Rear Mic Pins */
1725 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1726 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1727 /* Line Pin */
1728 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1729 /* Digital Beep */
1730 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1731 /* Line-Out as Input: disabled */
1732 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1733 { } /* end */
1734};
1735
Takashi Iwaicb53c622007-08-10 17:21:45 +02001736#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001737static const struct hda_amp_list ad1981_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001738 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1739 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1740 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1741 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1742 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1743 { } /* end */
1744};
1745#endif
1746
Takashi Iwai18a815d2006-03-01 19:54:39 +01001747/*
1748 * Patch for HP nx6320
1749 *
Tobin Davis18768992007-03-12 22:20:51 +01001750 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001751 * speaker output enabled _and_ mute-LED off.
1752 */
1753
1754#define AD1981_HP_EVENT 0x37
1755#define AD1981_MIC_EVENT 0x38
1756
Takashi Iwai498f5b12011-05-02 11:33:15 +02001757static const struct hda_verb ad1981_hp_init_verbs[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001758 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1759 /* pin sensing on HP and Mic jacks */
1760 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1761 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1762 {}
1763};
1764
1765/* turn on/off EAPD (+ mute HP) as a master switch */
1766static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1767 struct snd_ctl_elem_value *ucontrol)
1768{
1769 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1770 struct ad198x_spec *spec = codec->spec;
1771
1772 if (! ad198x_eapd_put(kcontrol, ucontrol))
1773 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001774 /* change speaker pin appropriately */
1775 snd_hda_codec_write(codec, 0x05, 0,
1776 AC_VERB_SET_PIN_WIDGET_CONTROL,
1777 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001778 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001779 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1780 HDA_AMP_MUTE,
1781 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001782 return 1;
1783}
1784
1785/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001786static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001787 .ops = &snd_hda_bind_vol,
1788 .values = {
1789 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1790 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1791 0
1792 },
1793};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001794
1795/* mute internal speaker if HP is plugged */
1796static void ad1981_hp_automute(struct hda_codec *codec)
1797{
1798 unsigned int present;
1799
Takashi Iwaid56757a2009-11-18 08:00:14 +01001800 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001801 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1802 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001803}
1804
1805/* toggle input of built-in and mic jack appropriately */
1806static void ad1981_hp_automic(struct hda_codec *codec)
1807{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001808 static const struct hda_verb mic_jack_on[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001809 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1810 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1811 {}
1812 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02001813 static const struct hda_verb mic_jack_off[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001814 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1815 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1816 {}
1817 };
1818 unsigned int present;
1819
Takashi Iwaid56757a2009-11-18 08:00:14 +01001820 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001821 if (present)
1822 snd_hda_sequence_write(codec, mic_jack_on);
1823 else
1824 snd_hda_sequence_write(codec, mic_jack_off);
1825}
1826
1827/* unsolicited event for HP jack sensing */
1828static void ad1981_hp_unsol_event(struct hda_codec *codec,
1829 unsigned int res)
1830{
1831 res >>= 26;
1832 switch (res) {
1833 case AD1981_HP_EVENT:
1834 ad1981_hp_automute(codec);
1835 break;
1836 case AD1981_MIC_EVENT:
1837 ad1981_hp_automic(codec);
1838 break;
1839 }
1840}
1841
Takashi Iwai498f5b12011-05-02 11:33:15 +02001842static const struct hda_input_mux ad1981_hp_capture_source = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001843 .num_items = 3,
1844 .items = {
1845 { "Mic", 0x0 },
1846 { "Docking-Station", 0x1 },
1847 { "Mix", 0x2 },
1848 },
1849};
1850
Takashi Iwai498f5b12011-05-02 11:33:15 +02001851static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001852 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001853 {
1854 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001855 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001856 .name = "Master Playback Switch",
1857 .info = ad198x_eapd_info,
1858 .get = ad198x_eapd_get,
1859 .put = ad1981_hp_master_sw_put,
1860 .private_value = 0x05,
1861 },
1862 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1863 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1864#if 0
1865 /* FIXME: analog mic/line loopback doesn't work with my tests...
1866 * (although recording is OK)
1867 */
1868 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1869 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1870 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1871 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1872 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1873 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1874 /* FIXME: does this laptop have analog CD connection? */
1875 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1876 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1877#endif
David Henningsson5f99f862011-01-04 15:24:24 +01001878 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1879 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001880 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1881 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1882 {
1883 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1884 .name = "Capture Source",
1885 .info = ad198x_mux_enum_info,
1886 .get = ad198x_mux_enum_get,
1887 .put = ad198x_mux_enum_put,
1888 },
1889 { } /* end */
1890};
1891
1892/* initialize jack-sensing, too */
1893static int ad1981_hp_init(struct hda_codec *codec)
1894{
1895 ad198x_init(codec);
1896 ad1981_hp_automute(codec);
1897 ad1981_hp_automic(codec);
1898 return 0;
1899}
1900
Tobin Davis18768992007-03-12 22:20:51 +01001901/* configuration for Toshiba Laptops */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001902static const struct hda_verb ad1981_toshiba_init_verbs[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001903 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1904 /* pin sensing on HP and Mic jacks */
1905 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1906 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1907 {}
1908};
1909
Takashi Iwai498f5b12011-05-02 11:33:15 +02001910static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001911 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1912 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1913 { }
1914};
1915
Takashi Iwai01686c52006-04-18 12:54:11 +02001916/* configuration for Lenovo Thinkpad T60 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001917static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
Takashi Iwai01686c52006-04-18 12:54:11 +02001918 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1919 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1920 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1921 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1922 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1923 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1924 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1925 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001926 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
Takashi Iwai01686c52006-04-18 12:54:11 +02001927 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1928 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1929 {
1930 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1931 .name = "Capture Source",
1932 .info = ad198x_mux_enum_info,
1933 .get = ad198x_mux_enum_get,
1934 .put = ad198x_mux_enum_put,
1935 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001936 /* identical with AD1983 */
1937 {
1938 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1939 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1940 .info = ad1983_spdif_route_info,
1941 .get = ad1983_spdif_route_get,
1942 .put = ad1983_spdif_route_put,
1943 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001944 { } /* end */
1945};
1946
Takashi Iwai498f5b12011-05-02 11:33:15 +02001947static const struct hda_input_mux ad1981_thinkpad_capture_source = {
Takashi Iwai01686c52006-04-18 12:54:11 +02001948 .num_items = 3,
1949 .items = {
1950 { "Mic", 0x0 },
1951 { "Mix", 0x2 },
1952 { "CD", 0x4 },
1953 },
1954};
1955
Takashi Iwai18a815d2006-03-01 19:54:39 +01001956/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001957enum {
1958 AD1981_BASIC,
1959 AD1981_HP,
1960 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001961 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001962 AD1981_MODELS
1963};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001964
Takashi Iwaiea734962011-01-17 11:29:34 +01001965static const char * const ad1981_models[AD1981_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001966 [AD1981_HP] = "hp",
1967 [AD1981_THINKPAD] = "thinkpad",
1968 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001969 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001970};
1971
Takashi Iwai498f5b12011-05-02 11:33:15 +02001972static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001973 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001974 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001975 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001976 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001977 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001978 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001979 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001980 /* HP nx6320 (reversed SSID, H/W bug) */
1981 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001982 {}
1983};
1984
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001985static int patch_ad1981(struct hda_codec *codec)
1986{
1987 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001988 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001989
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001990 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001991 if (spec == NULL)
1992 return -ENOMEM;
1993
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001994 codec->spec = spec;
1995
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001996 err = snd_hda_attach_beep_device(codec, 0x10);
1997 if (err < 0) {
1998 ad198x_free(codec);
1999 return err;
2000 }
2001 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
2002
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002003 spec->multiout.max_channels = 2;
2004 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
2005 spec->multiout.dac_nids = ad1981_dac_nids;
2006 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01002007 spec->num_adc_nids = 1;
2008 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002009 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002010 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01002011 spec->num_mixers = 1;
2012 spec->mixers[0] = ad1981_mixers;
2013 spec->num_init_verbs = 1;
2014 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002015 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002016#ifdef CONFIG_SND_HDA_POWER_SAVE
2017 spec->loopback.amplist = ad1981_loopbacks;
2018#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002019 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002020
2021 codec->patch_ops = ad198x_patch_ops;
2022
Takashi Iwai18a815d2006-03-01 19:54:39 +01002023 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002024 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
2025 ad1981_models,
2026 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01002027 switch (board_config) {
2028 case AD1981_HP:
2029 spec->mixers[0] = ad1981_hp_mixers;
2030 spec->num_init_verbs = 2;
2031 spec->init_verbs[1] = ad1981_hp_init_verbs;
Takashi Iwai695cd4a2011-06-10 14:37:04 +02002032 if (!is_jack_available(codec, 0x0a))
2033 spec->multiout.dig_out_nid = 0;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002034 spec->input_mux = &ad1981_hp_capture_source;
2035
2036 codec->patch_ops.init = ad1981_hp_init;
2037 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05002038 /* set the upper-limit for mixer amp to 0dB for avoiding the
2039 * possible damage by overloading
2040 */
2041 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2042 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2043 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2044 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2045 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01002046 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02002047 case AD1981_THINKPAD:
2048 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02002049 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04002050 /* set the upper-limit for mixer amp to 0dB for avoiding the
2051 * possible damage by overloading
2052 */
2053 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2054 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2055 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2056 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2057 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c52006-04-18 12:54:11 +02002058 break;
Tobin Davis18768992007-03-12 22:20:51 +01002059 case AD1981_TOSHIBA:
2060 spec->mixers[0] = ad1981_hp_mixers;
2061 spec->mixers[1] = ad1981_toshiba_mixers;
2062 spec->num_init_verbs = 2;
2063 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
2064 spec->multiout.dig_out_nid = 0;
2065 spec->input_mux = &ad1981_hp_capture_source;
2066 codec->patch_ops.init = ad1981_hp_init;
2067 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
2068 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002069 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01002070
2071 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002072 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002073
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002074 return 0;
2075}
2076
2077
2078/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002079 * AD1988
2080 *
2081 * Output pins and routes
2082 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002083 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002084 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
2085 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
2086 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
2087 * port-D 0x12 (mute/hp) <- 0x29 <- 04
2088 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
2089 * port-F 0x16 (mute) <- 0x2a <- 06
2090 * port-G 0x24 (mute) <- 0x27 <- 05
2091 * port-H 0x25 (mute) <- 0x28 <- 0a
2092 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
2093 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002094 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
2095 * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002096 *
2097 * Input pins and routes
2098 *
2099 * pin boost mix input # / adc input #
2100 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
2101 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
2102 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
2103 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
2104 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
2105 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
2106 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
2107 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
2108 *
2109 *
2110 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01002111 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002112 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002113 *
2114 * Inputs of Analog Mix (0x20)
2115 * 0:Port-B (front mic)
2116 * 1:Port-C/G/H (line-in)
2117 * 2:Port-A
2118 * 3:Port-D (line-in/2)
2119 * 4:Port-E/G/H (mic-in)
2120 * 5:Port-F (mic2-in)
2121 * 6:CD
2122 * 7:Beep
2123 *
2124 * ADC selection
2125 * 0:Port-A
2126 * 1:Port-B (front mic-in)
2127 * 2:Port-C (line-in)
2128 * 3:Port-F (mic2-in)
2129 * 4:Port-E (mic-in)
2130 * 5:CD
2131 * 6:Port-G
2132 * 7:Port-H
2133 * 8:Port-D (line-in/2)
2134 * 9:Mix
2135 *
2136 * Proposed pin assignments by the datasheet
2137 *
2138 * 6-stack
2139 * Port-A front headphone
2140 * B front mic-in
2141 * C rear line-in
2142 * D rear front-out
2143 * E rear mic-in
2144 * F rear surround
2145 * G rear CLFE
2146 * H rear side
2147 *
2148 * 3-stack
2149 * Port-A front headphone
2150 * B front mic
2151 * C rear line-in/surround
2152 * D rear front-out
2153 * E rear mic-in/CLFE
2154 *
2155 * laptop
2156 * Port-A headphone
2157 * B mic-in
2158 * C docking station
2159 * D internal speaker (with EAPD)
2160 * E/F quad mic array
2161 */
2162
2163
2164/* models */
2165enum {
2166 AD1988_6STACK,
2167 AD1988_6STACK_DIG,
2168 AD1988_3STACK,
2169 AD1988_3STACK_DIG,
2170 AD1988_LAPTOP,
2171 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002172 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002173 AD1988_MODEL_LAST,
2174};
2175
Takashi Iwaid32410b12005-11-24 16:06:23 +01002176/* reivision id to check workarounds */
2177#define AD1988A_REV2 0x100200
2178
Takashi Iwai1a806f42006-07-03 15:58:16 +02002179#define is_rev2(codec) \
2180 ((codec)->vendor_id == 0x11d41988 && \
2181 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002182
2183/*
2184 * mixers
2185 */
2186
Takashi Iwai498f5b12011-05-02 11:33:15 +02002187static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002188 0x04, 0x06, 0x05, 0x0a
2189};
2190
Takashi Iwai498f5b12011-05-02 11:33:15 +02002191static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002192 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002193};
2194
2195/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002196static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002197 0x04, 0x05, 0x0a, 0x06
2198};
2199
Takashi Iwai498f5b12011-05-02 11:33:15 +02002200static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002201 0x03
2202};
2203
Takashi Iwai498f5b12011-05-02 11:33:15 +02002204static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002205 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002206};
2207
Takashi Iwai498f5b12011-05-02 11:33:15 +02002208static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002209 0x08, 0x09, 0x0f
2210};
2211
Takashi Iwai498f5b12011-05-02 11:33:15 +02002212static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002213 0x0c, 0x0d, 0x0e
2214};
2215
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002216#define AD1988_SPDIF_OUT 0x02
2217#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002218#define AD1988_SPDIF_IN 0x07
2219
Takashi Iwai498f5b12011-05-02 11:33:15 +02002220static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01002221 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002222};
2223
Takashi Iwai498f5b12011-05-02 11:33:15 +02002224static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002225 .num_items = 5,
2226 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002227 { "Front Mic", 0x1 }, /* port-B */
2228 { "Line", 0x2 }, /* port-C */
2229 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002230 { "CD", 0x5 },
2231 { "Mix", 0x9 },
2232 },
2233};
2234
Takashi Iwai498f5b12011-05-02 11:33:15 +02002235static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002236 .num_items = 3,
2237 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002238 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002239 { "CD", 0x5 },
2240 { "Mix", 0x9 },
2241 },
2242};
2243
2244/*
2245 */
2246static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2247 struct snd_ctl_elem_info *uinfo)
2248{
2249 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2250 struct ad198x_spec *spec = codec->spec;
2251 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2252 spec->num_channel_mode);
2253}
2254
2255static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2256 struct snd_ctl_elem_value *ucontrol)
2257{
2258 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2259 struct ad198x_spec *spec = codec->spec;
2260 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2261 spec->num_channel_mode, spec->multiout.max_channels);
2262}
2263
2264static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2265 struct snd_ctl_elem_value *ucontrol)
2266{
2267 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2268 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002269 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2270 spec->num_channel_mode,
2271 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002272 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002273 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002274 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002275}
2276
Raymond Yau34588702011-09-23 19:03:25 +08002277static const struct snd_kcontrol_new ad1988_hp_mixers[] = {
2278 {
2279 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2280 .name = "Independent HP",
2281 .info = ad1988_independent_hp_info,
2282 .get = ad1988_independent_hp_get,
2283 .put = ad1988_independent_hp_put,
2284 },
2285 { } /* end */
2286};
2287
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002288/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002289static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002290 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2291 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2292 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2293 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2294 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002295 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002296};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002297
Takashi Iwai498f5b12011-05-02 11:33:15 +02002298static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002299 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2300 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2301 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2302 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2303 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002304 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002305};
2306
Takashi Iwai498f5b12011-05-02 11:33:15 +02002307static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002308 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002309 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2310 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2311 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2312 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2313 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2314 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2315 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2316
2317 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2318 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2319 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2320 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2321 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2322 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2323 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2324 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2325
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002326 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002327 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2328
David Henningsson5f99f862011-01-04 15:24:24 +01002329 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2330 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002331 { } /* end */
2332};
2333
2334/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002335static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002336 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002337 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002338 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2339 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002340 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002341};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002342
Takashi Iwai498f5b12011-05-02 11:33:15 +02002343static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002344 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002345 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2346 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2347 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002348 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002349};
2350
Takashi Iwai498f5b12011-05-02 11:33:15 +02002351static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002352 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002353 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002354 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2355 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2356 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002357 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2358 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2359
2360 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2361 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2362 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2363 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2364 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2365 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2366 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2367 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2368
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002369 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002370 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2371
David Henningsson5f99f862011-01-04 15:24:24 +01002372 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2373 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002374 {
2375 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2376 .name = "Channel Mode",
2377 .info = ad198x_ch_mode_info,
2378 .get = ad198x_ch_mode_get,
2379 .put = ad198x_ch_mode_put,
2380 },
2381
2382 { } /* end */
2383};
2384
2385/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002386static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002387 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002388 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2389 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2390 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2391
2392 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2393 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2394 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2395 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2396 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2397 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2398
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002399 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002400 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2401
David Henningsson5f99f862011-01-04 15:24:24 +01002402 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002403
2404 {
2405 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2406 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002407 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002408 .info = ad198x_eapd_info,
2409 .get = ad198x_eapd_get,
2410 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002411 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002412 },
2413
2414 { } /* end */
2415};
2416
2417/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002418static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002419 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2420 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2421 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2422 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2423 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2424 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2425 {
2426 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2427 /* The multiple "Capture Source" controls confuse alsamixer
2428 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002429 */
2430 /* .name = "Capture Source", */
2431 .name = "Input Source",
2432 .count = 3,
2433 .info = ad198x_mux_enum_info,
2434 .get = ad198x_mux_enum_get,
2435 .put = ad198x_mux_enum_put,
2436 },
2437 { } /* end */
2438};
2439
2440static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2441 struct snd_ctl_elem_info *uinfo)
2442{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002443 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002444 "PCM", "ADC1", "ADC2", "ADC3"
2445 };
2446 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2447 uinfo->count = 1;
2448 uinfo->value.enumerated.items = 4;
2449 if (uinfo->value.enumerated.item >= 4)
2450 uinfo->value.enumerated.item = 3;
2451 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2452 return 0;
2453}
2454
2455static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2456 struct snd_ctl_elem_value *ucontrol)
2457{
2458 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2459 unsigned int sel;
2460
Takashi Iwaibddcf542007-07-24 18:04:05 +02002461 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2462 AC_AMP_GET_INPUT);
2463 if (!(sel & 0x80))
2464 ucontrol->value.enumerated.item[0] = 0;
2465 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002466 sel = snd_hda_codec_read(codec, 0x0b, 0,
2467 AC_VERB_GET_CONNECT_SEL, 0);
2468 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002469 sel++;
2470 else
2471 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002472 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002473 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002474 return 0;
2475}
2476
2477static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2478 struct snd_ctl_elem_value *ucontrol)
2479{
2480 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002481 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002482 int change;
2483
Takashi Iwai35b26722007-05-05 12:17:17 +02002484 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002485 if (val > 3)
2486 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002487 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002488 sel = snd_hda_codec_read(codec, 0x1d, 0,
2489 AC_VERB_GET_AMP_GAIN_MUTE,
2490 AC_AMP_GET_INPUT);
2491 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002492 if (change) {
2493 snd_hda_codec_write_cache(codec, 0x1d, 0,
2494 AC_VERB_SET_AMP_GAIN_MUTE,
2495 AMP_IN_UNMUTE(0));
2496 snd_hda_codec_write_cache(codec, 0x1d, 0,
2497 AC_VERB_SET_AMP_GAIN_MUTE,
2498 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002499 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002500 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002501 sel = snd_hda_codec_read(codec, 0x1d, 0,
2502 AC_VERB_GET_AMP_GAIN_MUTE,
2503 AC_AMP_GET_INPUT | 0x01);
2504 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002505 if (change) {
2506 snd_hda_codec_write_cache(codec, 0x1d, 0,
2507 AC_VERB_SET_AMP_GAIN_MUTE,
2508 AMP_IN_MUTE(0));
2509 snd_hda_codec_write_cache(codec, 0x1d, 0,
2510 AC_VERB_SET_AMP_GAIN_MUTE,
2511 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002512 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002513 sel = snd_hda_codec_read(codec, 0x0b, 0,
2514 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2515 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002516 if (change)
2517 snd_hda_codec_write_cache(codec, 0x0b, 0,
2518 AC_VERB_SET_CONNECT_SEL,
2519 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002520 }
2521 return change;
2522}
2523
Takashi Iwai498f5b12011-05-02 11:33:15 +02002524static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002525 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2526 {
2527 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2528 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002529 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002530 .info = ad1988_spdif_playback_source_info,
2531 .get = ad1988_spdif_playback_source_get,
2532 .put = ad1988_spdif_playback_source_put,
2533 },
2534 { } /* end */
2535};
2536
Takashi Iwai498f5b12011-05-02 11:33:15 +02002537static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002538 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2539 { } /* end */
2540};
2541
Takashi Iwai498f5b12011-05-02 11:33:15 +02002542static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002543 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002544 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002545 { } /* end */
2546};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002547
2548/*
2549 * initialization verbs
2550 */
2551
2552/*
2553 * for 6-stack (+dig)
2554 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002555static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002556 /* Front, Surround, CLFE, side DAC; unmute as default */
2557 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2558 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2559 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2560 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002561 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002562 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002563 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2564 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2565 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2566 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2567 /* Port-D line-out path */
2568 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2569 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2570 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2571 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2572 /* Port-F surround path */
2573 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2574 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2575 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2576 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2577 /* Port-G CLFE path */
2578 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2579 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2580 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2581 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2582 /* Port-H side path */
2583 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2584 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2585 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2586 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2587 /* Mono out path */
2588 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2589 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2590 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2591 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2592 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2593 /* Port-B front mic-in path */
2594 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2595 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2596 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2597 /* Port-C line-in path */
2598 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2599 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2600 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2601 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2602 /* Port-E mic-in path */
2603 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2604 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2605 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2606 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002607 /* Analog CD Input */
2608 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002609 /* Analog Mix output amp */
2610 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002611
2612 { }
2613};
2614
Takashi Iwai498f5b12011-05-02 11:33:15 +02002615static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002616 /* Headphone; unmute as default */
2617 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2618 /* Port-A front headphon path */
2619 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2620 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2621 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2622 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2623 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002624
2625 { }
2626};
2627
Takashi Iwai498f5b12011-05-02 11:33:15 +02002628static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002629 /* mute analog mix */
2630 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2631 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2632 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2633 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2634 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2635 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2636 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2637 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2638 /* select ADCs - front-mic */
2639 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2640 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2641 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002642
2643 { }
2644};
2645
Takashi Iwai498f5b12011-05-02 11:33:15 +02002646static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002647 /* SPDIF out sel */
2648 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2649 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2650 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002651 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002652 /* SPDIF out pin */
2653 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002654
2655 { }
2656};
2657
Takashi Iwai498f5b12011-05-02 11:33:15 +02002658static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002659 /* unmute SPDIF input pin */
2660 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2661 { }
2662};
2663
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002664/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002665static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002666 /* SPDIF-1 out pin */
2667 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002668 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002669 /* SPDIF-2/HDMI out pin */
2670 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2671 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002672 { }
2673};
2674
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002675/*
2676 * verbs for 3stack (+dig)
2677 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002678static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002679 /* set port-C to line-in */
2680 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2681 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2682 /* set port-E to mic-in */
2683 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2684 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2685 { } /* end */
2686};
2687
Takashi Iwai498f5b12011-05-02 11:33:15 +02002688static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002689 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002690 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002691 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002692 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002693 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002694 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002695 { } /* end */
2696};
2697
Takashi Iwai498f5b12011-05-02 11:33:15 +02002698static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002699 { 2, ad1988_3stack_ch2_init },
2700 { 6, ad1988_3stack_ch6_init },
2701};
2702
Takashi Iwai498f5b12011-05-02 11:33:15 +02002703static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002704 /* Front, Surround, CLFE, side DAC; unmute as default */
2705 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2706 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2707 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2708 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002709 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002710 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002711 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2712 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2713 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2714 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2715 /* Port-D line-out path */
2716 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2717 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2718 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2719 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2720 /* Mono out path */
2721 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2722 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2723 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2724 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2725 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2726 /* Port-B front mic-in path */
2727 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2728 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2729 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002730 /* Port-C line-in/surround path - 6ch mode as default */
2731 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2732 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002733 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002734 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002735 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002736 /* Port-E mic-in/CLFE path - 6ch mode as default */
2737 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2738 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002739 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002740 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002741 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2742 /* mute analog mix */
2743 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2744 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2745 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2746 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2747 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2748 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2749 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2750 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2751 /* select ADCs - front-mic */
2752 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2753 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2754 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002755 /* Analog Mix output amp */
2756 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002757 { }
2758};
2759
2760/*
2761 * verbs for laptop mode (+dig)
2762 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002763static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002764 /* unmute port-A and mute port-D */
2765 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2766 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2767 { } /* end */
2768};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002769static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002770 /* mute port-A and unmute port-D */
2771 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2772 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2773 { } /* end */
2774};
2775
2776#define AD1988_HP_EVENT 0x01
2777
Takashi Iwai498f5b12011-05-02 11:33:15 +02002778static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002779 /* Front, Surround, CLFE, side DAC; unmute as default */
2780 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2781 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2782 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2783 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002784 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002785 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002786 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2787 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2788 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2789 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2790 /* unsolicited event for pin-sense */
2791 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2792 /* Port-D line-out path + EAPD */
2793 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2794 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2795 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2796 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2797 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2798 /* Mono out path */
2799 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2800 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2801 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2802 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2803 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2804 /* Port-B mic-in path */
2805 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2806 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2807 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2808 /* Port-C docking station - try to output */
2809 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2810 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2811 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2812 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2813 /* mute analog mix */
2814 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2815 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2816 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2817 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2818 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2819 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2820 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2821 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2822 /* select ADCs - mic */
2823 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2824 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2825 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002826 /* Analog Mix output amp */
2827 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002828 { }
2829};
2830
2831static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2832{
2833 if ((res >> 26) != AD1988_HP_EVENT)
2834 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002835 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002836 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2837 else
2838 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2839}
2840
Takashi Iwaicb53c622007-08-10 17:21:45 +02002841#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02002842static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002843 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2844 { 0x20, HDA_INPUT, 1 }, /* Line */
2845 { 0x20, HDA_INPUT, 4 }, /* Mic */
2846 { 0x20, HDA_INPUT, 6 }, /* CD */
2847 { } /* end */
2848};
2849#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002850
2851/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002852 * Automatic parse of I/O pins from the BIOS configuration
2853 */
2854
Takashi Iwaid32410b12005-11-24 16:06:23 +01002855enum {
2856 AD_CTL_WIDGET_VOL,
2857 AD_CTL_WIDGET_MUTE,
2858 AD_CTL_BIND_MUTE,
2859};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002860static const struct snd_kcontrol_new ad1988_control_templates[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002861 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2862 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2863 HDA_BIND_MUTE(NULL, 0, 0, 0),
2864};
2865
2866/* add dynamic controls */
2867static int add_control(struct ad198x_spec *spec, int type, const char *name,
2868 unsigned long val)
2869{
2870 struct snd_kcontrol_new *knew;
2871
Takashi Iwai603c4012008-07-30 15:01:44 +02002872 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2873 knew = snd_array_new(&spec->kctls);
2874 if (!knew)
2875 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002876 *knew = ad1988_control_templates[type];
2877 knew->name = kstrdup(name, GFP_KERNEL);
2878 if (! knew->name)
2879 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002880 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002881 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002882 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002883 return 0;
2884}
2885
2886#define AD1988_PIN_CD_NID 0x18
2887#define AD1988_PIN_BEEP_NID 0x10
2888
Takashi Iwai498f5b12011-05-02 11:33:15 +02002889static const hda_nid_t ad1988_mixer_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002890 /* A B C D E F G H */
2891 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2892};
2893
2894static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2895{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002896 static const hda_nid_t idx_to_dac[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002897 /* A B C D E F G H */
Raymond Yau356aab72011-08-31 10:30:59 +08002898 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002899 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02002900 static const hda_nid_t idx_to_dac_rev2[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002901 /* A B C D E F G H */
Raymond Yau356aab72011-08-31 10:30:59 +08002902 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002903 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002904 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002905 return idx_to_dac_rev2[idx];
2906 else
2907 return idx_to_dac[idx];
2908}
2909
Takashi Iwai498f5b12011-05-02 11:33:15 +02002910static const hda_nid_t ad1988_boost_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002911 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2912};
2913
2914static int ad1988_pin_idx(hda_nid_t nid)
2915{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002916 static const hda_nid_t ad1988_io_pins[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002917 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2918 };
2919 int i;
2920 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2921 if (ad1988_io_pins[i] == nid)
2922 return i;
2923 return 0; /* should be -1 */
2924}
2925
2926static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2927{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002928 static const int loopback_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002929 2, 0, 1, 3, 4, 5, 1, 4
2930 };
2931 switch (nid) {
2932 case AD1988_PIN_CD_NID:
2933 return 6;
2934 default:
2935 return loopback_idx[ad1988_pin_idx(nid)];
2936 }
2937}
2938
2939static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2940{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002941 static const int adc_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002942 0, 1, 2, 8, 4, 3, 6, 7
2943 };
2944 switch (nid) {
2945 case AD1988_PIN_CD_NID:
2946 return 5;
2947 default:
2948 return adc_idx[ad1988_pin_idx(nid)];
2949 }
2950}
2951
2952/* fill in the dac_nids table from the parsed pin configuration */
2953static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2954 const struct auto_pin_cfg *cfg)
2955{
2956 struct ad198x_spec *spec = codec->spec;
2957 int i, idx;
2958
2959 spec->multiout.dac_nids = spec->private_dac_nids;
2960
2961 /* check the pins hardwired to audio widget */
2962 for (i = 0; i < cfg->line_outs; i++) {
2963 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
Takashi Iwaidda14412011-05-02 11:29:30 +02002964 spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002965 }
2966 spec->multiout.num_dacs = cfg->line_outs;
2967 return 0;
2968}
2969
2970/* add playback controls from the parsed DAC table */
2971static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2972 const struct auto_pin_cfg *cfg)
2973{
2974 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01002975 static const char * const chname[4] = {
2976 "Front", "Surround", NULL /*CLFE*/, "Side"
2977 };
Takashi Iwaid32410b12005-11-24 16:06:23 +01002978 hda_nid_t nid;
2979 int i, err;
2980
2981 for (i = 0; i < cfg->line_outs; i++) {
2982 hda_nid_t dac = spec->multiout.dac_nids[i];
2983 if (! dac)
2984 continue;
2985 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2986 if (i == 2) {
2987 /* Center/LFE */
2988 err = add_control(spec, AD_CTL_WIDGET_VOL,
2989 "Center Playback Volume",
2990 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2991 if (err < 0)
2992 return err;
2993 err = add_control(spec, AD_CTL_WIDGET_VOL,
2994 "LFE Playback Volume",
2995 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2996 if (err < 0)
2997 return err;
2998 err = add_control(spec, AD_CTL_BIND_MUTE,
2999 "Center Playback Switch",
3000 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
3001 if (err < 0)
3002 return err;
3003 err = add_control(spec, AD_CTL_BIND_MUTE,
3004 "LFE Playback Switch",
3005 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
3006 if (err < 0)
3007 return err;
3008 } else {
3009 sprintf(name, "%s Playback Volume", chname[i]);
3010 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3011 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
3012 if (err < 0)
3013 return err;
3014 sprintf(name, "%s Playback Switch", chname[i]);
3015 err = add_control(spec, AD_CTL_BIND_MUTE, name,
3016 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
3017 if (err < 0)
3018 return err;
3019 }
3020 }
3021 return 0;
3022}
3023
3024/* add playback controls for speaker and HP outputs */
3025static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
3026 const char *pfx)
3027{
3028 struct ad198x_spec *spec = codec->spec;
3029 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02003030 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003031 char name[32];
3032
3033 if (! pin)
3034 return 0;
3035
3036 idx = ad1988_pin_idx(pin);
3037 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02003038 /* check whether the corresponding DAC was already taken */
3039 for (i = 0; i < spec->autocfg.line_outs; i++) {
3040 hda_nid_t pin = spec->autocfg.line_out_pins[i];
3041 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
3042 if (dac == nid)
3043 break;
3044 }
3045 if (i >= spec->autocfg.line_outs) {
3046 /* specify the DAC as the extra output */
3047 if (!spec->multiout.hp_nid)
3048 spec->multiout.hp_nid = nid;
3049 else
3050 spec->multiout.extra_out_nid[0] = nid;
3051 /* control HP volume/switch on the output mixer amp */
3052 sprintf(name, "%s Playback Volume", pfx);
3053 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3054 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
3055 if (err < 0)
3056 return err;
3057 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01003058 nid = ad1988_mixer_nids[idx];
3059 sprintf(name, "%s Playback Switch", pfx);
3060 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
3061 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
3062 return err;
3063 return 0;
3064}
3065
3066/* create input playback/capture controls for the given pin */
3067static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
Takashi Iwai9e042e72010-08-30 13:04:44 +02003068 const char *ctlname, int ctlidx, int boost)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003069{
3070 char name[32];
3071 int err, idx;
3072
3073 sprintf(name, "%s Playback Volume", ctlname);
3074 idx = ad1988_pin_to_loopback_idx(pin);
3075 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3076 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
3077 return err;
3078 sprintf(name, "%s Playback Switch", ctlname);
3079 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
3080 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
3081 return err;
3082 if (boost) {
3083 hda_nid_t bnid;
3084 idx = ad1988_pin_idx(pin);
3085 bnid = ad1988_boost_nids[idx];
3086 if (bnid) {
David Henningsson5f99f862011-01-04 15:24:24 +01003087 sprintf(name, "%s Boost Volume", ctlname);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003088 return add_control(spec, AD_CTL_WIDGET_VOL, name,
3089 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
3090
3091 }
3092 }
3093 return 0;
3094}
3095
3096/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02003097static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
Takashi Iwaid32410b12005-11-24 16:06:23 +01003098 const struct auto_pin_cfg *cfg)
3099{
Takashi Iwai10a20af2010-09-09 16:28:02 +02003100 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003101 struct hda_input_mux *imux = &spec->private_imux;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003102 int i, err, type, type_idx;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003103
Takashi Iwai9e042e72010-08-30 13:04:44 +02003104 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02003105 const char *label;
Takashi Iwai9e042e72010-08-30 13:04:44 +02003106 type = cfg->inputs[i].type;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003107 label = hda_get_autocfg_input_label(codec, cfg, i);
3108 snd_hda_add_imux_item(imux, label,
3109 ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
3110 &type_idx);
Takashi Iwai9e042e72010-08-30 13:04:44 +02003111 err = new_analog_input(spec, cfg->inputs[i].pin,
Takashi Iwai10a20af2010-09-09 16:28:02 +02003112 label, type_idx,
Takashi Iwai86e29592010-09-09 14:50:17 +02003113 type == AUTO_PIN_MIC);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003114 if (err < 0)
3115 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003116 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02003117 snd_hda_add_imux_item(imux, "Mix", 9, NULL);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003118
3119 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
3120 "Analog Mix Playback Volume",
3121 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3122 return err;
3123 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
3124 "Analog Mix Playback Switch",
3125 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3126 return err;
3127
3128 return 0;
3129}
3130
3131static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
3132 hda_nid_t nid, int pin_type,
3133 int dac_idx)
3134{
3135 /* set as output */
3136 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
3137 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
3138 switch (nid) {
Raymond Yau356aab72011-08-31 10:30:59 +08003139 case 0x11: /* port-A - DAC 03 */
3140 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003141 break;
3142 case 0x14: /* port-B - DAC 06 */
3143 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
3144 break;
3145 case 0x15: /* port-C - DAC 05 */
3146 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
3147 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003148 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01003149 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3150 break;
3151 case 0x13: /* mono - DAC 04 */
3152 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3153 break;
3154 }
3155}
3156
3157static void ad1988_auto_init_multi_out(struct hda_codec *codec)
3158{
3159 struct ad198x_spec *spec = codec->spec;
3160 int i;
3161
3162 for (i = 0; i < spec->autocfg.line_outs; i++) {
3163 hda_nid_t nid = spec->autocfg.line_out_pins[i];
3164 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
3165 }
3166}
3167
3168static void ad1988_auto_init_extra_out(struct hda_codec *codec)
3169{
3170 struct ad198x_spec *spec = codec->spec;
3171 hda_nid_t pin;
3172
Takashi Iwai82bc9552006-03-21 11:24:42 +01003173 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003174 if (pin) /* connect to front */
3175 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003176 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003177 if (pin) /* connect to front */
3178 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
3179}
3180
3181static void ad1988_auto_init_analog_input(struct hda_codec *codec)
3182{
3183 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9e042e72010-08-30 13:04:44 +02003184 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003185 int i, idx;
3186
Takashi Iwai9e042e72010-08-30 13:04:44 +02003187 for (i = 0; i < cfg->num_inputs; i++) {
3188 hda_nid_t nid = cfg->inputs[i].pin;
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003189 int type = cfg->inputs[i].type;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003190 switch (nid) {
3191 case 0x15: /* port-C */
3192 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3193 break;
3194 case 0x17: /* port-E */
3195 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3196 break;
3197 }
3198 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003199 type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003200 if (nid != AD1988_PIN_CD_NID)
3201 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3202 AMP_OUT_MUTE);
3203 idx = ad1988_pin_idx(nid);
3204 if (ad1988_boost_nids[idx])
3205 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3206 AC_VERB_SET_AMP_GAIN_MUTE,
3207 AMP_OUT_ZERO);
3208 }
3209}
3210
3211/* parse the BIOS configuration and set up the alc_spec */
3212/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3213static int ad1988_parse_auto_config(struct hda_codec *codec)
3214{
3215 struct ad198x_spec *spec = codec->spec;
3216 int err;
3217
Kailang Yangdf694da2005-12-05 19:42:22 +01003218 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003219 return err;
3220 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3221 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003222 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003223 return 0; /* can't find valid BIOS pin config */
3224 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003225 (err = ad1988_auto_create_extra_out(codec,
3226 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003227 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003228 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003229 "Headphone")) < 0 ||
Takashi Iwai10a20af2010-09-09 16:28:02 +02003230 (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003231 return err;
3232
3233 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3234
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003235 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003236 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3237 if (spec->autocfg.dig_in_pin)
3238 spec->dig_in_nid = AD1988_SPDIF_IN;
3239
Takashi Iwai603c4012008-07-30 15:01:44 +02003240 if (spec->kctls.list)
3241 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003242
3243 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3244
3245 spec->input_mux = &spec->private_imux;
3246
3247 return 1;
3248}
3249
3250/* init callback for auto-configuration model -- overriding the default init */
3251static int ad1988_auto_init(struct hda_codec *codec)
3252{
3253 ad198x_init(codec);
3254 ad1988_auto_init_multi_out(codec);
3255 ad1988_auto_init_extra_out(codec);
3256 ad1988_auto_init_analog_input(codec);
3257 return 0;
3258}
3259
Takashi Iwaid32410b12005-11-24 16:06:23 +01003260/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003261 */
3262
Takashi Iwaiea734962011-01-17 11:29:34 +01003263static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003264 [AD1988_6STACK] = "6stack",
3265 [AD1988_6STACK_DIG] = "6stack-dig",
3266 [AD1988_3STACK] = "3stack",
3267 [AD1988_3STACK_DIG] = "3stack-dig",
3268 [AD1988_LAPTOP] = "laptop",
3269 [AD1988_LAPTOP_DIG] = "laptop-dig",
3270 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003271};
3272
Takashi Iwai498f5b12011-05-02 11:33:15 +02003273static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003274 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003275 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003276 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01003277 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003278 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003279 {}
3280};
3281
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003282static int patch_ad1988(struct hda_codec *codec)
3283{
3284 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003285 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003286
3287 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3288 if (spec == NULL)
3289 return -ENOMEM;
3290
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003291 codec->spec = spec;
3292
Takashi Iwai1a806f42006-07-03 15:58:16 +02003293 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003294 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3295
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003296 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003297 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003298 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003299 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3300 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003301 board_config = AD1988_AUTO;
3302 }
3303
3304 if (board_config == AD1988_AUTO) {
3305 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003306 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003307 if (err < 0) {
3308 ad198x_free(codec);
3309 return err;
3310 } else if (! err) {
3311 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3312 board_config = AD1988_6STACK;
3313 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003314 }
3315
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003316 err = snd_hda_attach_beep_device(codec, 0x10);
3317 if (err < 0) {
3318 ad198x_free(codec);
3319 return err;
3320 }
3321 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3322
Raymond Yau356aab72011-08-31 10:30:59 +08003323 if (!spec->multiout.hp_nid)
Raymond Yau34588702011-09-23 19:03:25 +08003324 spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003325 switch (board_config) {
3326 case AD1988_6STACK:
3327 case AD1988_6STACK_DIG:
3328 spec->multiout.max_channels = 8;
3329 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003330 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003331 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3332 else
3333 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003334 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003335 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003336 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003337 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3338 else
3339 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08003340 spec->mixers[1] = ad1988_6stack_mixers2;
3341 spec->num_init_verbs = 1;
3342 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yau34588702011-09-23 19:03:25 +08003343 if (board_config == AD1988_6STACK_DIG) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003344 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3345 spec->dig_in_nid = AD1988_SPDIF_IN;
3346 }
3347 break;
3348 case AD1988_3STACK:
3349 case AD1988_3STACK_DIG:
3350 spec->multiout.max_channels = 6;
3351 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003352 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003353 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3354 else
3355 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003356 spec->input_mux = &ad1988_6stack_capture_source;
3357 spec->channel_mode = ad1988_3stack_modes;
3358 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003359 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003360 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003361 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3362 else
3363 spec->mixers[0] = ad1988_3stack_mixers1;
3364 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003365 spec->num_init_verbs = 1;
3366 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3367 if (board_config == AD1988_3STACK_DIG)
3368 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3369 break;
3370 case AD1988_LAPTOP:
3371 case AD1988_LAPTOP_DIG:
3372 spec->multiout.max_channels = 2;
3373 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003374 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003375 spec->input_mux = &ad1988_laptop_capture_source;
3376 spec->num_mixers = 1;
3377 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003378 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003379 spec->num_init_verbs = 1;
3380 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3381 if (board_config == AD1988_LAPTOP_DIG)
3382 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3383 break;
3384 }
3385
Raymond Yau34588702011-09-23 19:03:25 +08003386 if (spec->autocfg.hp_pins[0]) {
3387 spec->mixers[spec->num_mixers++] = ad1988_hp_mixers;
3388 spec->slave_vols = ad1988_6stack_fp_slave_vols;
3389 spec->slave_sws = ad1988_6stack_fp_slave_sws;
3390 spec->alt_dac_nid = ad1988_alt_dac_nid;
3391 spec->stream_analog_alt_playback =
3392 &ad198x_pcm_analog_alt_playback;
3393 }
3394
Takashi Iwaid32410b12005-11-24 16:06:23 +01003395 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3396 spec->adc_nids = ad1988_adc_nids;
3397 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003398 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3399 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3400 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003401 if (codec->vendor_id >= 0x11d4989a) {
3402 spec->mixers[spec->num_mixers++] =
3403 ad1989_spdif_out_mixers;
3404 spec->init_verbs[spec->num_init_verbs++] =
3405 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003406 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003407 } else {
3408 spec->mixers[spec->num_mixers++] =
3409 ad1988_spdif_out_mixers;
3410 spec->init_verbs[spec->num_init_verbs++] =
3411 ad1988_spdif_init_verbs;
3412 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003413 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003414 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003415 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003416 spec->init_verbs[spec->num_init_verbs++] =
3417 ad1988_spdif_in_init_verbs;
3418 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003419
3420 codec->patch_ops = ad198x_patch_ops;
3421 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003422 case AD1988_AUTO:
3423 codec->patch_ops.init = ad1988_auto_init;
3424 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003425 case AD1988_LAPTOP:
3426 case AD1988_LAPTOP_DIG:
3427 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3428 break;
3429 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003430#ifdef CONFIG_SND_HDA_POWER_SAVE
3431 spec->loopback.amplist = ad1988_loopbacks;
3432#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003433 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003434
Takashi Iwai729d55b2009-12-25 22:49:01 +01003435 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003436 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003437
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003438 return 0;
3439}
3440
3441
3442/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003443 * AD1884 / AD1984
3444 *
3445 * port-B - front line/mic-in
3446 * port-E - aux in/out
3447 * port-F - aux in/out
3448 * port-C - rear line/mic-in
3449 * port-D - rear line/hp-out
3450 * port-A - front line/hp-out
3451 *
3452 * AD1984 = AD1884 + two digital mic-ins
3453 *
3454 * FIXME:
3455 * For simplicity, we share the single DAC for both HP and line-outs
3456 * right now. The inidividual playbacks could be easily implemented,
3457 * but no build-up framework is given, so far.
3458 */
3459
Takashi Iwai498f5b12011-05-02 11:33:15 +02003460static const hda_nid_t ad1884_dac_nids[1] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003461 0x04,
3462};
3463
Takashi Iwai498f5b12011-05-02 11:33:15 +02003464static const hda_nid_t ad1884_adc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003465 0x08, 0x09,
3466};
3467
Takashi Iwai498f5b12011-05-02 11:33:15 +02003468static const hda_nid_t ad1884_capsrc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003469 0x0c, 0x0d,
3470};
3471
3472#define AD1884_SPDIF_OUT 0x02
3473
Takashi Iwai498f5b12011-05-02 11:33:15 +02003474static const struct hda_input_mux ad1884_capture_source = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003475 .num_items = 4,
3476 .items = {
3477 { "Front Mic", 0x0 },
3478 { "Mic", 0x1 },
3479 { "CD", 0x2 },
3480 { "Mix", 0x3 },
3481 },
3482};
3483
Takashi Iwai498f5b12011-05-02 11:33:15 +02003484static const struct snd_kcontrol_new ad1884_base_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003485 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3486 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3487 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3488 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3489 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3490 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3491 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3492 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3493 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3494 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3495 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3496 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003497 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3498 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003499 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3500 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3501 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3502 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3503 {
3504 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3505 /* The multiple "Capture Source" controls confuse alsamixer
3506 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003507 */
3508 /* .name = "Capture Source", */
3509 .name = "Input Source",
3510 .count = 2,
3511 .info = ad198x_mux_enum_info,
3512 .get = ad198x_mux_enum_get,
3513 .put = ad198x_mux_enum_put,
3514 },
3515 /* SPDIF controls */
3516 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3517 {
3518 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3519 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3520 /* identical with ad1983 */
3521 .info = ad1983_spdif_route_info,
3522 .get = ad1983_spdif_route_get,
3523 .put = ad1983_spdif_route_put,
3524 },
3525 { } /* end */
3526};
3527
Takashi Iwai498f5b12011-05-02 11:33:15 +02003528static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003529 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3530 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3531 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003532 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003533 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003534 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003535 { } /* end */
3536};
3537
3538/*
3539 * initialization verbs
3540 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003541static const struct hda_verb ad1884_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003542 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003543 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3544 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003545 /* Port-A (HP) mixer */
3546 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3547 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3548 /* Port-A pin */
3549 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3550 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3551 /* HP selector - select DAC2 */
3552 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3553 /* Port-D (Line-out) mixer */
3554 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3555 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3556 /* Port-D pin */
3557 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3558 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3559 /* Mono-out mixer */
3560 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3561 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3562 /* Mono-out pin */
3563 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3564 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3565 /* Mono selector */
3566 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3567 /* Port-B (front mic) pin */
3568 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003569 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003570 /* Port-C (rear mic) pin */
3571 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003572 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003573 /* Analog mixer; mute as default */
3574 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3575 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3576 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3577 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3578 /* Analog Mix output amp */
3579 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3580 /* SPDIF output selector */
3581 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3582 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3583 { } /* end */
3584};
3585
Takashi Iwaicb53c622007-08-10 17:21:45 +02003586#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02003587static const struct hda_amp_list ad1884_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003588 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3589 { 0x20, HDA_INPUT, 1 }, /* Mic */
3590 { 0x20, HDA_INPUT, 2 }, /* CD */
3591 { 0x20, HDA_INPUT, 4 }, /* Docking */
3592 { } /* end */
3593};
3594#endif
3595
Takashi Iwaiea734962011-01-17 11:29:34 +01003596static const char * const ad1884_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +01003597 "PCM Playback Volume",
3598 "Mic Playback Volume",
3599 "Mono Playback Volume",
3600 "Front Mic Playback Volume",
3601 "Mic Playback Volume",
3602 "CD Playback Volume",
3603 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003604 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003605 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003606 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003607 NULL
3608};
3609
Takashi Iwai2bac6472007-05-18 18:21:41 +02003610static int patch_ad1884(struct hda_codec *codec)
3611{
3612 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003613 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003614
3615 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3616 if (spec == NULL)
3617 return -ENOMEM;
3618
Takashi Iwai2bac6472007-05-18 18:21:41 +02003619 codec->spec = spec;
3620
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003621 err = snd_hda_attach_beep_device(codec, 0x10);
3622 if (err < 0) {
3623 ad198x_free(codec);
3624 return err;
3625 }
3626 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3627
Takashi Iwai2bac6472007-05-18 18:21:41 +02003628 spec->multiout.max_channels = 2;
3629 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3630 spec->multiout.dac_nids = ad1884_dac_nids;
3631 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3632 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3633 spec->adc_nids = ad1884_adc_nids;
3634 spec->capsrc_nids = ad1884_capsrc_nids;
3635 spec->input_mux = &ad1884_capture_source;
3636 spec->num_mixers = 1;
3637 spec->mixers[0] = ad1884_base_mixers;
3638 spec->num_init_verbs = 1;
3639 spec->init_verbs[0] = ad1884_init_verbs;
3640 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003641#ifdef CONFIG_SND_HDA_POWER_SAVE
3642 spec->loopback.amplist = ad1884_loopbacks;
3643#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003644 spec->vmaster_nid = 0x04;
3645 /* we need to cover all playback volumes */
3646 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003647
3648 codec->patch_ops = ad198x_patch_ops;
3649
Takashi Iwai729d55b2009-12-25 22:49:01 +01003650 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003651 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003652
Takashi Iwai2bac6472007-05-18 18:21:41 +02003653 return 0;
3654}
3655
3656/*
3657 * Lenovo Thinkpad T61/X61
3658 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003659static const struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003660 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003661 .items = {
3662 { "Mic", 0x0 },
3663 { "Internal Mic", 0x1 },
3664 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003665 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003666 },
3667};
3668
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003669
3670/*
3671 * Dell Precision T3400
3672 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003673static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003674 .num_items = 3,
3675 .items = {
3676 { "Front Mic", 0x0 },
3677 { "Line-In", 0x1 },
3678 { "Mix", 0x3 },
3679 },
3680};
3681
3682
Takashi Iwai498f5b12011-05-02 11:33:15 +02003683static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003684 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3685 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3686 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3687 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3688 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3689 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3690 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3691 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003692 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3693 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003694 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3695 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003696 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3697 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3698 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003699 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3700 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3701 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3702 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3703 {
3704 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3705 /* The multiple "Capture Source" controls confuse alsamixer
3706 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003707 */
3708 /* .name = "Capture Source", */
3709 .name = "Input Source",
3710 .count = 2,
3711 .info = ad198x_mux_enum_info,
3712 .get = ad198x_mux_enum_get,
3713 .put = ad198x_mux_enum_put,
3714 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003715 /* SPDIF controls */
3716 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3717 {
3718 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3719 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3720 /* identical with ad1983 */
3721 .info = ad1983_spdif_route_info,
3722 .get = ad1983_spdif_route_get,
3723 .put = ad1983_spdif_route_put,
3724 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003725 { } /* end */
3726};
3727
3728/* additional verbs */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003729static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003730 /* Port-E (docking station mic) pin */
3731 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3732 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3733 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003734 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003735 /* Analog PC Beeper - allow firmware/ACPI beeps */
3736 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003737 /* Analog mixer - docking mic; mute as default */
3738 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003739 /* enable EAPD bit */
3740 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003741 { } /* end */
3742};
3743
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003744/*
3745 * Dell Precision T3400
3746 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003747static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003748 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3749 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3750 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3751 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3752 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3753 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3754 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3755 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3756 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003757 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
3758 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003759 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3760 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3761 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3762 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3763 {
3764 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3765 /* The multiple "Capture Source" controls confuse alsamixer
3766 * So call somewhat different..
3767 */
3768 /* .name = "Capture Source", */
3769 .name = "Input Source",
3770 .count = 2,
3771 .info = ad198x_mux_enum_info,
3772 .get = ad198x_mux_enum_get,
3773 .put = ad198x_mux_enum_put,
3774 },
3775 { } /* end */
3776};
3777
Takashi Iwai2bac6472007-05-18 18:21:41 +02003778/* Digial MIC ADC NID 0x05 + 0x06 */
3779static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3780 struct hda_codec *codec,
3781 unsigned int stream_tag,
3782 unsigned int format,
3783 struct snd_pcm_substream *substream)
3784{
3785 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3786 stream_tag, 0, format);
3787 return 0;
3788}
3789
3790static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3791 struct hda_codec *codec,
3792 struct snd_pcm_substream *substream)
3793{
Takashi Iwai888afa12008-03-18 09:57:50 +01003794 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003795 return 0;
3796}
3797
Takashi Iwai498f5b12011-05-02 11:33:15 +02003798static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003799 .substreams = 2,
3800 .channels_min = 2,
3801 .channels_max = 2,
3802 .nid = 0x05,
3803 .ops = {
3804 .prepare = ad1984_pcm_dmic_prepare,
3805 .cleanup = ad1984_pcm_dmic_cleanup
3806 },
3807};
3808
3809static int ad1984_build_pcms(struct hda_codec *codec)
3810{
3811 struct ad198x_spec *spec = codec->spec;
3812 struct hda_pcm *info;
3813 int err;
3814
3815 err = ad198x_build_pcms(codec);
3816 if (err < 0)
3817 return err;
3818
3819 info = spec->pcm_rec + codec->num_pcms;
3820 codec->num_pcms++;
3821 info->name = "AD1984 Digital Mic";
3822 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3823 return 0;
3824}
3825
3826/* models */
3827enum {
3828 AD1984_BASIC,
3829 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003830 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003831 AD1984_MODELS
3832};
3833
Takashi Iwaiea734962011-01-17 11:29:34 +01003834static const char * const ad1984_models[AD1984_MODELS] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003835 [AD1984_BASIC] = "basic",
3836 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003837 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003838};
3839
Takashi Iwai498f5b12011-05-02 11:33:15 +02003840static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003841 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003842 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003843 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003844 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003845 {}
3846};
3847
3848static int patch_ad1984(struct hda_codec *codec)
3849{
3850 struct ad198x_spec *spec;
3851 int board_config, err;
3852
3853 err = patch_ad1884(codec);
3854 if (err < 0)
3855 return err;
3856 spec = codec->spec;
3857 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3858 ad1984_models, ad1984_cfg_tbl);
3859 switch (board_config) {
3860 case AD1984_BASIC:
3861 /* additional digital mics */
3862 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3863 codec->patch_ops.build_pcms = ad1984_build_pcms;
3864 break;
3865 case AD1984_THINKPAD:
Jerone Young68c18692010-08-03 01:46:44 -05003866 if (codec->subsystem_id == 0x17aa20fb) {
3867 /* Thinpad X300 does not have the ability to do SPDIF,
3868 or attach to docking station to use SPDIF */
3869 spec->multiout.dig_out_nid = 0;
3870 } else
3871 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003872 spec->input_mux = &ad1984_thinkpad_capture_source;
3873 spec->mixers[0] = ad1984_thinkpad_mixers;
3874 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003875 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003876 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003877 case AD1984_DELL_DESKTOP:
3878 spec->multiout.dig_out_nid = 0;
3879 spec->input_mux = &ad1984_dell_desktop_capture_source;
3880 spec->mixers[0] = ad1984_dell_desktop_mixers;
3881 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003882 }
3883 return 0;
3884}
3885
3886
3887/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003888 * AD1883 / AD1884A / AD1984A / AD1984B
3889 *
3890 * port-B (0x14) - front mic-in
3891 * port-E (0x1c) - rear mic-in
3892 * port-F (0x16) - CD / ext out
3893 * port-C (0x15) - rear line-in
3894 * port-D (0x12) - rear line-out
3895 * port-A (0x11) - front hp-out
3896 *
3897 * AD1984A = AD1884A + digital-mic
3898 * AD1883 = equivalent with AD1984A
3899 * AD1984B = AD1984A + extra SPDIF-out
3900 *
3901 * FIXME:
3902 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3903 */
3904
Takashi Iwai498f5b12011-05-02 11:33:15 +02003905static const hda_nid_t ad1884a_dac_nids[1] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003906 0x03,
3907};
3908
3909#define ad1884a_adc_nids ad1884_adc_nids
3910#define ad1884a_capsrc_nids ad1884_capsrc_nids
3911
3912#define AD1884A_SPDIF_OUT 0x02
3913
Takashi Iwai498f5b12011-05-02 11:33:15 +02003914static const struct hda_input_mux ad1884a_capture_source = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003915 .num_items = 5,
3916 .items = {
3917 { "Front Mic", 0x0 },
3918 { "Mic", 0x4 },
3919 { "Line", 0x1 },
3920 { "CD", 0x2 },
3921 { "Mix", 0x3 },
3922 },
3923};
3924
Takashi Iwai498f5b12011-05-02 11:33:15 +02003925static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003926 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3927 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3928 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3929 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3930 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3931 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3932 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3933 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3934 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3935 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3936 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3937 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3938 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3939 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3940 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3941 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003942 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3943 HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
3944 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003945 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3946 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3947 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3948 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3949 {
3950 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3951 /* The multiple "Capture Source" controls confuse alsamixer
3952 * So call somewhat different..
3953 */
3954 /* .name = "Capture Source", */
3955 .name = "Input Source",
3956 .count = 2,
3957 .info = ad198x_mux_enum_info,
3958 .get = ad198x_mux_enum_get,
3959 .put = ad198x_mux_enum_put,
3960 },
3961 /* SPDIF controls */
3962 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3963 {
3964 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3965 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3966 /* identical with ad1983 */
3967 .info = ad1983_spdif_route_info,
3968 .get = ad1983_spdif_route_get,
3969 .put = ad1983_spdif_route_put,
3970 },
3971 { } /* end */
3972};
3973
3974/*
3975 * initialization verbs
3976 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003977static const struct hda_verb ad1884a_init_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003978 /* DACs; unmute as default */
3979 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3980 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3981 /* Port-A (HP) mixer - route only from analog mixer */
3982 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3983 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3984 /* Port-A pin */
3985 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3986 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3987 /* Port-D (Line-out) mixer - route only from analog mixer */
3988 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3989 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3990 /* Port-D pin */
3991 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3992 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3993 /* Mono-out mixer - route only from analog mixer */
3994 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3995 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3996 /* Mono-out pin */
3997 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3998 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3999 /* Port-B (front mic) pin */
4000 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004001 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004002 /* Port-C (rear line-in) pin */
4003 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004004 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004005 /* Port-E (rear mic) pin */
4006 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4007 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4008 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
4009 /* Port-F (CD) pin */
4010 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4011 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4012 /* Analog mixer; mute as default */
4013 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4014 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4015 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4016 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4017 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
4018 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4019 /* Analog Mix output amp */
4020 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4021 /* capture sources */
4022 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
4023 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4024 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4025 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4026 /* SPDIF output amp */
4027 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4028 { } /* end */
4029};
4030
4031#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02004032static const struct hda_amp_list ad1884a_loopbacks[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004033 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4034 { 0x20, HDA_INPUT, 1 }, /* Mic */
4035 { 0x20, HDA_INPUT, 2 }, /* CD */
4036 { 0x20, HDA_INPUT, 4 }, /* Docking */
4037 { } /* end */
4038};
4039#endif
4040
4041/*
4042 * Laptop model
4043 *
4044 * Port A: Headphone jack
4045 * Port B: MIC jack
4046 * Port C: Internal MIC
4047 * Port D: Dock Line Out (if enabled)
4048 * Port E: Dock Line In (if enabled)
4049 * Port F: Internal speakers
4050 */
4051
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004052static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
4053 struct snd_ctl_elem_value *ucontrol)
4054{
4055 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4056 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
4057 int mute = (!ucontrol->value.integer.value[0] &&
4058 !ucontrol->value.integer.value[1]);
4059 /* toggle GPIO1 according to the mute state */
4060 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
4061 mute ? 0x02 : 0x0);
4062 return ret;
4063}
Takashi Iwaic5059252008-02-16 09:43:56 +01004064
Takashi Iwai498f5b12011-05-02 11:33:15 +02004065static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004066 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004067 {
4068 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4069 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004070 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004071 .info = snd_hda_mixer_amp_switch_info,
4072 .get = snd_hda_mixer_amp_switch_get,
4073 .put = ad1884a_mobile_master_sw_put,
4074 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4075 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004076 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4077 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4078 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4079 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4080 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4081 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4082 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4083 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4084 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004085 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4086 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4087 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004088 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4089 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004090 { } /* end */
4091};
4092
Takashi Iwai498f5b12011-05-02 11:33:15 +02004093static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004094 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db172009-07-02 16:10:23 +02004095 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4096 {
4097 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4098 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004099 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db172009-07-02 16:10:23 +02004100 .info = snd_hda_mixer_amp_switch_info,
4101 .get = snd_hda_mixer_amp_switch_get,
4102 .put = ad1884a_mobile_master_sw_put,
4103 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4104 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004105 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4106 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02004107 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
4108 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004109 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4110 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004111 { } /* end */
4112};
4113
Takashi Iwaic5059252008-02-16 09:43:56 +01004114/* mute internal speaker if HP is plugged */
4115static void ad1884a_hp_automute(struct hda_codec *codec)
4116{
4117 unsigned int present;
4118
Takashi Iwaid56757a2009-11-18 08:00:14 +01004119 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01004120 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4121 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4122 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4123 present ? 0x00 : 0x02);
4124}
4125
Takashi Iwai269ef192008-05-30 15:32:15 +02004126/* switch to external mic if plugged */
4127static void ad1884a_hp_automic(struct hda_codec *codec)
4128{
4129 unsigned int present;
4130
Takashi Iwaid56757a2009-11-18 08:00:14 +01004131 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02004132 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
4133 present ? 0 : 1);
4134}
4135
Takashi Iwaic5059252008-02-16 09:43:56 +01004136#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02004137#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01004138
4139/* unsolicited event for HP jack sensing */
4140static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
4141{
Takashi Iwai269ef192008-05-30 15:32:15 +02004142 switch (res >> 26) {
4143 case AD1884A_HP_EVENT:
4144 ad1884a_hp_automute(codec);
4145 break;
4146 case AD1884A_MIC_EVENT:
4147 ad1884a_hp_automic(codec);
4148 break;
4149 }
Takashi Iwaic5059252008-02-16 09:43:56 +01004150}
4151
4152/* initialize jack-sensing, too */
4153static int ad1884a_hp_init(struct hda_codec *codec)
4154{
4155 ad198x_init(codec);
4156 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02004157 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01004158 return 0;
4159}
4160
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004161/* mute internal speaker if HP or docking HP is plugged */
4162static void ad1884a_laptop_automute(struct hda_codec *codec)
4163{
4164 unsigned int present;
4165
Takashi Iwaid56757a2009-11-18 08:00:14 +01004166 present = snd_hda_jack_detect(codec, 0x11);
4167 if (!present)
4168 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004169 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4170 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4171 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4172 present ? 0x00 : 0x02);
4173}
4174
4175/* switch to external mic if plugged */
4176static void ad1884a_laptop_automic(struct hda_codec *codec)
4177{
4178 unsigned int idx;
4179
Takashi Iwaid56757a2009-11-18 08:00:14 +01004180 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004181 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01004182 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004183 idx = 4;
4184 else
4185 idx = 1;
4186 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
4187}
4188
4189/* unsolicited event for HP jack sensing */
4190static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
4191 unsigned int res)
4192{
4193 switch (res >> 26) {
4194 case AD1884A_HP_EVENT:
4195 ad1884a_laptop_automute(codec);
4196 break;
4197 case AD1884A_MIC_EVENT:
4198 ad1884a_laptop_automic(codec);
4199 break;
4200 }
4201}
4202
4203/* initialize jack-sensing, too */
4204static int ad1884a_laptop_init(struct hda_codec *codec)
4205{
4206 ad198x_init(codec);
4207 ad1884a_laptop_automute(codec);
4208 ad1884a_laptop_automic(codec);
4209 return 0;
4210}
4211
Takashi Iwaic5059252008-02-16 09:43:56 +01004212/* additional verbs for laptop model */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004213static const struct hda_verb ad1884a_laptop_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004214 /* Port-A (HP) pin - always unmuted */
4215 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4216 /* Port-F (int speaker) mixer - route only from analog mixer */
4217 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4218 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004219 /* Port-F (int speaker) pin */
4220 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004221 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004222 /* required for compaq 6530s/6531s speaker output */
4223 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004224 /* Port-C pin - internal mic-in */
4225 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4226 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4227 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004228 /* Port-D (docking line-out) pin - default unmuted */
4229 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004230 /* analog mix */
4231 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4232 /* unsolicited event for pin-sense */
4233 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004234 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004235 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004236 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004237 /* allow to touch GPIO1 (for mute control) */
4238 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4239 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4240 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004241 { } /* end */
4242};
4243
Takashi Iwai498f5b12011-05-02 11:33:15 +02004244static const struct hda_verb ad1884a_mobile_verbs[] = {
Takashi Iwai73156132009-04-23 08:24:48 +02004245 /* DACs; unmute as default */
4246 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4247 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4248 /* Port-A (HP) mixer - route only from analog mixer */
4249 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4250 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4251 /* Port-A pin */
4252 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4253 /* Port-A (HP) pin - always unmuted */
4254 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4255 /* Port-B (mic jack) pin */
4256 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4257 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4258 /* Port-C (int mic) pin */
4259 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4260 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4261 /* Port-F (int speaker) mixer - route only from analog mixer */
4262 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4263 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4264 /* Port-F pin */
4265 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4266 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4267 /* Analog mixer; mute as default */
4268 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4269 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4270 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4271 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4272 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4273 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4274 /* Analog Mix output amp */
4275 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4276 /* capture sources */
4277 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4278 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4279 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4280 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4281 /* unsolicited event for pin-sense */
4282 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4283 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db172009-07-02 16:10:23 +02004284 /* allow to touch GPIO1 (for mute control) */
4285 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4286 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4287 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004288 { } /* end */
4289};
4290
Takashi Iwaic5059252008-02-16 09:43:56 +01004291/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004292 * Thinkpad X300
4293 * 0x11 - HP
4294 * 0x12 - speaker
4295 * 0x14 - mic-in
4296 * 0x17 - built-in mic
4297 */
4298
Takashi Iwai498f5b12011-05-02 11:33:15 +02004299static const struct hda_verb ad1984a_thinkpad_verbs[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004300 /* HP unmute */
4301 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4302 /* analog mix */
4303 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4304 /* turn on EAPD */
4305 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4306 /* unsolicited event for pin-sense */
4307 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4308 /* internal mic - dmic */
4309 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004310 /* set magic COEFs for dmic */
4311 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4312 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004313 { } /* end */
4314};
4315
Takashi Iwai498f5b12011-05-02 11:33:15 +02004316static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004317 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4318 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4319 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4320 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4321 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4322 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004323 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4324 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004325 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4326 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4327 {
4328 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4329 .name = "Capture Source",
4330 .info = ad198x_mux_enum_info,
4331 .get = ad198x_mux_enum_get,
4332 .put = ad198x_mux_enum_put,
4333 },
4334 { } /* end */
4335};
4336
Takashi Iwai498f5b12011-05-02 11:33:15 +02004337static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004338 .num_items = 3,
4339 .items = {
4340 { "Mic", 0x0 },
4341 { "Internal Mic", 0x5 },
4342 { "Mix", 0x3 },
4343 },
4344};
4345
4346/* mute internal speaker if HP is plugged */
4347static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4348{
4349 unsigned int present;
4350
Takashi Iwaid56757a2009-11-18 08:00:14 +01004351 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004352 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4353 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4354}
4355
4356/* unsolicited event for HP jack sensing */
4357static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4358 unsigned int res)
4359{
4360 if ((res >> 26) != AD1884A_HP_EVENT)
4361 return;
4362 ad1984a_thinkpad_automute(codec);
4363}
4364
4365/* initialize jack-sensing, too */
4366static int ad1984a_thinkpad_init(struct hda_codec *codec)
4367{
4368 ad198x_init(codec);
4369 ad1984a_thinkpad_automute(codec);
4370 return 0;
4371}
4372
4373/*
David Henningsson677cd902011-02-07 15:19:34 +01004374 * Precision R5500
4375 * 0x12 - HP/line-out
4376 * 0x13 - speaker (mono)
4377 * 0x15 - mic-in
4378 */
4379
Takashi Iwai498f5b12011-05-02 11:33:15 +02004380static const struct hda_verb ad1984a_precision_verbs[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004381 /* Unmute main output path */
4382 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4383 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
4384 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
4385 /* Analog mixer; mute as default */
4386 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4387 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4388 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4389 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4390 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4391 /* Select mic as input */
4392 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
4393 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
4394 /* Configure as mic */
4395 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4396 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4397 /* HP unmute */
4398 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4399 /* turn on EAPD */
4400 {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4401 /* unsolicited event for pin-sense */
4402 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4403 { } /* end */
4404};
4405
Takashi Iwai498f5b12011-05-02 11:33:15 +02004406static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004407 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4408 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4409 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4410 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4411 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4412 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4413 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4414 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4415 HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
4416 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4417 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4418 { } /* end */
4419};
4420
4421
4422/* mute internal speaker if HP is plugged */
4423static void ad1984a_precision_automute(struct hda_codec *codec)
4424{
4425 unsigned int present;
4426
4427 present = snd_hda_jack_detect(codec, 0x12);
4428 snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
4429 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4430}
4431
4432
4433/* unsolicited event for HP jack sensing */
4434static void ad1984a_precision_unsol_event(struct hda_codec *codec,
4435 unsigned int res)
4436{
4437 if ((res >> 26) != AD1884A_HP_EVENT)
4438 return;
4439 ad1984a_precision_automute(codec);
4440}
4441
4442/* initialize jack-sensing, too */
4443static int ad1984a_precision_init(struct hda_codec *codec)
4444{
4445 ad198x_init(codec);
4446 ad1984a_precision_automute(codec);
4447 return 0;
4448}
4449
4450
4451/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004452 * HP Touchsmart
4453 * port-A (0x11) - front hp-out
4454 * port-B (0x14) - unused
4455 * port-C (0x15) - unused
4456 * port-D (0x12) - rear line out
4457 * port-E (0x1c) - front mic-in
4458 * port-F (0x16) - Internal speakers
4459 * digital-mic (0x17) - Internal mic
4460 */
4461
Takashi Iwai498f5b12011-05-02 11:33:15 +02004462static const struct hda_verb ad1984a_touchsmart_verbs[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004463 /* DACs; unmute as default */
4464 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4465 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4466 /* Port-A (HP) mixer - route only from analog mixer */
4467 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4468 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4469 /* Port-A pin */
4470 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4471 /* Port-A (HP) pin - always unmuted */
4472 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4473 /* Port-E (int speaker) mixer - route only from analog mixer */
4474 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4475 /* Port-E pin */
4476 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4477 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4478 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4479 /* Port-F (int speaker) mixer - route only from analog mixer */
4480 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4481 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4482 /* Port-F pin */
4483 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4484 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4485 /* Analog mixer; mute as default */
4486 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4487 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4488 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4489 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4490 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4491 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4492 /* Analog Mix output amp */
4493 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4494 /* capture sources */
4495 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4496 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4497 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4498 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4499 /* unsolicited event for pin-sense */
4500 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4501 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4502 /* allow to touch GPIO1 (for mute control) */
4503 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4504 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4505 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4506 /* internal mic - dmic */
4507 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4508 /* set magic COEFs for dmic */
4509 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4510 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4511 { } /* end */
4512};
4513
Takashi Iwai498f5b12011-05-02 11:33:15 +02004514static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004515 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4516/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4517 {
4518 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004519 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004520 .name = "Master Playback Switch",
4521 .info = snd_hda_mixer_amp_switch_info,
4522 .get = snd_hda_mixer_amp_switch_get,
4523 .put = ad1884a_mobile_master_sw_put,
4524 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4525 },
4526 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4527 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4528 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4529 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004530 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
4531 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004532 { } /* end */
4533};
4534
4535/* switch to external mic if plugged */
4536static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4537{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004538 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004539 snd_hda_codec_write(codec, 0x0c, 0,
4540 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004541 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004542 snd_hda_codec_write(codec, 0x0c, 0,
4543 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004544}
4545
4546
4547/* unsolicited event for HP jack sensing */
4548static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4549 unsigned int res)
4550{
4551 switch (res >> 26) {
4552 case AD1884A_HP_EVENT:
4553 ad1884a_hp_automute(codec);
4554 break;
4555 case AD1884A_MIC_EVENT:
4556 ad1984a_touchsmart_automic(codec);
4557 break;
4558 }
4559}
4560
4561/* initialize jack-sensing, too */
4562static int ad1984a_touchsmart_init(struct hda_codec *codec)
4563{
4564 ad198x_init(codec);
4565 ad1884a_hp_automute(codec);
4566 ad1984a_touchsmart_automic(codec);
4567 return 0;
4568}
4569
4570
4571/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004572 */
4573
4574enum {
4575 AD1884A_DESKTOP,
4576 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004577 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004578 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004579 AD1984A_TOUCHSMART,
David Henningsson677cd902011-02-07 15:19:34 +01004580 AD1984A_PRECISION,
Takashi Iwaic5059252008-02-16 09:43:56 +01004581 AD1884A_MODELS
4582};
4583
Takashi Iwaiea734962011-01-17 11:29:34 +01004584static const char * const ad1884a_models[AD1884A_MODELS] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004585 [AD1884A_DESKTOP] = "desktop",
4586 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004587 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004588 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004589 [AD1984A_TOUCHSMART] = "touchsmart",
David Henningsson677cd902011-02-07 15:19:34 +01004590 [AD1984A_PRECISION] = "precision",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004591};
4592
Takashi Iwai498f5b12011-05-02 11:33:15 +02004593static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004594 SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004595 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004596 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004597 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004598 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004599 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004600 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4601 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004602 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004603 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004604 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004605 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004606};
4607
4608static int patch_ad1884a(struct hda_codec *codec)
4609{
4610 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004611 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004612
4613 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4614 if (spec == NULL)
4615 return -ENOMEM;
4616
Takashi Iwaic5059252008-02-16 09:43:56 +01004617 codec->spec = spec;
4618
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004619 err = snd_hda_attach_beep_device(codec, 0x10);
4620 if (err < 0) {
4621 ad198x_free(codec);
4622 return err;
4623 }
4624 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4625
Takashi Iwaic5059252008-02-16 09:43:56 +01004626 spec->multiout.max_channels = 2;
4627 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4628 spec->multiout.dac_nids = ad1884a_dac_nids;
4629 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4630 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4631 spec->adc_nids = ad1884a_adc_nids;
4632 spec->capsrc_nids = ad1884a_capsrc_nids;
4633 spec->input_mux = &ad1884a_capture_source;
4634 spec->num_mixers = 1;
4635 spec->mixers[0] = ad1884a_base_mixers;
4636 spec->num_init_verbs = 1;
4637 spec->init_verbs[0] = ad1884a_init_verbs;
4638 spec->spdif_route = 0;
4639#ifdef CONFIG_SND_HDA_POWER_SAVE
4640 spec->loopback.amplist = ad1884a_loopbacks;
4641#endif
4642 codec->patch_ops = ad198x_patch_ops;
4643
4644 /* override some parameters */
4645 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004646 ad1884a_models,
4647 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004648 switch (board_config) {
4649 case AD1884A_LAPTOP:
4650 spec->mixers[0] = ad1884a_laptop_mixers;
4651 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4652 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004653 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4654 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004655 /* set the upper-limit for mixer amp to 0dB for avoiding the
4656 * possible damage by overloading
4657 */
4658 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4659 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4660 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4661 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4662 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004663 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004664 case AD1884A_MOBILE:
4665 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004666 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004667 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004668 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4669 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004670 /* set the upper-limit for mixer amp to 0dB for avoiding the
4671 * possible damage by overloading
4672 */
4673 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4674 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4675 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4676 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4677 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004678 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004679 case AD1884A_THINKPAD:
4680 spec->mixers[0] = ad1984a_thinkpad_mixers;
4681 spec->init_verbs[spec->num_init_verbs++] =
4682 ad1984a_thinkpad_verbs;
4683 spec->multiout.dig_out_nid = 0;
4684 spec->input_mux = &ad1984a_thinkpad_capture_source;
4685 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4686 codec->patch_ops.init = ad1984a_thinkpad_init;
4687 break;
David Henningsson677cd902011-02-07 15:19:34 +01004688 case AD1984A_PRECISION:
4689 spec->mixers[0] = ad1984a_precision_mixers;
4690 spec->init_verbs[spec->num_init_verbs++] =
4691 ad1984a_precision_verbs;
4692 spec->multiout.dig_out_nid = 0;
4693 codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
4694 codec->patch_ops.init = ad1984a_precision_init;
4695 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004696 case AD1984A_TOUCHSMART:
4697 spec->mixers[0] = ad1984a_touchsmart_mixers;
4698 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4699 spec->multiout.dig_out_nid = 0;
4700 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4701 codec->patch_ops.init = ad1984a_touchsmart_init;
4702 /* set the upper-limit for mixer amp to 0dB for avoiding the
4703 * possible damage by overloading
4704 */
4705 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4706 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4707 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4708 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4709 (1 << AC_AMPCAP_MUTE_SHIFT));
4710 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004711 }
4712
Takashi Iwai729d55b2009-12-25 22:49:01 +01004713 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004714 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004715
Takashi Iwaic5059252008-02-16 09:43:56 +01004716 return 0;
4717}
4718
4719
4720/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004721 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004722 *
4723 * port-A - front hp-out
4724 * port-B - front mic-in
4725 * port-C - rear line-in, shared surr-out (3stack)
4726 * port-D - rear line-out
4727 * port-E - rear mic-in, shared clfe-out (3stack)
4728 * port-F - rear surr-out (6stack)
4729 * port-G - rear clfe-out (6stack)
4730 */
4731
Takashi Iwai498f5b12011-05-02 11:33:15 +02004732static const hda_nid_t ad1882_dac_nids[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004733 0x04, 0x03, 0x05
4734};
4735
Takashi Iwai498f5b12011-05-02 11:33:15 +02004736static const hda_nid_t ad1882_adc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004737 0x08, 0x09,
4738};
4739
Takashi Iwai498f5b12011-05-02 11:33:15 +02004740static const hda_nid_t ad1882_capsrc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004741 0x0c, 0x0d,
4742};
4743
4744#define AD1882_SPDIF_OUT 0x02
4745
4746/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004747static const struct hda_input_mux ad1882_capture_source = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004748 .num_items = 5,
4749 .items = {
4750 { "Front Mic", 0x1 },
4751 { "Mic", 0x4 },
4752 { "Line", 0x2 },
4753 { "CD", 0x3 },
4754 { "Mix", 0x7 },
4755 },
4756};
4757
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004758/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004759static const struct hda_input_mux ad1882a_capture_source = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004760 .num_items = 5,
4761 .items = {
4762 { "Front Mic", 0x1 },
4763 { "Mic", 0x4},
4764 { "Line", 0x2 },
4765 { "Digital Mic", 0x06 },
4766 { "Mix", 0x7 },
4767 },
4768};
4769
Takashi Iwai498f5b12011-05-02 11:33:15 +02004770static const struct snd_kcontrol_new ad1882_base_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004771 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4772 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4773 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4774 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4775 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4776 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4777 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4778 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004779
David Henningsson5f99f862011-01-04 15:24:24 +01004780 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
4781 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
4782 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
Takashi Iwai0ac85512007-06-20 15:46:13 +02004783 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4784 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4785 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4786 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4787 {
4788 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4789 /* The multiple "Capture Source" controls confuse alsamixer
4790 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004791 */
4792 /* .name = "Capture Source", */
4793 .name = "Input Source",
4794 .count = 2,
4795 .info = ad198x_mux_enum_info,
4796 .get = ad198x_mux_enum_get,
4797 .put = ad198x_mux_enum_put,
4798 },
4799 /* SPDIF controls */
4800 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4801 {
4802 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4803 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4804 /* identical with ad1983 */
4805 .info = ad1983_spdif_route_info,
4806 .get = ad1983_spdif_route_get,
4807 .put = ad1983_spdif_route_put,
4808 },
4809 { } /* end */
4810};
4811
Takashi Iwai498f5b12011-05-02 11:33:15 +02004812static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004813 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4814 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4815 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4816 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4817 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4818 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4819 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4820 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004821 { } /* end */
4822};
4823
Takashi Iwai498f5b12011-05-02 11:33:15 +02004824static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004825 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4826 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4827 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4828 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4829 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4830 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4831 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4832 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004833 HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004834 { } /* end */
4835};
4836
Takashi Iwai498f5b12011-05-02 11:33:15 +02004837static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004838 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4839 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4840 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4841 {
4842 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4843 .name = "Channel Mode",
4844 .info = ad198x_ch_mode_info,
4845 .get = ad198x_ch_mode_get,
4846 .put = ad198x_ch_mode_put,
4847 },
4848 { } /* end */
4849};
4850
Takashi Iwai498f5b12011-05-02 11:33:15 +02004851static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004852 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4853 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4854 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4855 { } /* end */
4856};
4857
Takashi Iwai498f5b12011-05-02 11:33:15 +02004858static const struct hda_verb ad1882_ch2_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004859 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4860 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4861 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4862 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4863 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4864 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4865 { } /* end */
4866};
4867
Takashi Iwai498f5b12011-05-02 11:33:15 +02004868static const struct hda_verb ad1882_ch4_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004869 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4870 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4871 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4872 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4873 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4874 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4875 { } /* end */
4876};
4877
Takashi Iwai498f5b12011-05-02 11:33:15 +02004878static const struct hda_verb ad1882_ch6_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004879 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4880 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4881 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4882 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4883 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4884 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4885 { } /* end */
4886};
4887
Takashi Iwai498f5b12011-05-02 11:33:15 +02004888static const struct hda_channel_mode ad1882_modes[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004889 { 2, ad1882_ch2_init },
4890 { 4, ad1882_ch4_init },
4891 { 6, ad1882_ch6_init },
4892};
4893
4894/*
4895 * initialization verbs
4896 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004897static const struct hda_verb ad1882_init_verbs[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004898 /* DACs; mute as default */
4899 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4900 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4901 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4902 /* Port-A (HP) mixer */
4903 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4904 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4905 /* Port-A pin */
4906 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4907 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4908 /* HP selector - select DAC2 */
4909 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4910 /* Port-D (Line-out) mixer */
4911 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4912 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4913 /* Port-D pin */
4914 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4915 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4916 /* Mono-out mixer */
4917 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4918 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4919 /* Mono-out pin */
4920 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4921 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4922 /* Port-B (front mic) pin */
4923 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4924 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4925 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4926 /* Port-C (line-in) pin */
4927 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4928 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4929 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4930 /* Port-C mixer - mute as input */
4931 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4932 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4933 /* Port-E (mic-in) pin */
4934 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4935 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4936 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4937 /* Port-E mixer - mute as input */
4938 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4939 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4940 /* Port-F (surround) */
4941 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4942 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4943 /* Port-G (CLFE) */
4944 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4945 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4946 /* Analog mixer; mute as default */
4947 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4948 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4949 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4950 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4951 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4952 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4953 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4954 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4955 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4956 /* Analog Mix output amp */
4957 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4958 /* SPDIF output selector */
4959 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4960 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4961 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4962 { } /* end */
4963};
4964
Takashi Iwaicb53c622007-08-10 17:21:45 +02004965#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02004966static const struct hda_amp_list ad1882_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02004967 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4968 { 0x20, HDA_INPUT, 1 }, /* Mic */
4969 { 0x20, HDA_INPUT, 4 }, /* Line */
4970 { 0x20, HDA_INPUT, 6 }, /* CD */
4971 { } /* end */
4972};
4973#endif
4974
Takashi Iwai0ac85512007-06-20 15:46:13 +02004975/* models */
4976enum {
4977 AD1882_3STACK,
4978 AD1882_6STACK,
4979 AD1882_MODELS
4980};
4981
Takashi Iwaiea734962011-01-17 11:29:34 +01004982static const char * const ad1882_models[AD1986A_MODELS] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004983 [AD1882_3STACK] = "3stack",
4984 [AD1882_6STACK] = "6stack",
4985};
4986
4987
4988static int patch_ad1882(struct hda_codec *codec)
4989{
4990 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004991 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004992
4993 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4994 if (spec == NULL)
4995 return -ENOMEM;
4996
Takashi Iwai0ac85512007-06-20 15:46:13 +02004997 codec->spec = spec;
4998
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004999 err = snd_hda_attach_beep_device(codec, 0x10);
5000 if (err < 0) {
5001 ad198x_free(codec);
5002 return err;
5003 }
5004 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
5005
Takashi Iwai0ac85512007-06-20 15:46:13 +02005006 spec->multiout.max_channels = 6;
5007 spec->multiout.num_dacs = 3;
5008 spec->multiout.dac_nids = ad1882_dac_nids;
5009 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
5010 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
5011 spec->adc_nids = ad1882_adc_nids;
5012 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005013 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005014 spec->input_mux = &ad1882_capture_source;
5015 else
5016 spec->input_mux = &ad1882a_capture_source;
5017 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005018 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005019 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005020 spec->mixers[1] = ad1882_loopback_mixers;
5021 else
5022 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005023 spec->num_init_verbs = 1;
5024 spec->init_verbs[0] = ad1882_init_verbs;
5025 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02005026#ifdef CONFIG_SND_HDA_POWER_SAVE
5027 spec->loopback.amplist = ad1882_loopbacks;
5028#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01005029 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005030
5031 codec->patch_ops = ad198x_patch_ops;
5032
5033 /* override some parameters */
5034 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
5035 ad1882_models, NULL);
5036 switch (board_config) {
5037 default:
5038 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005039 spec->num_mixers = 3;
5040 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005041 spec->channel_mode = ad1882_modes;
5042 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
5043 spec->need_dac_fix = 1;
5044 spec->multiout.max_channels = 2;
5045 spec->multiout.num_dacs = 1;
5046 break;
5047 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005048 spec->num_mixers = 3;
5049 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005050 break;
5051 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01005052
5053 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02005054 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01005055
Takashi Iwai0ac85512007-06-20 15:46:13 +02005056 return 0;
5057}
5058
5059
5060/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07005061 * patch entries
5062 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02005063static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01005064 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02005065 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005066 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005067 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005068 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
5069 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02005070 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
5071 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005072 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005073 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01005074 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02005075 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005076 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02005077 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
5078 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005079 {} /* terminator */
5080};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01005081
5082MODULE_ALIAS("snd-hda-codec-id:11d4*");
5083
5084MODULE_LICENSE("GPL");
5085MODULE_DESCRIPTION("Analog Devices HD-audio codec");
5086
5087static struct hda_codec_preset_list analog_list = {
5088 .preset = snd_hda_preset_analog,
5089 .owner = THIS_MODULE,
5090};
5091
5092static int __init patch_analog_init(void)
5093{
5094 return snd_hda_add_codec_preset(&analog_list);
5095}
5096
5097static void __exit patch_analog_exit(void)
5098{
5099 snd_hda_delete_codec_preset(&analog_list);
5100}
5101
5102module_init(patch_analog_init)
5103module_exit(patch_analog_exit)