blob: bcb3310c394ff808a83d531cb49bdf2defcac056 [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020033struct ad198x_spec {
Takashi Iwai498f5b12011-05-02 11:33:15 +020034 const struct snd_kcontrol_new *mixers[6];
Takashi Iwai985be542005-11-02 18:26:49 +010035 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010036 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Raymond Yau28220842011-02-08 19:58:25 +080037 const struct hda_verb *init_verbs[6]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010038 * don't forget NULL termination!
39 */
40 unsigned int num_init_verbs;
41
42 /* playback */
43 struct hda_multi_out multiout; /* playback set-up
44 * max_channels, dacs must be set
45 * dig_out_nid and hp_nid are optional
46 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010047 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020048 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010049
Takashi Iwai498f5b12011-05-02 11:33:15 +020050 const hda_nid_t *alt_dac_nid;
51 const struct hda_pcm_stream *stream_analog_alt_playback;
Raymond Yau34588702011-09-23 19:03:25 +080052 int independent_hp;
53 int num_active_streams;
Raymond Yauc66ddf32011-01-17 11:19:03 +010054
Takashi Iwai985be542005-11-02 18:26:49 +010055 /* capture */
56 unsigned int num_adc_nids;
Takashi Iwai498f5b12011-05-02 11:33:15 +020057 const hda_nid_t *adc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010058 hda_nid_t dig_in_nid; /* digital-in NID; optional */
59
60 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020061 const struct hda_input_mux *input_mux;
Takashi Iwai498f5b12011-05-02 11:33:15 +020062 const hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010063 unsigned int cur_mux[3];
64
65 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010066 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010067 int num_channel_mode;
68
69 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020070 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010071
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020072 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010073
74 /* dynamic controls, init_verbs and input_mux */
75 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020076 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010077 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020078 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020079
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010080 unsigned int jack_present: 1;
81 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
82 unsigned int inv_eapd: 1; /* inverted EAPD implementation */
83 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020084
Takashi Iwaicb53c622007-08-10 17:21:45 +020085#ifdef CONFIG_SND_HDA_POWER_SAVE
86 struct hda_loopback_check loopback;
87#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010088 /* for virtual master */
89 hda_nid_t vmaster_nid;
Takashi Iwaiea734962011-01-17 11:29:34 +010090 const char * const *slave_vols;
91 const char * const *slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092};
93
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020094/*
95 * input MUX handling (common part)
96 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +010097static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020098{
99 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
100 struct ad198x_spec *spec = codec->spec;
101
102 return snd_hda_input_mux_info(spec->input_mux, uinfo);
103}
104
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100105static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200106{
107 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
108 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100109 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200110
Takashi Iwai985be542005-11-02 18:26:49 +0100111 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200112 return 0;
113}
114
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100115static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200116{
117 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
118 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100119 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200120
121 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100122 spec->capsrc_nids[adc_idx],
123 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200124}
125
126/*
127 * initialization (common callbacks)
128 */
129static int ad198x_init(struct hda_codec *codec)
130{
131 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100132 int i;
133
134 for (i = 0; i < spec->num_init_verbs; i++)
135 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200136 return 0;
137}
138
Takashi Iwaiea734962011-01-17 11:29:34 +0100139static const char * const ad_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100140 "Front Playback Volume",
141 "Surround Playback Volume",
142 "Center Playback Volume",
143 "LFE Playback Volume",
144 "Side Playback Volume",
145 "Headphone Playback Volume",
146 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100147 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100148 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100149 NULL
150};
151
Takashi Iwaiea734962011-01-17 11:29:34 +0100152static const char * const ad_slave_sws[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100153 "Front Playback Switch",
154 "Surround Playback Switch",
155 "Center Playback Switch",
156 "LFE Playback Switch",
157 "Side Playback Switch",
158 "Headphone Playback Switch",
159 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100160 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100161 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100162 NULL
163};
164
Takashi Iwaiea734962011-01-17 11:29:34 +0100165static const char * const ad1988_6stack_fp_slave_vols[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100166 "Front Playback Volume",
167 "Surround Playback Volume",
168 "Center Playback Volume",
169 "LFE Playback Volume",
170 "Side Playback Volume",
171 "IEC958 Playback Volume",
172 NULL
173};
174
Takashi Iwaiea734962011-01-17 11:29:34 +0100175static const char * const ad1988_6stack_fp_slave_sws[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100176 "Front Playback Switch",
177 "Surround Playback Switch",
178 "Center Playback Switch",
179 "LFE Playback Switch",
180 "Side Playback Switch",
181 "IEC958 Playback Switch",
182 NULL
183};
Takashi Iwai603c4012008-07-30 15:01:44 +0200184static void ad198x_free_kctls(struct hda_codec *codec);
185
Takashi Iwai67d634c2009-11-16 15:35:59 +0100186#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100187/* additional beep mixers; the actual parameters are overwritten at build */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200188static const struct snd_kcontrol_new ad_beep_mixer[] = {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100189 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200190 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100191 { } /* end */
192};
193
Takashi Iwai498f5b12011-05-02 11:33:15 +0200194static const struct snd_kcontrol_new ad_beep2_mixer[] = {
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100195 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
196 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
197 { } /* end */
198};
199
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100200#define set_beep_amp(spec, nid, idx, dir) \
201 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100202#else
203#define set_beep_amp(spec, nid, idx, dir) /* NOP */
204#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100205
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200206static int ad198x_build_controls(struct hda_codec *codec)
207{
208 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100209 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100210 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200211 int err;
212
Takashi Iwai985be542005-11-02 18:26:49 +0100213 for (i = 0; i < spec->num_mixers; i++) {
214 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
215 if (err < 0)
216 return err;
217 }
218 if (spec->multiout.dig_out_nid) {
Stephen Warren74b654c2011-06-01 11:14:18 -0600219 err = snd_hda_create_spdif_out_ctls(codec,
220 spec->multiout.dig_out_nid,
221 spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100222 if (err < 0)
223 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100224 err = snd_hda_create_spdif_share_sw(codec,
225 &spec->multiout);
226 if (err < 0)
227 return err;
228 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100229 }
230 if (spec->dig_in_nid) {
231 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
232 if (err < 0)
233 return err;
234 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100235
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100236 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100237#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100238 if (spec->beep_amp) {
Takashi Iwai498f5b12011-05-02 11:33:15 +0200239 const struct snd_kcontrol_new *knew;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100240 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
241 for ( ; knew->name; knew++) {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100242 struct snd_kcontrol *kctl;
243 kctl = snd_ctl_new1(knew, codec);
244 if (!kctl)
245 return -ENOMEM;
246 kctl->private_value = spec->beep_amp;
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100247 err = snd_hda_ctl_add(codec, 0, kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100248 if (err < 0)
249 return err;
250 }
251 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100252#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100253
Takashi Iwai2134ea42008-01-10 16:53:55 +0100254 /* if we have no master control, let's create it */
255 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100256 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100257 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100258 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100259 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100260 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100261 (spec->slave_vols ?
262 spec->slave_vols : ad_slave_vols));
263 if (err < 0)
264 return err;
265 }
266 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
267 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
268 NULL,
269 (spec->slave_sws ?
270 spec->slave_sws : ad_slave_sws));
271 if (err < 0)
272 return err;
273 }
274
Takashi Iwai603c4012008-07-30 15:01:44 +0200275 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100276
277 /* assign Capture Source enums to NID */
278 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
279 if (!kctl)
280 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
281 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100282 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100283 if (err < 0)
284 return err;
285 }
286
287 /* assign IEC958 enums to NID */
288 kctl = snd_hda_find_mixer_ctl(codec,
289 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
290 if (kctl) {
291 err = snd_hda_add_nid(codec, kctl, 0,
292 spec->multiout.dig_out_nid);
293 if (err < 0)
294 return err;
295 }
296
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200297 return 0;
298}
299
Takashi Iwaicb53c622007-08-10 17:21:45 +0200300#ifdef CONFIG_SND_HDA_POWER_SAVE
301static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
302{
303 struct ad198x_spec *spec = codec->spec;
304 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
305}
306#endif
307
Raymond Yau34588702011-09-23 19:03:25 +0800308static void activate_ctl(struct hda_codec *codec, const char *name, int active)
309{
310 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
311 if (ctl) {
312 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
313 ctl->vd[0].access |= active ? 0 :
314 SNDRV_CTL_ELEM_ACCESS_INACTIVE;
315 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
316 ctl->vd[0].access |= active ?
317 SNDRV_CTL_ELEM_ACCESS_WRITE : 0;
318 snd_ctl_notify(codec->bus->card,
319 SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
320 }
321}
322
323static void set_stream_active(struct hda_codec *codec, bool active)
324{
325 struct ad198x_spec *spec = codec->spec;
326 if (active)
327 spec->num_active_streams++;
328 else
329 spec->num_active_streams--;
330 activate_ctl(codec, "Independent HP", spec->num_active_streams == 0);
331}
332
333static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol,
334 struct snd_ctl_elem_info *uinfo)
335{
336 static const char * const texts[] = { "OFF", "ON", NULL};
337 int index;
338 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
339 uinfo->count = 1;
340 uinfo->value.enumerated.items = 2;
341 index = uinfo->value.enumerated.item;
342 if (index >= 2)
343 index = 1;
344 strcpy(uinfo->value.enumerated.name, texts[index]);
345 return 0;
346}
347
348static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol,
349 struct snd_ctl_elem_value *ucontrol)
350{
351 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
352 struct ad198x_spec *spec = codec->spec;
353 ucontrol->value.enumerated.item[0] = spec->independent_hp;
354 return 0;
355}
356
357static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol,
358 struct snd_ctl_elem_value *ucontrol)
359{
360 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
361 struct ad198x_spec *spec = codec->spec;
362 unsigned int select = ucontrol->value.enumerated.item[0];
363 if (spec->independent_hp != select) {
364 spec->independent_hp = select;
365 if (spec->independent_hp)
366 spec->multiout.hp_nid = 0;
367 else
368 spec->multiout.hp_nid = spec->alt_dac_nid[0];
369 return 1;
370 }
371 return 0;
372}
373
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200374/*
375 * Analog playback callbacks
376 */
377static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
378 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100379 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200380{
381 struct ad198x_spec *spec = codec->spec;
Raymond Yau34588702011-09-23 19:03:25 +0800382 int err;
383 set_stream_active(codec, true);
384 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
Takashi Iwai9a081602008-02-12 18:37:26 +0100385 hinfo);
Raymond Yau34588702011-09-23 19:03:25 +0800386 if (err < 0) {
387 set_stream_active(codec, false);
388 return err;
389 }
390 return 0;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200391}
392
393static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
394 struct hda_codec *codec,
395 unsigned int stream_tag,
396 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100397 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200398{
399 struct ad198x_spec *spec = codec->spec;
400 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
401 format, substream);
402}
403
404static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
405 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100406 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200407{
408 struct ad198x_spec *spec = codec->spec;
409 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
410}
411
Raymond Yau34588702011-09-23 19:03:25 +0800412static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo,
413 struct hda_codec *codec,
414 struct snd_pcm_substream *substream)
415{
416 set_stream_active(codec, false);
417 return 0;
418}
419
420static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
421 struct hda_codec *codec,
422 struct snd_pcm_substream *substream)
423{
424 struct ad198x_spec *spec = codec->spec;
425 if (!spec->independent_hp)
426 return -EBUSY;
427 set_stream_active(codec, true);
428 return 0;
429}
430
431static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
432 struct hda_codec *codec,
433 struct snd_pcm_substream *substream)
434{
435 set_stream_active(codec, false);
436 return 0;
437}
438
Takashi Iwai498f5b12011-05-02 11:33:15 +0200439static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100440 .substreams = 1,
441 .channels_min = 2,
442 .channels_max = 2,
Raymond Yau34588702011-09-23 19:03:25 +0800443 .ops = {
444 .open = ad1988_alt_playback_pcm_open,
445 .close = ad1988_alt_playback_pcm_close
446 },
Raymond Yauc66ddf32011-01-17 11:19:03 +0100447};
448
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200449/*
450 * Digital out
451 */
452static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
453 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100454 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200455{
456 struct ad198x_spec *spec = codec->spec;
457 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
458}
459
460static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
461 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100462 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200463{
464 struct ad198x_spec *spec = codec->spec;
465 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
466}
467
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200468static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
469 struct hda_codec *codec,
470 unsigned int stream_tag,
471 unsigned int format,
472 struct snd_pcm_substream *substream)
473{
474 struct ad198x_spec *spec = codec->spec;
475 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
476 format, substream);
477}
478
Takashi Iwai9411e212009-02-13 11:32:28 +0100479static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
480 struct hda_codec *codec,
481 struct snd_pcm_substream *substream)
482{
483 struct ad198x_spec *spec = codec->spec;
484 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
485}
486
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200487/*
488 * Analog capture
489 */
490static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
491 struct hda_codec *codec,
492 unsigned int stream_tag,
493 unsigned int format,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100494 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200495{
496 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100497 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
498 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200499 return 0;
500}
501
502static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
503 struct hda_codec *codec,
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +0100504 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200505{
506 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100507 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200508 return 0;
509}
510
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200511/*
512 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200513static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200514 .substreams = 1,
515 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100516 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200517 .nid = 0, /* fill later */
518 .ops = {
519 .open = ad198x_playback_pcm_open,
520 .prepare = ad198x_playback_pcm_prepare,
Raymond Yau34588702011-09-23 19:03:25 +0800521 .cleanup = ad198x_playback_pcm_cleanup,
522 .close = ad198x_playback_pcm_close
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200523 },
524};
525
Takashi Iwai498f5b12011-05-02 11:33:15 +0200526static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100527 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200528 .channels_min = 2,
529 .channels_max = 2,
530 .nid = 0, /* fill later */
531 .ops = {
532 .prepare = ad198x_capture_pcm_prepare,
533 .cleanup = ad198x_capture_pcm_cleanup
534 },
535};
536
Takashi Iwai498f5b12011-05-02 11:33:15 +0200537static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200538 .substreams = 1,
539 .channels_min = 2,
540 .channels_max = 2,
541 .nid = 0, /* fill later */
542 .ops = {
543 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200544 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100545 .prepare = ad198x_dig_playback_pcm_prepare,
546 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200547 },
548};
549
Takashi Iwai498f5b12011-05-02 11:33:15 +0200550static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100551 .substreams = 1,
552 .channels_min = 2,
553 .channels_max = 2,
554 /* NID is set in alc_build_pcms */
555};
556
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200557static int ad198x_build_pcms(struct hda_codec *codec)
558{
559 struct ad198x_spec *spec = codec->spec;
560 struct hda_pcm *info = spec->pcm_rec;
561
562 codec->num_pcms = 1;
563 codec->pcm_info = info;
564
565 info->name = "AD198x Analog";
566 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
567 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
568 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
569 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100570 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
571 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200572
573 if (spec->multiout.dig_out_nid) {
574 info++;
575 codec->num_pcms++;
576 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100577 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200578 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
579 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100580 if (spec->dig_in_nid) {
581 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
582 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
583 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200584 }
585
Raymond Yauc66ddf32011-01-17 11:19:03 +0100586 if (spec->alt_dac_nid && spec->stream_analog_alt_playback) {
587 codec->num_pcms++;
588 info = spec->pcm_rec + 2;
589 info->name = "AD198x Headphone";
590 info->pcm_type = HDA_PCM_TYPE_AUDIO;
591 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
592 *spec->stream_analog_alt_playback;
593 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
594 spec->alt_dac_nid[0];
595 }
596
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200597 return 0;
598}
599
Takashi Iwai603c4012008-07-30 15:01:44 +0200600static void ad198x_free_kctls(struct hda_codec *codec)
601{
602 struct ad198x_spec *spec = codec->spec;
603
604 if (spec->kctls.list) {
605 struct snd_kcontrol_new *kctl = spec->kctls.list;
606 int i;
607 for (i = 0; i < spec->kctls.used; i++)
608 kfree(kctl[i].name);
609 }
610 snd_array_free(&spec->kctls);
611}
612
Daniel T Chenea52bf22009-12-27 18:48:29 -0500613static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
614 hda_nid_t hp)
615{
616 struct ad198x_spec *spec = codec->spec;
Raymond Yaua01ef052011-06-01 15:09:48 +0800617 if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
618 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500619 !spec->inv_eapd ? 0x00 : 0x02);
Raymond Yaua01ef052011-06-01 15:09:48 +0800620 if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
621 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500622 !spec->inv_eapd ? 0x00 : 0x02);
623}
624
625static void ad198x_power_eapd(struct hda_codec *codec)
626{
627 /* We currently only handle front, HP */
628 switch (codec->vendor_id) {
629 case 0x11d41882:
630 case 0x11d4882a:
631 case 0x11d41884:
632 case 0x11d41984:
633 case 0x11d41883:
634 case 0x11d4184a:
635 case 0x11d4194a:
636 case 0x11d4194b:
Takashi Iwai4dffbe02011-06-03 10:05:02 +0200637 case 0x11d41988:
638 case 0x11d4198b:
639 case 0x11d4989a:
640 case 0x11d4989b:
Daniel T Chenea52bf22009-12-27 18:48:29 -0500641 ad198x_power_eapd_write(codec, 0x12, 0x11);
642 break;
643 case 0x11d41981:
644 case 0x11d41983:
645 ad198x_power_eapd_write(codec, 0x05, 0x06);
646 break;
647 case 0x11d41986:
648 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
649 break;
Daniel T Chenea52bf22009-12-27 18:48:29 -0500650 }
651}
652
Takashi Iwai0da26922011-04-26 15:18:33 +0200653static void ad198x_shutup(struct hda_codec *codec)
654{
655 snd_hda_shutup_pins(codec);
656 ad198x_power_eapd(codec);
657}
658
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200659static void ad198x_free(struct hda_codec *codec)
660{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100661 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100662
Takashi Iwai603c4012008-07-30 15:01:44 +0200663 if (!spec)
664 return;
665
Daniel T Chenea52bf22009-12-27 18:48:29 -0500666 ad198x_shutup(codec);
Takashi Iwai603c4012008-07-30 15:01:44 +0200667 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100668 kfree(spec);
669 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200670}
671
Takashi Iwai2a439522011-07-26 09:52:50 +0200672#ifdef CONFIG_PM
Daniel T Chenea52bf22009-12-27 18:48:29 -0500673static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
674{
675 ad198x_shutup(codec);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500676 return 0;
677}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500678#endif
679
Takashi Iwai498f5b12011-05-02 11:33:15 +0200680static const struct hda_codec_ops ad198x_patch_ops = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200681 .build_controls = ad198x_build_controls,
682 .build_pcms = ad198x_build_pcms,
683 .init = ad198x_init,
684 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200685#ifdef CONFIG_SND_HDA_POWER_SAVE
686 .check_power_status = ad198x_check_power_status,
687#endif
Takashi Iwai2a439522011-07-26 09:52:50 +0200688#ifdef CONFIG_PM
Daniel T Chenea52bf22009-12-27 18:48:29 -0500689 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500690#endif
691 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200692};
693
694
695/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100696 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100697 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100698 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200699#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100700
701static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
702 struct snd_ctl_elem_value *ucontrol)
703{
704 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
705 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100706 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100707 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
708 else
709 ucontrol->value.integer.value[0] = spec->cur_eapd;
710 return 0;
711}
712
713static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
714 struct snd_ctl_elem_value *ucontrol)
715{
716 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
717 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100718 hda_nid_t nid = kcontrol->private_value & 0xff;
719 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100720 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100721 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100722 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200723 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100724 return 0;
725 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200726 snd_hda_codec_write_cache(codec, nid,
727 0, AC_VERB_SET_EAPD_BTLENABLE,
728 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100729 return 1;
730}
731
Takashi Iwai9230d212006-03-13 13:49:49 +0100732static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
733 struct snd_ctl_elem_info *uinfo);
734static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
735 struct snd_ctl_elem_value *ucontrol);
736static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
737 struct snd_ctl_elem_value *ucontrol);
738
739
Takashi Iwai18a815d2006-03-01 19:54:39 +0100740/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200741 * AD1986A specific
742 */
743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744#define AD1986A_SPDIF_OUT 0x02
745#define AD1986A_FRONT_DAC 0x03
746#define AD1986A_SURR_DAC 0x04
747#define AD1986A_CLFE_DAC 0x05
748#define AD1986A_ADC 0x06
749
Takashi Iwai498f5b12011-05-02 11:33:15 +0200750static const hda_nid_t ad1986a_dac_nids[3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
752};
Takashi Iwai498f5b12011-05-02 11:33:15 +0200753static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
754static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Takashi Iwai498f5b12011-05-02 11:33:15 +0200756static const struct hda_input_mux ad1986a_capture_source = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 .num_items = 7,
758 .items = {
759 { "Mic", 0x0 },
760 { "CD", 0x1 },
761 { "Aux", 0x3 },
762 { "Line", 0x4 },
763 { "Mix", 0x5 },
764 { "Mono", 0x6 },
765 { "Phone", 0x7 },
766 },
767};
768
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Takashi Iwai498f5b12011-05-02 11:33:15 +0200770static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200771 .ops = &snd_hda_bind_vol,
772 .values = {
773 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
774 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
775 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
776 0
777 },
778};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Takashi Iwai498f5b12011-05-02 11:33:15 +0200780static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200781 .ops = &snd_hda_bind_sw,
782 .values = {
783 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
784 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
785 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
786 0
787 },
788};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
790/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 * mixers
792 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200793static const struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200794 /*
795 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
796 */
797 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
798 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
800 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
801 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
802 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
803 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
804 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
805 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
806 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
807 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
808 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
809 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
810 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
811 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
812 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
813 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
814 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
815 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
816 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100817 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
819 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
820 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
821 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
822 {
823 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
824 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200825 .info = ad198x_mux_enum_info,
826 .get = ad198x_mux_enum_get,
827 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 },
829 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
830 { } /* end */
831};
832
Takashi Iwai9230d212006-03-13 13:49:49 +0100833/* additional mixers for 3stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200834static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100835 {
836 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
837 .name = "Channel Mode",
838 .info = ad198x_ch_mode_info,
839 .get = ad198x_ch_mode_get,
840 .put = ad198x_ch_mode_put,
841 },
842 { } /* end */
843};
844
845/* laptop model - 2ch only */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200846static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
Takashi Iwai9230d212006-03-13 13:49:49 +0100847
Takashi Iwai20a45e82007-08-15 22:20:45 +0200848/* master controls both pins 0x1a and 0x1b */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200849static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200850 .ops = &snd_hda_bind_vol,
851 .values = {
852 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
853 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
854 0,
855 },
856};
857
Takashi Iwai498f5b12011-05-02 11:33:15 +0200858static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200859 .ops = &snd_hda_bind_sw,
860 .values = {
861 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
862 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
863 0,
864 },
865};
866
Takashi Iwai498f5b12011-05-02 11:33:15 +0200867static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100868 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
869 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200870 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
871 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100872 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
873 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
874 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
875 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
876 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
877 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
878 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
879 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100880 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100881 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100882 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
883 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
884 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
885 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
886 {
887 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
888 .name = "Capture Source",
889 .info = ad198x_mux_enum_info,
890 .get = ad198x_mux_enum_get,
891 .put = ad198x_mux_enum_put,
892 },
893 { } /* end */
894};
895
Takashi Iwai825aa972006-03-17 10:50:49 +0100896/* laptop-eapd model - 2ch only */
897
Takashi Iwai498f5b12011-05-02 11:33:15 +0200898static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100899 .num_items = 3,
900 .items = {
901 { "Mic", 0x0 },
902 { "Internal Mic", 0x4 },
903 { "Mix", 0x5 },
904 },
905};
906
Takashi Iwai498f5b12011-05-02 11:33:15 +0200907static const struct hda_input_mux ad1986a_automic_capture_source = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100908 .num_items = 2,
909 .items = {
910 { "Mic", 0x0 },
911 { "Mix", 0x5 },
912 },
913};
914
Takashi Iwai498f5b12011-05-02 11:33:15 +0200915static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200916 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
917 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200918 { } /* end */
919};
920
Takashi Iwai498f5b12011-05-02 11:33:15 +0200921static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa972006-03-17 10:50:49 +0100922 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
923 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100924 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
925 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100926 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100927 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
928 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
929 {
930 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
931 .name = "Capture Source",
932 .info = ad198x_mux_enum_info,
933 .get = ad198x_mux_enum_get,
934 .put = ad198x_mux_enum_put,
935 },
936 {
937 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
938 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100939 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100940 .info = ad198x_eapd_info,
941 .get = ad198x_eapd_get,
942 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100943 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100944 },
945 { } /* end */
946};
947
Takashi Iwai498f5b12011-05-02 11:33:15 +0200948static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
Takashi Iwai16d11a82009-06-24 14:07:53 +0200949 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
950 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa972006-03-17 10:50:49 +0100951 { } /* end */
952};
953
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100954/* re-connect the mic boost input according to the jack sensing */
955static void ad1986a_automic(struct hda_codec *codec)
956{
957 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100958 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100959 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
960 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100961 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100962}
963
964#define AD1986A_MIC_EVENT 0x36
965
966static void ad1986a_automic_unsol_event(struct hda_codec *codec,
967 unsigned int res)
968{
969 if ((res >> 26) != AD1986A_MIC_EVENT)
970 return;
971 ad1986a_automic(codec);
972}
973
974static int ad1986a_automic_init(struct hda_codec *codec)
975{
976 ad198x_init(codec);
977 ad1986a_automic(codec);
978 return 0;
979}
980
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200981/* laptop-automute - 2ch only */
982
983static void ad1986a_update_hp(struct hda_codec *codec)
984{
985 struct ad198x_spec *spec = codec->spec;
986 unsigned int mute;
987
988 if (spec->jack_present)
989 mute = HDA_AMP_MUTE; /* mute internal speaker */
990 else
991 /* unmute internal speaker if necessary */
992 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
993 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
994 HDA_AMP_MUTE, mute);
995}
996
997static void ad1986a_hp_automute(struct hda_codec *codec)
998{
999 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001000
Takashi Iwaid56757a2009-11-18 08:00:14 +01001001 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +02001002 if (spec->inv_jack_detect)
1003 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001004 ad1986a_update_hp(codec);
1005}
1006
1007#define AD1986A_HP_EVENT 0x37
1008
1009static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
1010{
1011 if ((res >> 26) != AD1986A_HP_EVENT)
1012 return;
1013 ad1986a_hp_automute(codec);
1014}
1015
1016static int ad1986a_hp_init(struct hda_codec *codec)
1017{
1018 ad198x_init(codec);
1019 ad1986a_hp_automute(codec);
1020 return 0;
1021}
1022
1023/* bind hp and internal speaker mute (with plug check) */
1024static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1025 struct snd_ctl_elem_value *ucontrol)
1026{
1027 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1028 long *valp = ucontrol->value.integer.value;
1029 int change;
1030
1031 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
1032 HDA_AMP_MUTE,
1033 valp[0] ? 0 : HDA_AMP_MUTE);
1034 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
1035 HDA_AMP_MUTE,
1036 valp[1] ? 0 : HDA_AMP_MUTE);
1037 if (change)
1038 ad1986a_update_hp(codec);
1039 return change;
1040}
1041
Takashi Iwai498f5b12011-05-02 11:33:15 +02001042static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001043 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
1044 {
1045 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1046 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01001047 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001048 .info = snd_hda_mixer_amp_switch_info,
1049 .get = snd_hda_mixer_amp_switch_get,
1050 .put = ad1986a_hp_master_sw_put,
1051 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
1052 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001053 { } /* end */
1054};
1055
Takashi Iwai16d11a82009-06-24 14:07:53 +02001056
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057/*
1058 * initialization verbs
1059 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001060static const struct hda_verb ad1986a_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 /* Front, Surround, CLFE DAC; mute as default */
1062 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1063 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1064 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1065 /* Downmix - off */
1066 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1067 /* HP, Line-Out, Surround, CLFE selectors */
1068 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
1069 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
1070 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1071 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1072 /* Mono selector */
1073 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
1074 /* Mic selector: Mic 1/2 pin */
1075 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1076 /* Line-in selector: Line-in */
1077 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
1078 /* Mic 1/2 swap */
1079 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
1080 /* Record selector: mic */
1081 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
1082 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
1083 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1084 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1085 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1086 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1087 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1088 /* PC beep */
1089 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
1090 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
1091 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1092 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1093 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1094 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1095 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001096 /* HP Pin */
1097 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1098 /* Front, Surround, CLFE Pins */
1099 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1100 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1101 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1102 /* Mono Pin */
1103 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1104 /* Mic Pin */
1105 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1106 /* Line, Aux, CD, Beep-In Pin */
1107 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1108 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1109 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1110 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1111 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 { } /* end */
1113};
1114
Takashi Iwai498f5b12011-05-02 11:33:15 +02001115static const struct hda_verb ad1986a_ch2_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001116 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001117 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1118 /* Line-in selectors */
1119 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001120 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001121 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1122 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
1123 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001124 { } /* end */
1125};
1126
Takashi Iwai498f5b12011-05-02 11:33:15 +02001127static const struct hda_verb ad1986a_ch4_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001128 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001129 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1130 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001131 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001132 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1133 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001134 { } /* end */
1135};
1136
Takashi Iwai498f5b12011-05-02 11:33:15 +02001137static const struct hda_verb ad1986a_ch6_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001138 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001139 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1140 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001141 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001142 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1143 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001144 { } /* end */
1145};
1146
Takashi Iwai498f5b12011-05-02 11:33:15 +02001147static const struct hda_channel_mode ad1986a_modes[3] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001148 { 2, ad1986a_ch2_init },
1149 { 4, ad1986a_ch4_init },
1150 { 6, ad1986a_ch6_init },
1151};
1152
Takashi Iwai825aa972006-03-17 10:50:49 +01001153/* eapd initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001154static const struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001155 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa972006-03-17 10:50:49 +01001156 {}
1157};
1158
Takashi Iwai498f5b12011-05-02 11:33:15 +02001159static const struct hda_verb ad1986a_automic_verbs[] = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001160 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1161 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1162 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1163 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1164 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1165 {}
1166};
1167
Tobin Davisf36090f2007-01-08 11:07:12 +01001168/* Ultra initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001169static const struct hda_verb ad1986a_ultra_init[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001170 /* eapd initialization */
1171 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1172 /* CLFE -> Mic in */
1173 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1174 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1175 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1176 { } /* end */
1177};
1178
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001179/* pin sensing on HP jack */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001180static const struct hda_verb ad1986a_hp_init_verbs[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001181 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1182 {}
1183};
1184
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001185static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1186 unsigned int res)
1187{
1188 switch (res >> 26) {
1189 case AD1986A_HP_EVENT:
1190 ad1986a_hp_automute(codec);
1191 break;
1192 case AD1986A_MIC_EVENT:
1193 ad1986a_automic(codec);
1194 break;
1195 }
1196}
1197
1198static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1199{
1200 ad198x_init(codec);
1201 ad1986a_hp_automute(codec);
1202 ad1986a_automic(codec);
1203 return 0;
1204}
1205
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001206
Takashi Iwai9230d212006-03-13 13:49:49 +01001207/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001208enum {
1209 AD1986A_6STACK,
1210 AD1986A_3STACK,
1211 AD1986A_LAPTOP,
1212 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001213 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001214 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001215 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001216 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001217 AD1986A_MODELS
1218};
Takashi Iwai9230d212006-03-13 13:49:49 +01001219
Takashi Iwaiea734962011-01-17 11:29:34 +01001220static const char * const ad1986a_models[AD1986A_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001221 [AD1986A_6STACK] = "6stack",
1222 [AD1986A_3STACK] = "3stack",
1223 [AD1986A_LAPTOP] = "laptop",
1224 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001225 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001226 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001227 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001228 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001229};
1230
Takashi Iwai498f5b12011-05-02 11:33:15 +02001231static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001232 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001233 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001234 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001235 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001236 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1237 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1238 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1239 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001240 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001241 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001242 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1243 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1244 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1245 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1246 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001247 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001248 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001249 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001250 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001251 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001252 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001253 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001254 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001255 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001256 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001257 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001258 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001259 {}
1260};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
Takashi Iwaicb53c622007-08-10 17:21:45 +02001262#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001263static const struct hda_amp_list ad1986a_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001264 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1265 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1266 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1267 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1268 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1269 { } /* end */
1270};
1271#endif
1272
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001273static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1274{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001275 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001276 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1277}
1278
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279static int patch_ad1986a(struct hda_codec *codec)
1280{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001281 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001282 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001284 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 if (spec == NULL)
1286 return -ENOMEM;
1287
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 codec->spec = spec;
1289
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001290 err = snd_hda_attach_beep_device(codec, 0x19);
1291 if (err < 0) {
1292 ad198x_free(codec);
1293 return err;
1294 }
1295 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 spec->multiout.max_channels = 6;
1298 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1299 spec->multiout.dac_nids = ad1986a_dac_nids;
1300 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001301 spec->num_adc_nids = 1;
1302 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001303 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001304 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001305 spec->num_mixers = 1;
1306 spec->mixers[0] = ad1986a_mixers;
1307 spec->num_init_verbs = 1;
1308 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001309#ifdef CONFIG_SND_HDA_POWER_SAVE
1310 spec->loopback.amplist = ad1986a_loopbacks;
1311#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001312 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001313 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001315 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
Takashi Iwai9230d212006-03-13 13:49:49 +01001317 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001318 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1319 ad1986a_models,
1320 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001321 switch (board_config) {
1322 case AD1986A_3STACK:
1323 spec->num_mixers = 2;
1324 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001325 spec->num_init_verbs = 2;
1326 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001327 spec->channel_mode = ad1986a_modes;
1328 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001329 spec->need_dac_fix = 1;
1330 spec->multiout.max_channels = 2;
1331 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001332 break;
1333 case AD1986A_LAPTOP:
1334 spec->mixers[0] = ad1986a_laptop_mixers;
1335 spec->multiout.max_channels = 2;
1336 spec->multiout.num_dacs = 1;
1337 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1338 break;
Takashi Iwai825aa972006-03-17 10:50:49 +01001339 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001340 spec->num_mixers = 3;
1341 spec->mixers[0] = ad1986a_laptop_master_mixers;
1342 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1343 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001344 spec->num_init_verbs = 2;
1345 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1346 spec->multiout.max_channels = 2;
1347 spec->multiout.num_dacs = 1;
1348 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1349 if (!is_jack_available(codec, 0x25))
1350 spec->multiout.dig_out_nid = 0;
1351 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1352 break;
1353 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001354 spec->num_mixers = 2;
1355 spec->mixers[0] = ad1986a_laptop_master_mixers;
1356 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001357 spec->num_init_verbs = 3;
Takashi Iwai825aa972006-03-17 10:50:49 +01001358 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001359 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa972006-03-17 10:50:49 +01001360 spec->multiout.max_channels = 2;
1361 spec->multiout.num_dacs = 1;
1362 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001363 if (!is_jack_available(codec, 0x25))
1364 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001365 spec->input_mux = &ad1986a_automic_capture_source;
1366 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1367 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa972006-03-17 10:50:49 +01001368 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001369 case AD1986A_SAMSUNG_P50:
1370 spec->num_mixers = 2;
1371 spec->mixers[0] = ad1986a_automute_master_mixers;
1372 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1373 spec->num_init_verbs = 4;
1374 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1375 spec->init_verbs[2] = ad1986a_automic_verbs;
1376 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1377 spec->multiout.max_channels = 2;
1378 spec->multiout.num_dacs = 1;
1379 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1380 if (!is_jack_available(codec, 0x25))
1381 spec->multiout.dig_out_nid = 0;
1382 spec->input_mux = &ad1986a_automic_capture_source;
1383 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1384 codec->patch_ops.init = ad1986a_samsung_p50_init;
1385 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001386 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001387 spec->num_mixers = 3;
1388 spec->mixers[0] = ad1986a_automute_master_mixers;
1389 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1390 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001391 spec->num_init_verbs = 3;
1392 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1393 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1394 spec->multiout.max_channels = 2;
1395 spec->multiout.num_dacs = 1;
1396 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001397 if (!is_jack_available(codec, 0x25))
1398 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001399 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1400 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1401 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001402 /* Lenovo N100 seems to report the reversed bit
1403 * for HP jack-sensing
1404 */
1405 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001406 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001407 case AD1986A_ULTRA:
1408 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1409 spec->num_init_verbs = 2;
1410 spec->init_verbs[1] = ad1986a_ultra_init;
1411 spec->multiout.max_channels = 2;
1412 spec->multiout.num_dacs = 1;
1413 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1414 spec->multiout.dig_out_nid = 0;
1415 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001416 }
1417
Takashi Iwaid29240c2007-10-26 12:35:56 +02001418 /* AD1986A has a hardware problem that it can't share a stream
1419 * with multiple output pins. The copy of front to surrounds
1420 * causes noisy or silent outputs at a certain timing, e.g.
1421 * changing the volume.
1422 * So, let's disable the shared stream.
1423 */
1424 spec->multiout.no_share_stream = 1;
1425
Takashi Iwai729d55b2009-12-25 22:49:01 +01001426 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001427 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001428
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 return 0;
1430}
1431
1432/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001433 * AD1983 specific
1434 */
1435
1436#define AD1983_SPDIF_OUT 0x02
1437#define AD1983_DAC 0x03
1438#define AD1983_ADC 0x04
1439
Takashi Iwai498f5b12011-05-02 11:33:15 +02001440static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
1441static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
1442static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001443
Takashi Iwai498f5b12011-05-02 11:33:15 +02001444static const struct hda_input_mux ad1983_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001445 .num_items = 4,
1446 .items = {
1447 { "Mic", 0x0 },
1448 { "Line", 0x1 },
1449 { "Mix", 0x2 },
1450 { "Mix Mono", 0x3 },
1451 },
1452};
1453
1454/*
1455 * SPDIF playback route
1456 */
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001457static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001458{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001459 static const char * const texts[] = { "PCM", "ADC" };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001460
1461 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1462 uinfo->count = 1;
1463 uinfo->value.enumerated.items = 2;
1464 if (uinfo->value.enumerated.item > 1)
1465 uinfo->value.enumerated.item = 1;
1466 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1467 return 0;
1468}
1469
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001470static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001471{
1472 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1473 struct ad198x_spec *spec = codec->spec;
1474
1475 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1476 return 0;
1477}
1478
Takashi Iwaic8b6bf9b2005-11-17 14:57:47 +01001479static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001480{
1481 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1482 struct ad198x_spec *spec = codec->spec;
1483
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001484 if (ucontrol->value.enumerated.item[0] > 1)
1485 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001486 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1487 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001488 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1489 AC_VERB_SET_CONNECT_SEL,
1490 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001491 return 1;
1492 }
1493 return 0;
1494}
1495
Takashi Iwai498f5b12011-05-02 11:33:15 +02001496static const struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001497 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1498 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1499 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1508 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001509 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001510 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1511 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1512 {
1513 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1514 .name = "Capture Source",
1515 .info = ad198x_mux_enum_info,
1516 .get = ad198x_mux_enum_get,
1517 .put = ad198x_mux_enum_put,
1518 },
1519 {
1520 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001521 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001522 .info = ad1983_spdif_route_info,
1523 .get = ad1983_spdif_route_get,
1524 .put = ad1983_spdif_route_put,
1525 },
1526 { } /* end */
1527};
1528
Takashi Iwai498f5b12011-05-02 11:33:15 +02001529static const struct hda_verb ad1983_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001530 /* Front, HP, Mono; mute as default */
1531 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1532 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1533 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1534 /* Beep, PCM, Mic, Line-In: mute */
1535 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1536 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1537 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1538 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1539 /* Front, HP selectors; from Mix */
1540 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1541 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1542 /* Mono selector; from Mix */
1543 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1544 /* Mic selector; Mic */
1545 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1546 /* Line-in selector: Line-in */
1547 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1548 /* Mic boost: 0dB */
1549 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1550 /* Record selector: mic */
1551 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1552 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1553 /* SPDIF route: PCM */
1554 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1555 /* Front Pin */
1556 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1557 /* HP Pin */
1558 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1559 /* Mono Pin */
1560 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1561 /* Mic Pin */
1562 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1563 /* Line Pin */
1564 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1565 { } /* end */
1566};
1567
Takashi Iwaicb53c622007-08-10 17:21:45 +02001568#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001569static const struct hda_amp_list ad1983_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001570 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1571 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1572 { } /* end */
1573};
1574#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001575
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001576static int patch_ad1983(struct hda_codec *codec)
1577{
1578 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001579 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001580
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001581 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001582 if (spec == NULL)
1583 return -ENOMEM;
1584
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001585 codec->spec = spec;
1586
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001587 err = snd_hda_attach_beep_device(codec, 0x10);
1588 if (err < 0) {
1589 ad198x_free(codec);
1590 return err;
1591 }
1592 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1593
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001594 spec->multiout.max_channels = 2;
1595 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1596 spec->multiout.dac_nids = ad1983_dac_nids;
1597 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001598 spec->num_adc_nids = 1;
1599 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001600 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001601 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001602 spec->num_mixers = 1;
1603 spec->mixers[0] = ad1983_mixers;
1604 spec->num_init_verbs = 1;
1605 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001606 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001607#ifdef CONFIG_SND_HDA_POWER_SAVE
1608 spec->loopback.amplist = ad1983_loopbacks;
1609#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001610 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001611
1612 codec->patch_ops = ad198x_patch_ops;
1613
Takashi Iwai729d55b2009-12-25 22:49:01 +01001614 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001615 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001616
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001617 return 0;
1618}
1619
1620
1621/*
1622 * AD1981 HD specific
1623 */
1624
1625#define AD1981_SPDIF_OUT 0x02
1626#define AD1981_DAC 0x03
1627#define AD1981_ADC 0x04
1628
Takashi Iwai498f5b12011-05-02 11:33:15 +02001629static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
1630static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
1631static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001632
1633/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001634static const struct hda_input_mux ad1981_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001635 .num_items = 7,
1636 .items = {
1637 { "Front Mic", 0x0 },
1638 { "Line", 0x1 },
1639 { "Mix", 0x2 },
1640 { "Mix Mono", 0x3 },
1641 { "CD", 0x4 },
1642 { "Mic", 0x6 },
1643 { "Aux", 0x7 },
1644 },
1645};
1646
Takashi Iwai498f5b12011-05-02 11:33:15 +02001647static const struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001648 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1649 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1650 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1652 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1653 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1654 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1655 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1656 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1657 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1658 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1659 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1660 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1661 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1662 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1663 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1664 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1665 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001666 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1667 HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001668 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1669 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1670 {
1671 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1672 .name = "Capture Source",
1673 .info = ad198x_mux_enum_info,
1674 .get = ad198x_mux_enum_get,
1675 .put = ad198x_mux_enum_put,
1676 },
1677 /* identical with AD1983 */
1678 {
1679 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001680 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001681 .info = ad1983_spdif_route_info,
1682 .get = ad1983_spdif_route_get,
1683 .put = ad1983_spdif_route_put,
1684 },
1685 { } /* end */
1686};
1687
Takashi Iwai498f5b12011-05-02 11:33:15 +02001688static const struct hda_verb ad1981_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001689 /* Front, HP, Mono; mute as default */
1690 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1691 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1692 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1693 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1694 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1695 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1696 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1697 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1698 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1699 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1700 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1701 /* Front, HP selectors; from Mix */
1702 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1703 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1704 /* Mono selector; from Mix */
1705 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1706 /* Mic Mixer; select Front Mic */
1707 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1708 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1709 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001710 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1711 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001712 /* Record selector: Front mic */
1713 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1714 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1715 /* SPDIF route: PCM */
1716 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1717 /* Front Pin */
1718 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1719 /* HP Pin */
1720 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1721 /* Mono Pin */
1722 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1723 /* Front & Rear Mic Pins */
1724 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1725 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1726 /* Line Pin */
1727 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1728 /* Digital Beep */
1729 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1730 /* Line-Out as Input: disabled */
1731 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1732 { } /* end */
1733};
1734
Takashi Iwaicb53c622007-08-10 17:21:45 +02001735#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001736static const struct hda_amp_list ad1981_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001737 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1738 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1739 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1740 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1741 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1742 { } /* end */
1743};
1744#endif
1745
Takashi Iwai18a815d2006-03-01 19:54:39 +01001746/*
1747 * Patch for HP nx6320
1748 *
Tobin Davis18768992007-03-12 22:20:51 +01001749 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001750 * speaker output enabled _and_ mute-LED off.
1751 */
1752
1753#define AD1981_HP_EVENT 0x37
1754#define AD1981_MIC_EVENT 0x38
1755
Takashi Iwai498f5b12011-05-02 11:33:15 +02001756static const struct hda_verb ad1981_hp_init_verbs[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001757 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1758 /* pin sensing on HP and Mic jacks */
1759 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1760 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1761 {}
1762};
1763
1764/* turn on/off EAPD (+ mute HP) as a master switch */
1765static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1766 struct snd_ctl_elem_value *ucontrol)
1767{
1768 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1769 struct ad198x_spec *spec = codec->spec;
1770
1771 if (! ad198x_eapd_put(kcontrol, ucontrol))
1772 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001773 /* change speaker pin appropriately */
1774 snd_hda_codec_write(codec, 0x05, 0,
1775 AC_VERB_SET_PIN_WIDGET_CONTROL,
1776 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001777 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001778 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1779 HDA_AMP_MUTE,
1780 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001781 return 1;
1782}
1783
1784/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001785static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001786 .ops = &snd_hda_bind_vol,
1787 .values = {
1788 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1789 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1790 0
1791 },
1792};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001793
1794/* mute internal speaker if HP is plugged */
1795static void ad1981_hp_automute(struct hda_codec *codec)
1796{
1797 unsigned int present;
1798
Takashi Iwaid56757a2009-11-18 08:00:14 +01001799 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001800 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1801 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001802}
1803
1804/* toggle input of built-in and mic jack appropriately */
1805static void ad1981_hp_automic(struct hda_codec *codec)
1806{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001807 static const struct hda_verb mic_jack_on[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001808 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1809 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1810 {}
1811 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02001812 static const struct hda_verb mic_jack_off[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001813 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1814 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1815 {}
1816 };
1817 unsigned int present;
1818
Takashi Iwaid56757a2009-11-18 08:00:14 +01001819 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001820 if (present)
1821 snd_hda_sequence_write(codec, mic_jack_on);
1822 else
1823 snd_hda_sequence_write(codec, mic_jack_off);
1824}
1825
1826/* unsolicited event for HP jack sensing */
1827static void ad1981_hp_unsol_event(struct hda_codec *codec,
1828 unsigned int res)
1829{
1830 res >>= 26;
1831 switch (res) {
1832 case AD1981_HP_EVENT:
1833 ad1981_hp_automute(codec);
1834 break;
1835 case AD1981_MIC_EVENT:
1836 ad1981_hp_automic(codec);
1837 break;
1838 }
1839}
1840
Takashi Iwai498f5b12011-05-02 11:33:15 +02001841static const struct hda_input_mux ad1981_hp_capture_source = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001842 .num_items = 3,
1843 .items = {
1844 { "Mic", 0x0 },
1845 { "Docking-Station", 0x1 },
1846 { "Mix", 0x2 },
1847 },
1848};
1849
Takashi Iwai498f5b12011-05-02 11:33:15 +02001850static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001851 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001852 {
1853 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001854 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001855 .name = "Master Playback Switch",
1856 .info = ad198x_eapd_info,
1857 .get = ad198x_eapd_get,
1858 .put = ad1981_hp_master_sw_put,
1859 .private_value = 0x05,
1860 },
1861 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1862 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1863#if 0
1864 /* FIXME: analog mic/line loopback doesn't work with my tests...
1865 * (although recording is OK)
1866 */
1867 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1868 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1869 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1870 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1871 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1872 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1873 /* FIXME: does this laptop have analog CD connection? */
1874 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1875 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1876#endif
David Henningsson5f99f862011-01-04 15:24:24 +01001877 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1878 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001879 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1880 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1881 {
1882 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1883 .name = "Capture Source",
1884 .info = ad198x_mux_enum_info,
1885 .get = ad198x_mux_enum_get,
1886 .put = ad198x_mux_enum_put,
1887 },
1888 { } /* end */
1889};
1890
1891/* initialize jack-sensing, too */
1892static int ad1981_hp_init(struct hda_codec *codec)
1893{
1894 ad198x_init(codec);
1895 ad1981_hp_automute(codec);
1896 ad1981_hp_automic(codec);
1897 return 0;
1898}
1899
Tobin Davis18768992007-03-12 22:20:51 +01001900/* configuration for Toshiba Laptops */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001901static const struct hda_verb ad1981_toshiba_init_verbs[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001902 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1903 /* pin sensing on HP and Mic jacks */
1904 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1905 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1906 {}
1907};
1908
Takashi Iwai498f5b12011-05-02 11:33:15 +02001909static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001910 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1911 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1912 { }
1913};
1914
Takashi Iwai01686c52006-04-18 12:54:11 +02001915/* configuration for Lenovo Thinkpad T60 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001916static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
Takashi Iwai01686c52006-04-18 12:54:11 +02001917 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1918 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1919 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1920 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1921 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1922 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1923 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1924 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001925 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
Takashi Iwai01686c52006-04-18 12:54:11 +02001926 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1927 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1928 {
1929 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1930 .name = "Capture Source",
1931 .info = ad198x_mux_enum_info,
1932 .get = ad198x_mux_enum_get,
1933 .put = ad198x_mux_enum_put,
1934 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001935 /* identical with AD1983 */
1936 {
1937 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1938 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1939 .info = ad1983_spdif_route_info,
1940 .get = ad1983_spdif_route_get,
1941 .put = ad1983_spdif_route_put,
1942 },
Takashi Iwai01686c52006-04-18 12:54:11 +02001943 { } /* end */
1944};
1945
Takashi Iwai498f5b12011-05-02 11:33:15 +02001946static const struct hda_input_mux ad1981_thinkpad_capture_source = {
Takashi Iwai01686c52006-04-18 12:54:11 +02001947 .num_items = 3,
1948 .items = {
1949 { "Mic", 0x0 },
1950 { "Mix", 0x2 },
1951 { "CD", 0x4 },
1952 },
1953};
1954
Takashi Iwai18a815d2006-03-01 19:54:39 +01001955/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001956enum {
1957 AD1981_BASIC,
1958 AD1981_HP,
1959 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001960 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001961 AD1981_MODELS
1962};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001963
Takashi Iwaiea734962011-01-17 11:29:34 +01001964static const char * const ad1981_models[AD1981_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001965 [AD1981_HP] = "hp",
1966 [AD1981_THINKPAD] = "thinkpad",
1967 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001968 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001969};
1970
Takashi Iwai498f5b12011-05-02 11:33:15 +02001971static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001972 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001973 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001974 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001975 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001976 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c52006-04-18 12:54:11 +02001977 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001978 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001979 /* HP nx6320 (reversed SSID, H/W bug) */
1980 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001981 {}
1982};
1983
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001984static int patch_ad1981(struct hda_codec *codec)
1985{
1986 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001987 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001988
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001989 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001990 if (spec == NULL)
1991 return -ENOMEM;
1992
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001993 codec->spec = spec;
1994
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001995 err = snd_hda_attach_beep_device(codec, 0x10);
1996 if (err < 0) {
1997 ad198x_free(codec);
1998 return err;
1999 }
2000 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
2001
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002002 spec->multiout.max_channels = 2;
2003 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
2004 spec->multiout.dac_nids = ad1981_dac_nids;
2005 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01002006 spec->num_adc_nids = 1;
2007 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002008 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002009 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01002010 spec->num_mixers = 1;
2011 spec->mixers[0] = ad1981_mixers;
2012 spec->num_init_verbs = 1;
2013 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002014 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002015#ifdef CONFIG_SND_HDA_POWER_SAVE
2016 spec->loopback.amplist = ad1981_loopbacks;
2017#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002018 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002019
2020 codec->patch_ops = ad198x_patch_ops;
2021
Takashi Iwai18a815d2006-03-01 19:54:39 +01002022 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002023 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
2024 ad1981_models,
2025 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01002026 switch (board_config) {
2027 case AD1981_HP:
2028 spec->mixers[0] = ad1981_hp_mixers;
2029 spec->num_init_verbs = 2;
2030 spec->init_verbs[1] = ad1981_hp_init_verbs;
Takashi Iwai695cd4a2011-06-10 14:37:04 +02002031 if (!is_jack_available(codec, 0x0a))
2032 spec->multiout.dig_out_nid = 0;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002033 spec->input_mux = &ad1981_hp_capture_source;
2034
2035 codec->patch_ops.init = ad1981_hp_init;
2036 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05002037 /* set the upper-limit for mixer amp to 0dB for avoiding the
2038 * possible damage by overloading
2039 */
2040 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2041 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2042 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2043 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2044 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01002045 break;
Takashi Iwai01686c52006-04-18 12:54:11 +02002046 case AD1981_THINKPAD:
2047 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c52006-04-18 12:54:11 +02002048 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04002049 /* set the upper-limit for mixer amp to 0dB for avoiding the
2050 * possible damage by overloading
2051 */
2052 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2053 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2054 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2055 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2056 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c52006-04-18 12:54:11 +02002057 break;
Tobin Davis18768992007-03-12 22:20:51 +01002058 case AD1981_TOSHIBA:
2059 spec->mixers[0] = ad1981_hp_mixers;
2060 spec->mixers[1] = ad1981_toshiba_mixers;
2061 spec->num_init_verbs = 2;
2062 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
2063 spec->multiout.dig_out_nid = 0;
2064 spec->input_mux = &ad1981_hp_capture_source;
2065 codec->patch_ops.init = ad1981_hp_init;
2066 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
2067 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002068 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01002069
2070 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002071 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002072
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002073 return 0;
2074}
2075
2076
2077/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002078 * AD1988
2079 *
2080 * Output pins and routes
2081 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002082 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002083 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
2084 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
2085 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
2086 * port-D 0x12 (mute/hp) <- 0x29 <- 04
2087 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
2088 * port-F 0x16 (mute) <- 0x2a <- 06
2089 * port-G 0x24 (mute) <- 0x27 <- 05
2090 * port-H 0x25 (mute) <- 0x28 <- 0a
2091 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
2092 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002093 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
2094 * (*) 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 +01002095 *
2096 * Input pins and routes
2097 *
2098 * pin boost mix input # / adc input #
2099 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
2100 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
2101 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
2102 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
2103 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
2104 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
2105 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
2106 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
2107 *
2108 *
2109 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01002110 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002111 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002112 *
2113 * Inputs of Analog Mix (0x20)
2114 * 0:Port-B (front mic)
2115 * 1:Port-C/G/H (line-in)
2116 * 2:Port-A
2117 * 3:Port-D (line-in/2)
2118 * 4:Port-E/G/H (mic-in)
2119 * 5:Port-F (mic2-in)
2120 * 6:CD
2121 * 7:Beep
2122 *
2123 * ADC selection
2124 * 0:Port-A
2125 * 1:Port-B (front mic-in)
2126 * 2:Port-C (line-in)
2127 * 3:Port-F (mic2-in)
2128 * 4:Port-E (mic-in)
2129 * 5:CD
2130 * 6:Port-G
2131 * 7:Port-H
2132 * 8:Port-D (line-in/2)
2133 * 9:Mix
2134 *
2135 * Proposed pin assignments by the datasheet
2136 *
2137 * 6-stack
2138 * Port-A front headphone
2139 * B front mic-in
2140 * C rear line-in
2141 * D rear front-out
2142 * E rear mic-in
2143 * F rear surround
2144 * G rear CLFE
2145 * H rear side
2146 *
2147 * 3-stack
2148 * Port-A front headphone
2149 * B front mic
2150 * C rear line-in/surround
2151 * D rear front-out
2152 * E rear mic-in/CLFE
2153 *
2154 * laptop
2155 * Port-A headphone
2156 * B mic-in
2157 * C docking station
2158 * D internal speaker (with EAPD)
2159 * E/F quad mic array
2160 */
2161
2162
2163/* models */
2164enum {
2165 AD1988_6STACK,
2166 AD1988_6STACK_DIG,
2167 AD1988_3STACK,
2168 AD1988_3STACK_DIG,
2169 AD1988_LAPTOP,
2170 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002171 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002172 AD1988_MODEL_LAST,
2173};
2174
Takashi Iwaid32410b12005-11-24 16:06:23 +01002175/* reivision id to check workarounds */
2176#define AD1988A_REV2 0x100200
2177
Takashi Iwai1a806f42006-07-03 15:58:16 +02002178#define is_rev2(codec) \
2179 ((codec)->vendor_id == 0x11d41988 && \
2180 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002181
2182/*
2183 * mixers
2184 */
2185
Takashi Iwai498f5b12011-05-02 11:33:15 +02002186static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002187 0x04, 0x06, 0x05, 0x0a
2188};
2189
Takashi Iwai498f5b12011-05-02 11:33:15 +02002190static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002191 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002192};
2193
2194/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002195static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002196 0x04, 0x05, 0x0a, 0x06
2197};
2198
Takashi Iwai498f5b12011-05-02 11:33:15 +02002199static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002200 0x03
2201};
2202
Takashi Iwai498f5b12011-05-02 11:33:15 +02002203static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002204 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002205};
2206
Takashi Iwai498f5b12011-05-02 11:33:15 +02002207static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002208 0x08, 0x09, 0x0f
2209};
2210
Takashi Iwai498f5b12011-05-02 11:33:15 +02002211static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002212 0x0c, 0x0d, 0x0e
2213};
2214
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002215#define AD1988_SPDIF_OUT 0x02
2216#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002217#define AD1988_SPDIF_IN 0x07
2218
Takashi Iwai498f5b12011-05-02 11:33:15 +02002219static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01002220 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002221};
2222
Takashi Iwai498f5b12011-05-02 11:33:15 +02002223static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002224 .num_items = 5,
2225 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002226 { "Front Mic", 0x1 }, /* port-B */
2227 { "Line", 0x2 }, /* port-C */
2228 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002229 { "CD", 0x5 },
2230 { "Mix", 0x9 },
2231 },
2232};
2233
Takashi Iwai498f5b12011-05-02 11:33:15 +02002234static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002235 .num_items = 3,
2236 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002237 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002238 { "CD", 0x5 },
2239 { "Mix", 0x9 },
2240 },
2241};
2242
2243/*
2244 */
2245static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2246 struct snd_ctl_elem_info *uinfo)
2247{
2248 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2249 struct ad198x_spec *spec = codec->spec;
2250 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2251 spec->num_channel_mode);
2252}
2253
2254static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2255 struct snd_ctl_elem_value *ucontrol)
2256{
2257 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2258 struct ad198x_spec *spec = codec->spec;
2259 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2260 spec->num_channel_mode, spec->multiout.max_channels);
2261}
2262
2263static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2264 struct snd_ctl_elem_value *ucontrol)
2265{
2266 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2267 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002268 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2269 spec->num_channel_mode,
2270 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002271 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002272 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002273 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002274}
2275
Raymond Yau34588702011-09-23 19:03:25 +08002276static const struct snd_kcontrol_new ad1988_hp_mixers[] = {
2277 {
2278 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2279 .name = "Independent HP",
2280 .info = ad1988_independent_hp_info,
2281 .get = ad1988_independent_hp_get,
2282 .put = ad1988_independent_hp_put,
2283 },
2284 { } /* end */
2285};
2286
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002287/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002288static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002289 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2290 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2291 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2292 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2293 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002294 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002295};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002296
Takashi Iwai498f5b12011-05-02 11:33:15 +02002297static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002298 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2299 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2300 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2301 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2302 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002303 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002304};
2305
Takashi Iwai498f5b12011-05-02 11:33:15 +02002306static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002307 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002308 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2309 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2310 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2311 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2312 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2313 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2314 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2315
2316 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2317 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2318 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2319 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2320 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2321 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2322 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2323 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2324
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002325 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002326 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2327
David Henningsson5f99f862011-01-04 15:24:24 +01002328 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2329 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002330 { } /* end */
2331};
2332
2333/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002334static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002335 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002336 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002337 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2338 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002339 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002340};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002341
Takashi Iwai498f5b12011-05-02 11:33:15 +02002342static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002343 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002344 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2345 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2346 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002347 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002348};
2349
Takashi Iwai498f5b12011-05-02 11:33:15 +02002350static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002351 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002352 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002353 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2354 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2355 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002356 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2357 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2358
2359 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2360 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2361 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2362 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2363 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2364 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2365 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2366 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2367
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002368 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002369 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2370
David Henningsson5f99f862011-01-04 15:24:24 +01002371 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2372 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002373 {
2374 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2375 .name = "Channel Mode",
2376 .info = ad198x_ch_mode_info,
2377 .get = ad198x_ch_mode_get,
2378 .put = ad198x_ch_mode_put,
2379 },
2380
2381 { } /* end */
2382};
2383
2384/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002385static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002386 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002387 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2388 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2389 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2390
2391 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2392 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2393 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2394 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2395 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2396 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2397
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002398 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002399 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2400
David Henningsson5f99f862011-01-04 15:24:24 +01002401 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002402
2403 {
2404 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2405 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002406 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002407 .info = ad198x_eapd_info,
2408 .get = ad198x_eapd_get,
2409 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002410 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002411 },
2412
2413 { } /* end */
2414};
2415
2416/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002417static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002418 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2419 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2420 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2421 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2422 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2423 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2424 {
2425 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2426 /* The multiple "Capture Source" controls confuse alsamixer
2427 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002428 */
2429 /* .name = "Capture Source", */
2430 .name = "Input Source",
2431 .count = 3,
2432 .info = ad198x_mux_enum_info,
2433 .get = ad198x_mux_enum_get,
2434 .put = ad198x_mux_enum_put,
2435 },
2436 { } /* end */
2437};
2438
2439static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2440 struct snd_ctl_elem_info *uinfo)
2441{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002442 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002443 "PCM", "ADC1", "ADC2", "ADC3"
2444 };
2445 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2446 uinfo->count = 1;
2447 uinfo->value.enumerated.items = 4;
2448 if (uinfo->value.enumerated.item >= 4)
2449 uinfo->value.enumerated.item = 3;
2450 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2451 return 0;
2452}
2453
2454static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2455 struct snd_ctl_elem_value *ucontrol)
2456{
2457 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2458 unsigned int sel;
2459
Takashi Iwaibddcf542007-07-24 18:04:05 +02002460 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2461 AC_AMP_GET_INPUT);
2462 if (!(sel & 0x80))
2463 ucontrol->value.enumerated.item[0] = 0;
2464 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002465 sel = snd_hda_codec_read(codec, 0x0b, 0,
2466 AC_VERB_GET_CONNECT_SEL, 0);
2467 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002468 sel++;
2469 else
2470 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002471 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002472 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002473 return 0;
2474}
2475
2476static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2477 struct snd_ctl_elem_value *ucontrol)
2478{
2479 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002480 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002481 int change;
2482
Takashi Iwai35b26722007-05-05 12:17:17 +02002483 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002484 if (val > 3)
2485 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002486 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002487 sel = snd_hda_codec_read(codec, 0x1d, 0,
2488 AC_VERB_GET_AMP_GAIN_MUTE,
2489 AC_AMP_GET_INPUT);
2490 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002491 if (change) {
2492 snd_hda_codec_write_cache(codec, 0x1d, 0,
2493 AC_VERB_SET_AMP_GAIN_MUTE,
2494 AMP_IN_UNMUTE(0));
2495 snd_hda_codec_write_cache(codec, 0x1d, 0,
2496 AC_VERB_SET_AMP_GAIN_MUTE,
2497 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002498 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002499 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002500 sel = snd_hda_codec_read(codec, 0x1d, 0,
2501 AC_VERB_GET_AMP_GAIN_MUTE,
2502 AC_AMP_GET_INPUT | 0x01);
2503 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002504 if (change) {
2505 snd_hda_codec_write_cache(codec, 0x1d, 0,
2506 AC_VERB_SET_AMP_GAIN_MUTE,
2507 AMP_IN_MUTE(0));
2508 snd_hda_codec_write_cache(codec, 0x1d, 0,
2509 AC_VERB_SET_AMP_GAIN_MUTE,
2510 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002511 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002512 sel = snd_hda_codec_read(codec, 0x0b, 0,
2513 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2514 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002515 if (change)
2516 snd_hda_codec_write_cache(codec, 0x0b, 0,
2517 AC_VERB_SET_CONNECT_SEL,
2518 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002519 }
2520 return change;
2521}
2522
Takashi Iwai498f5b12011-05-02 11:33:15 +02002523static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002524 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2525 {
2526 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2527 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002528 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002529 .info = ad1988_spdif_playback_source_info,
2530 .get = ad1988_spdif_playback_source_get,
2531 .put = ad1988_spdif_playback_source_put,
2532 },
2533 { } /* end */
2534};
2535
Takashi Iwai498f5b12011-05-02 11:33:15 +02002536static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002537 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2538 { } /* end */
2539};
2540
Takashi Iwai498f5b12011-05-02 11:33:15 +02002541static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002542 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002543 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002544 { } /* end */
2545};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002546
2547/*
2548 * initialization verbs
2549 */
2550
2551/*
2552 * for 6-stack (+dig)
2553 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002554static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002555 /* Front, Surround, CLFE, side DAC; unmute as default */
2556 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2557 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2558 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2559 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002560 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002561 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002562 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2563 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2564 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2565 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2566 /* Port-D line-out path */
2567 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2568 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2569 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2570 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2571 /* Port-F surround path */
2572 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2573 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2574 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2575 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2576 /* Port-G CLFE path */
2577 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2578 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2579 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2580 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2581 /* Port-H side path */
2582 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2583 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2584 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2585 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2586 /* Mono out path */
2587 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2588 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2589 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2590 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2591 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2592 /* Port-B front mic-in path */
2593 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2594 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2595 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2596 /* Port-C line-in path */
2597 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2598 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2599 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2600 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2601 /* Port-E mic-in path */
2602 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2603 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2604 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2605 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002606 /* Analog CD Input */
2607 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002608 /* Analog Mix output amp */
2609 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002610
2611 { }
2612};
2613
Takashi Iwai498f5b12011-05-02 11:33:15 +02002614static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002615 /* Headphone; unmute as default */
2616 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2617 /* Port-A front headphon path */
2618 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2619 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2620 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2621 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2622 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002623
2624 { }
2625};
2626
Takashi Iwai498f5b12011-05-02 11:33:15 +02002627static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002628 /* mute analog mix */
2629 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2630 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2631 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2632 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2633 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2634 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2635 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2636 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2637 /* select ADCs - front-mic */
2638 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2639 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2640 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002641
2642 { }
2643};
2644
Takashi Iwai498f5b12011-05-02 11:33:15 +02002645static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002646 /* SPDIF out sel */
2647 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2648 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2649 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002650 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002651 /* SPDIF out pin */
2652 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002653
2654 { }
2655};
2656
Takashi Iwai498f5b12011-05-02 11:33:15 +02002657static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002658 /* unmute SPDIF input pin */
2659 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2660 { }
2661};
2662
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002663/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002664static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002665 /* SPDIF-1 out pin */
2666 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002667 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002668 /* SPDIF-2/HDMI out pin */
2669 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2670 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002671 { }
2672};
2673
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002674/*
2675 * verbs for 3stack (+dig)
2676 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002677static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002678 /* set port-C to line-in */
2679 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2680 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2681 /* set port-E to mic-in */
2682 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2683 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2684 { } /* end */
2685};
2686
Takashi Iwai498f5b12011-05-02 11:33:15 +02002687static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002688 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002689 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002690 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002691 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002692 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002693 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002694 { } /* end */
2695};
2696
Takashi Iwai498f5b12011-05-02 11:33:15 +02002697static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002698 { 2, ad1988_3stack_ch2_init },
2699 { 6, ad1988_3stack_ch6_init },
2700};
2701
Takashi Iwai498f5b12011-05-02 11:33:15 +02002702static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002703 /* Front, Surround, CLFE, side DAC; unmute as default */
2704 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2705 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2706 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2707 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002708 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002709 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002710 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2711 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2712 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2713 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2714 /* Port-D line-out path */
2715 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2716 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2717 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2718 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2719 /* Mono out path */
2720 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2721 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2722 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2723 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2724 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2725 /* Port-B front mic-in path */
2726 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2727 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2728 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002729 /* Port-C line-in/surround path - 6ch mode as default */
2730 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2731 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002732 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002733 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002734 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002735 /* Port-E mic-in/CLFE path - 6ch mode as default */
2736 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2737 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002738 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002739 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002740 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2741 /* mute analog mix */
2742 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2743 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2744 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2745 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2746 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2747 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2748 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2749 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2750 /* select ADCs - front-mic */
2751 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2752 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2753 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002754 /* Analog Mix output amp */
2755 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002756 { }
2757};
2758
2759/*
2760 * verbs for laptop mode (+dig)
2761 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002762static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002763 /* unmute port-A and mute port-D */
2764 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2765 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2766 { } /* end */
2767};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002768static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002769 /* mute port-A and unmute port-D */
2770 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2771 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2772 { } /* end */
2773};
2774
2775#define AD1988_HP_EVENT 0x01
2776
Takashi Iwai498f5b12011-05-02 11:33:15 +02002777static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002778 /* Front, Surround, CLFE, side DAC; unmute as default */
2779 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2780 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2781 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2782 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002783 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002784 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002785 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2786 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2787 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2788 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2789 /* unsolicited event for pin-sense */
2790 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2791 /* Port-D line-out path + EAPD */
2792 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2793 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2794 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2795 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2796 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2797 /* Mono out path */
2798 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2799 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2800 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2801 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2802 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2803 /* Port-B mic-in path */
2804 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2805 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2806 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2807 /* Port-C docking station - try to output */
2808 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2809 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2810 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2811 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2812 /* mute analog mix */
2813 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2814 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2815 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2816 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2817 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2818 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2819 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2820 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2821 /* select ADCs - mic */
2822 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2823 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2824 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002825 /* Analog Mix output amp */
2826 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002827 { }
2828};
2829
2830static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2831{
2832 if ((res >> 26) != AD1988_HP_EVENT)
2833 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002834 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002835 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2836 else
2837 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2838}
2839
Takashi Iwaicb53c622007-08-10 17:21:45 +02002840#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02002841static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002842 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2843 { 0x20, HDA_INPUT, 1 }, /* Line */
2844 { 0x20, HDA_INPUT, 4 }, /* Mic */
2845 { 0x20, HDA_INPUT, 6 }, /* CD */
2846 { } /* end */
2847};
2848#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002849
2850/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002851 * Automatic parse of I/O pins from the BIOS configuration
2852 */
2853
Takashi Iwaid32410b12005-11-24 16:06:23 +01002854enum {
2855 AD_CTL_WIDGET_VOL,
2856 AD_CTL_WIDGET_MUTE,
2857 AD_CTL_BIND_MUTE,
2858};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002859static const struct snd_kcontrol_new ad1988_control_templates[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002860 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2861 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2862 HDA_BIND_MUTE(NULL, 0, 0, 0),
2863};
2864
2865/* add dynamic controls */
2866static int add_control(struct ad198x_spec *spec, int type, const char *name,
2867 unsigned long val)
2868{
2869 struct snd_kcontrol_new *knew;
2870
Takashi Iwai603c4012008-07-30 15:01:44 +02002871 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2872 knew = snd_array_new(&spec->kctls);
2873 if (!knew)
2874 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002875 *knew = ad1988_control_templates[type];
2876 knew->name = kstrdup(name, GFP_KERNEL);
2877 if (! knew->name)
2878 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002879 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002880 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002881 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002882 return 0;
2883}
2884
2885#define AD1988_PIN_CD_NID 0x18
2886#define AD1988_PIN_BEEP_NID 0x10
2887
Takashi Iwai498f5b12011-05-02 11:33:15 +02002888static const hda_nid_t ad1988_mixer_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002889 /* A B C D E F G H */
2890 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2891};
2892
2893static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2894{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002895 static const hda_nid_t idx_to_dac[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002896 /* A B C D E F G H */
Raymond Yau356aab72011-08-31 10:30:59 +08002897 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002898 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02002899 static const hda_nid_t idx_to_dac_rev2[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002900 /* A B C D E F G H */
Raymond Yau356aab72011-08-31 10:30:59 +08002901 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002902 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002903 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002904 return idx_to_dac_rev2[idx];
2905 else
2906 return idx_to_dac[idx];
2907}
2908
Takashi Iwai498f5b12011-05-02 11:33:15 +02002909static const hda_nid_t ad1988_boost_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002910 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2911};
2912
2913static int ad1988_pin_idx(hda_nid_t nid)
2914{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002915 static const hda_nid_t ad1988_io_pins[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002916 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2917 };
2918 int i;
2919 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2920 if (ad1988_io_pins[i] == nid)
2921 return i;
2922 return 0; /* should be -1 */
2923}
2924
2925static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2926{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002927 static const int loopback_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002928 2, 0, 1, 3, 4, 5, 1, 4
2929 };
2930 switch (nid) {
2931 case AD1988_PIN_CD_NID:
2932 return 6;
2933 default:
2934 return loopback_idx[ad1988_pin_idx(nid)];
2935 }
2936}
2937
2938static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2939{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002940 static const int adc_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002941 0, 1, 2, 8, 4, 3, 6, 7
2942 };
2943 switch (nid) {
2944 case AD1988_PIN_CD_NID:
2945 return 5;
2946 default:
2947 return adc_idx[ad1988_pin_idx(nid)];
2948 }
2949}
2950
2951/* fill in the dac_nids table from the parsed pin configuration */
2952static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2953 const struct auto_pin_cfg *cfg)
2954{
2955 struct ad198x_spec *spec = codec->spec;
2956 int i, idx;
2957
2958 spec->multiout.dac_nids = spec->private_dac_nids;
2959
2960 /* check the pins hardwired to audio widget */
2961 for (i = 0; i < cfg->line_outs; i++) {
2962 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
Takashi Iwaidda14412011-05-02 11:29:30 +02002963 spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002964 }
2965 spec->multiout.num_dacs = cfg->line_outs;
2966 return 0;
2967}
2968
2969/* add playback controls from the parsed DAC table */
2970static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2971 const struct auto_pin_cfg *cfg)
2972{
2973 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01002974 static const char * const chname[4] = {
2975 "Front", "Surround", NULL /*CLFE*/, "Side"
2976 };
Takashi Iwaid32410b12005-11-24 16:06:23 +01002977 hda_nid_t nid;
2978 int i, err;
2979
2980 for (i = 0; i < cfg->line_outs; i++) {
2981 hda_nid_t dac = spec->multiout.dac_nids[i];
2982 if (! dac)
2983 continue;
2984 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2985 if (i == 2) {
2986 /* Center/LFE */
2987 err = add_control(spec, AD_CTL_WIDGET_VOL,
2988 "Center Playback Volume",
2989 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2990 if (err < 0)
2991 return err;
2992 err = add_control(spec, AD_CTL_WIDGET_VOL,
2993 "LFE Playback Volume",
2994 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2995 if (err < 0)
2996 return err;
2997 err = add_control(spec, AD_CTL_BIND_MUTE,
2998 "Center Playback Switch",
2999 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
3000 if (err < 0)
3001 return err;
3002 err = add_control(spec, AD_CTL_BIND_MUTE,
3003 "LFE Playback Switch",
3004 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
3005 if (err < 0)
3006 return err;
3007 } else {
3008 sprintf(name, "%s Playback Volume", chname[i]);
3009 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3010 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
3011 if (err < 0)
3012 return err;
3013 sprintf(name, "%s Playback Switch", chname[i]);
3014 err = add_control(spec, AD_CTL_BIND_MUTE, name,
3015 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
3016 if (err < 0)
3017 return err;
3018 }
3019 }
3020 return 0;
3021}
3022
3023/* add playback controls for speaker and HP outputs */
3024static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
3025 const char *pfx)
3026{
3027 struct ad198x_spec *spec = codec->spec;
3028 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02003029 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003030 char name[32];
3031
3032 if (! pin)
3033 return 0;
3034
3035 idx = ad1988_pin_idx(pin);
3036 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02003037 /* check whether the corresponding DAC was already taken */
3038 for (i = 0; i < spec->autocfg.line_outs; i++) {
3039 hda_nid_t pin = spec->autocfg.line_out_pins[i];
3040 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
3041 if (dac == nid)
3042 break;
3043 }
3044 if (i >= spec->autocfg.line_outs) {
3045 /* specify the DAC as the extra output */
3046 if (!spec->multiout.hp_nid)
3047 spec->multiout.hp_nid = nid;
3048 else
3049 spec->multiout.extra_out_nid[0] = nid;
3050 /* control HP volume/switch on the output mixer amp */
3051 sprintf(name, "%s Playback Volume", pfx);
3052 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3053 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
3054 if (err < 0)
3055 return err;
3056 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01003057 nid = ad1988_mixer_nids[idx];
3058 sprintf(name, "%s Playback Switch", pfx);
3059 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
3060 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
3061 return err;
3062 return 0;
3063}
3064
3065/* create input playback/capture controls for the given pin */
3066static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
Takashi Iwai9e042e72010-08-30 13:04:44 +02003067 const char *ctlname, int ctlidx, int boost)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003068{
3069 char name[32];
3070 int err, idx;
3071
3072 sprintf(name, "%s Playback Volume", ctlname);
3073 idx = ad1988_pin_to_loopback_idx(pin);
3074 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3075 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
3076 return err;
3077 sprintf(name, "%s Playback Switch", ctlname);
3078 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
3079 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
3080 return err;
3081 if (boost) {
3082 hda_nid_t bnid;
3083 idx = ad1988_pin_idx(pin);
3084 bnid = ad1988_boost_nids[idx];
3085 if (bnid) {
David Henningsson5f99f862011-01-04 15:24:24 +01003086 sprintf(name, "%s Boost Volume", ctlname);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003087 return add_control(spec, AD_CTL_WIDGET_VOL, name,
3088 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
3089
3090 }
3091 }
3092 return 0;
3093}
3094
3095/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02003096static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
Takashi Iwaid32410b12005-11-24 16:06:23 +01003097 const struct auto_pin_cfg *cfg)
3098{
Takashi Iwai10a20af2010-09-09 16:28:02 +02003099 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003100 struct hda_input_mux *imux = &spec->private_imux;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003101 int i, err, type, type_idx;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003102
Takashi Iwai9e042e72010-08-30 13:04:44 +02003103 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02003104 const char *label;
Takashi Iwai9e042e72010-08-30 13:04:44 +02003105 type = cfg->inputs[i].type;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003106 label = hda_get_autocfg_input_label(codec, cfg, i);
3107 snd_hda_add_imux_item(imux, label,
3108 ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
3109 &type_idx);
Takashi Iwai9e042e72010-08-30 13:04:44 +02003110 err = new_analog_input(spec, cfg->inputs[i].pin,
Takashi Iwai10a20af2010-09-09 16:28:02 +02003111 label, type_idx,
Takashi Iwai86e29592010-09-09 14:50:17 +02003112 type == AUTO_PIN_MIC);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003113 if (err < 0)
3114 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003115 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02003116 snd_hda_add_imux_item(imux, "Mix", 9, NULL);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003117
3118 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
3119 "Analog Mix Playback Volume",
3120 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3121 return err;
3122 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
3123 "Analog Mix Playback Switch",
3124 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3125 return err;
3126
3127 return 0;
3128}
3129
3130static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
3131 hda_nid_t nid, int pin_type,
3132 int dac_idx)
3133{
3134 /* set as output */
3135 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
3136 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
3137 switch (nid) {
Raymond Yau356aab72011-08-31 10:30:59 +08003138 case 0x11: /* port-A - DAC 03 */
3139 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003140 break;
3141 case 0x14: /* port-B - DAC 06 */
3142 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
3143 break;
3144 case 0x15: /* port-C - DAC 05 */
3145 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
3146 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003147 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01003148 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3149 break;
3150 case 0x13: /* mono - DAC 04 */
3151 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3152 break;
3153 }
3154}
3155
3156static void ad1988_auto_init_multi_out(struct hda_codec *codec)
3157{
3158 struct ad198x_spec *spec = codec->spec;
3159 int i;
3160
3161 for (i = 0; i < spec->autocfg.line_outs; i++) {
3162 hda_nid_t nid = spec->autocfg.line_out_pins[i];
3163 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
3164 }
3165}
3166
3167static void ad1988_auto_init_extra_out(struct hda_codec *codec)
3168{
3169 struct ad198x_spec *spec = codec->spec;
3170 hda_nid_t pin;
3171
Takashi Iwai82bc9552006-03-21 11:24:42 +01003172 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003173 if (pin) /* connect to front */
3174 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003175 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003176 if (pin) /* connect to front */
3177 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
3178}
3179
3180static void ad1988_auto_init_analog_input(struct hda_codec *codec)
3181{
3182 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9e042e72010-08-30 13:04:44 +02003183 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003184 int i, idx;
3185
Takashi Iwai9e042e72010-08-30 13:04:44 +02003186 for (i = 0; i < cfg->num_inputs; i++) {
3187 hda_nid_t nid = cfg->inputs[i].pin;
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003188 int type = cfg->inputs[i].type;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003189 switch (nid) {
3190 case 0x15: /* port-C */
3191 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3192 break;
3193 case 0x17: /* port-E */
3194 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3195 break;
3196 }
3197 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003198 type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003199 if (nid != AD1988_PIN_CD_NID)
3200 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3201 AMP_OUT_MUTE);
3202 idx = ad1988_pin_idx(nid);
3203 if (ad1988_boost_nids[idx])
3204 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3205 AC_VERB_SET_AMP_GAIN_MUTE,
3206 AMP_OUT_ZERO);
3207 }
3208}
3209
3210/* parse the BIOS configuration and set up the alc_spec */
3211/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3212static int ad1988_parse_auto_config(struct hda_codec *codec)
3213{
3214 struct ad198x_spec *spec = codec->spec;
3215 int err;
3216
Kailang Yangdf694da2005-12-05 19:42:22 +01003217 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003218 return err;
3219 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3220 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003221 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003222 return 0; /* can't find valid BIOS pin config */
3223 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003224 (err = ad1988_auto_create_extra_out(codec,
3225 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003226 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003227 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003228 "Headphone")) < 0 ||
Takashi Iwai10a20af2010-09-09 16:28:02 +02003229 (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003230 return err;
3231
3232 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3233
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003234 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003235 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3236 if (spec->autocfg.dig_in_pin)
3237 spec->dig_in_nid = AD1988_SPDIF_IN;
3238
Takashi Iwai603c4012008-07-30 15:01:44 +02003239 if (spec->kctls.list)
3240 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003241
3242 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3243
3244 spec->input_mux = &spec->private_imux;
3245
3246 return 1;
3247}
3248
3249/* init callback for auto-configuration model -- overriding the default init */
3250static int ad1988_auto_init(struct hda_codec *codec)
3251{
3252 ad198x_init(codec);
3253 ad1988_auto_init_multi_out(codec);
3254 ad1988_auto_init_extra_out(codec);
3255 ad1988_auto_init_analog_input(codec);
3256 return 0;
3257}
3258
Takashi Iwaid32410b12005-11-24 16:06:23 +01003259/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003260 */
3261
Takashi Iwaiea734962011-01-17 11:29:34 +01003262static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003263 [AD1988_6STACK] = "6stack",
3264 [AD1988_6STACK_DIG] = "6stack-dig",
3265 [AD1988_3STACK] = "3stack",
3266 [AD1988_3STACK_DIG] = "3stack-dig",
3267 [AD1988_LAPTOP] = "laptop",
3268 [AD1988_LAPTOP_DIG] = "laptop-dig",
3269 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003270};
3271
Takashi Iwai498f5b12011-05-02 11:33:15 +02003272static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003273 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003274 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003275 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01003276 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003277 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003278 {}
3279};
3280
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003281static int patch_ad1988(struct hda_codec *codec)
3282{
3283 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003284 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003285
3286 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3287 if (spec == NULL)
3288 return -ENOMEM;
3289
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003290 codec->spec = spec;
3291
Takashi Iwai1a806f42006-07-03 15:58:16 +02003292 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003293 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3294
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003295 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003296 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003297 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003298 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3299 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003300 board_config = AD1988_AUTO;
3301 }
3302
3303 if (board_config == AD1988_AUTO) {
3304 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003305 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003306 if (err < 0) {
3307 ad198x_free(codec);
3308 return err;
3309 } else if (! err) {
3310 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3311 board_config = AD1988_6STACK;
3312 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003313 }
3314
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003315 err = snd_hda_attach_beep_device(codec, 0x10);
3316 if (err < 0) {
3317 ad198x_free(codec);
3318 return err;
3319 }
3320 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3321
Raymond Yau356aab72011-08-31 10:30:59 +08003322 if (!spec->multiout.hp_nid)
Raymond Yau34588702011-09-23 19:03:25 +08003323 spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003324 switch (board_config) {
3325 case AD1988_6STACK:
3326 case AD1988_6STACK_DIG:
3327 spec->multiout.max_channels = 8;
3328 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003329 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003330 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3331 else
3332 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003333 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003334 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003335 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003336 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3337 else
3338 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08003339 spec->mixers[1] = ad1988_6stack_mixers2;
3340 spec->num_init_verbs = 1;
3341 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yau34588702011-09-23 19:03:25 +08003342 if (board_config == AD1988_6STACK_DIG) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003343 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3344 spec->dig_in_nid = AD1988_SPDIF_IN;
3345 }
3346 break;
3347 case AD1988_3STACK:
3348 case AD1988_3STACK_DIG:
3349 spec->multiout.max_channels = 6;
3350 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003351 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003352 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3353 else
3354 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003355 spec->input_mux = &ad1988_6stack_capture_source;
3356 spec->channel_mode = ad1988_3stack_modes;
3357 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003358 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003359 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003360 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3361 else
3362 spec->mixers[0] = ad1988_3stack_mixers1;
3363 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003364 spec->num_init_verbs = 1;
3365 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3366 if (board_config == AD1988_3STACK_DIG)
3367 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3368 break;
3369 case AD1988_LAPTOP:
3370 case AD1988_LAPTOP_DIG:
3371 spec->multiout.max_channels = 2;
3372 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003373 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003374 spec->input_mux = &ad1988_laptop_capture_source;
3375 spec->num_mixers = 1;
3376 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003377 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003378 spec->num_init_verbs = 1;
3379 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3380 if (board_config == AD1988_LAPTOP_DIG)
3381 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3382 break;
3383 }
3384
Raymond Yau34588702011-09-23 19:03:25 +08003385 if (spec->autocfg.hp_pins[0]) {
3386 spec->mixers[spec->num_mixers++] = ad1988_hp_mixers;
3387 spec->slave_vols = ad1988_6stack_fp_slave_vols;
3388 spec->slave_sws = ad1988_6stack_fp_slave_sws;
3389 spec->alt_dac_nid = ad1988_alt_dac_nid;
3390 spec->stream_analog_alt_playback =
3391 &ad198x_pcm_analog_alt_playback;
3392 }
3393
Takashi Iwaid32410b12005-11-24 16:06:23 +01003394 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3395 spec->adc_nids = ad1988_adc_nids;
3396 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003397 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3398 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3399 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003400 if (codec->vendor_id >= 0x11d4989a) {
3401 spec->mixers[spec->num_mixers++] =
3402 ad1989_spdif_out_mixers;
3403 spec->init_verbs[spec->num_init_verbs++] =
3404 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003405 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003406 } else {
3407 spec->mixers[spec->num_mixers++] =
3408 ad1988_spdif_out_mixers;
3409 spec->init_verbs[spec->num_init_verbs++] =
3410 ad1988_spdif_init_verbs;
3411 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003412 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003413 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003414 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003415 spec->init_verbs[spec->num_init_verbs++] =
3416 ad1988_spdif_in_init_verbs;
3417 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003418
3419 codec->patch_ops = ad198x_patch_ops;
3420 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003421 case AD1988_AUTO:
3422 codec->patch_ops.init = ad1988_auto_init;
3423 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003424 case AD1988_LAPTOP:
3425 case AD1988_LAPTOP_DIG:
3426 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3427 break;
3428 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003429#ifdef CONFIG_SND_HDA_POWER_SAVE
3430 spec->loopback.amplist = ad1988_loopbacks;
3431#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003432 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003433
Takashi Iwai729d55b2009-12-25 22:49:01 +01003434 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003435 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003436
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003437 return 0;
3438}
3439
3440
3441/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003442 * AD1884 / AD1984
3443 *
3444 * port-B - front line/mic-in
3445 * port-E - aux in/out
3446 * port-F - aux in/out
3447 * port-C - rear line/mic-in
3448 * port-D - rear line/hp-out
3449 * port-A - front line/hp-out
3450 *
3451 * AD1984 = AD1884 + two digital mic-ins
3452 *
3453 * FIXME:
3454 * For simplicity, we share the single DAC for both HP and line-outs
3455 * right now. The inidividual playbacks could be easily implemented,
3456 * but no build-up framework is given, so far.
3457 */
3458
Takashi Iwai498f5b12011-05-02 11:33:15 +02003459static const hda_nid_t ad1884_dac_nids[1] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003460 0x04,
3461};
3462
Takashi Iwai498f5b12011-05-02 11:33:15 +02003463static const hda_nid_t ad1884_adc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003464 0x08, 0x09,
3465};
3466
Takashi Iwai498f5b12011-05-02 11:33:15 +02003467static const hda_nid_t ad1884_capsrc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003468 0x0c, 0x0d,
3469};
3470
3471#define AD1884_SPDIF_OUT 0x02
3472
Takashi Iwai498f5b12011-05-02 11:33:15 +02003473static const struct hda_input_mux ad1884_capture_source = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003474 .num_items = 4,
3475 .items = {
3476 { "Front Mic", 0x0 },
3477 { "Mic", 0x1 },
3478 { "CD", 0x2 },
3479 { "Mix", 0x3 },
3480 },
3481};
3482
Takashi Iwai498f5b12011-05-02 11:33:15 +02003483static const struct snd_kcontrol_new ad1884_base_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003484 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3485 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3486 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3487 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3488 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3489 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3490 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3491 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3492 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3493 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3494 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3495 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003496 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3497 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003498 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3499 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3500 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3501 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3502 {
3503 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3504 /* The multiple "Capture Source" controls confuse alsamixer
3505 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003506 */
3507 /* .name = "Capture Source", */
3508 .name = "Input Source",
3509 .count = 2,
3510 .info = ad198x_mux_enum_info,
3511 .get = ad198x_mux_enum_get,
3512 .put = ad198x_mux_enum_put,
3513 },
3514 /* SPDIF controls */
3515 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3516 {
3517 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3518 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3519 /* identical with ad1983 */
3520 .info = ad1983_spdif_route_info,
3521 .get = ad1983_spdif_route_get,
3522 .put = ad1983_spdif_route_put,
3523 },
3524 { } /* end */
3525};
3526
Takashi Iwai498f5b12011-05-02 11:33:15 +02003527static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003528 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3529 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3530 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003531 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003532 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003533 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003534 { } /* end */
3535};
3536
3537/*
3538 * initialization verbs
3539 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003540static const struct hda_verb ad1884_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003541 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003542 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3543 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003544 /* Port-A (HP) mixer */
3545 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3546 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3547 /* Port-A pin */
3548 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3549 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3550 /* HP selector - select DAC2 */
3551 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3552 /* Port-D (Line-out) mixer */
3553 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3554 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3555 /* Port-D pin */
3556 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3557 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3558 /* Mono-out mixer */
3559 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3560 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3561 /* Mono-out pin */
3562 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3563 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3564 /* Mono selector */
3565 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3566 /* Port-B (front mic) pin */
3567 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003568 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003569 /* Port-C (rear mic) pin */
3570 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003571 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003572 /* Analog mixer; mute as default */
3573 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3574 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3575 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3576 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3577 /* Analog Mix output amp */
3578 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3579 /* SPDIF output selector */
3580 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3581 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3582 { } /* end */
3583};
3584
Takashi Iwaicb53c622007-08-10 17:21:45 +02003585#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02003586static const struct hda_amp_list ad1884_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003587 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3588 { 0x20, HDA_INPUT, 1 }, /* Mic */
3589 { 0x20, HDA_INPUT, 2 }, /* CD */
3590 { 0x20, HDA_INPUT, 4 }, /* Docking */
3591 { } /* end */
3592};
3593#endif
3594
Takashi Iwaiea734962011-01-17 11:29:34 +01003595static const char * const ad1884_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +01003596 "PCM Playback Volume",
3597 "Mic Playback Volume",
3598 "Mono Playback Volume",
3599 "Front Mic Playback Volume",
3600 "Mic Playback Volume",
3601 "CD Playback Volume",
3602 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003603 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003604 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003605 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003606 NULL
3607};
3608
Takashi Iwai2bac6472007-05-18 18:21:41 +02003609static int patch_ad1884(struct hda_codec *codec)
3610{
3611 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003612 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003613
3614 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3615 if (spec == NULL)
3616 return -ENOMEM;
3617
Takashi Iwai2bac6472007-05-18 18:21:41 +02003618 codec->spec = spec;
3619
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003620 err = snd_hda_attach_beep_device(codec, 0x10);
3621 if (err < 0) {
3622 ad198x_free(codec);
3623 return err;
3624 }
3625 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3626
Takashi Iwai2bac6472007-05-18 18:21:41 +02003627 spec->multiout.max_channels = 2;
3628 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3629 spec->multiout.dac_nids = ad1884_dac_nids;
3630 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3631 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3632 spec->adc_nids = ad1884_adc_nids;
3633 spec->capsrc_nids = ad1884_capsrc_nids;
3634 spec->input_mux = &ad1884_capture_source;
3635 spec->num_mixers = 1;
3636 spec->mixers[0] = ad1884_base_mixers;
3637 spec->num_init_verbs = 1;
3638 spec->init_verbs[0] = ad1884_init_verbs;
3639 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003640#ifdef CONFIG_SND_HDA_POWER_SAVE
3641 spec->loopback.amplist = ad1884_loopbacks;
3642#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003643 spec->vmaster_nid = 0x04;
3644 /* we need to cover all playback volumes */
3645 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003646
3647 codec->patch_ops = ad198x_patch_ops;
3648
Takashi Iwai729d55b2009-12-25 22:49:01 +01003649 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003650 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003651
Takashi Iwai2bac6472007-05-18 18:21:41 +02003652 return 0;
3653}
3654
3655/*
3656 * Lenovo Thinkpad T61/X61
3657 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003658static const struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003659 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003660 .items = {
3661 { "Mic", 0x0 },
3662 { "Internal Mic", 0x1 },
3663 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003664 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003665 },
3666};
3667
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003668
3669/*
3670 * Dell Precision T3400
3671 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003672static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003673 .num_items = 3,
3674 .items = {
3675 { "Front Mic", 0x0 },
3676 { "Line-In", 0x1 },
3677 { "Mix", 0x3 },
3678 },
3679};
3680
3681
Takashi Iwai498f5b12011-05-02 11:33:15 +02003682static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003683 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3684 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3685 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3686 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3687 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3688 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3689 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3690 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003691 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3692 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003693 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3694 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003695 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3696 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3697 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003698 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3699 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3700 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3701 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3702 {
3703 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3704 /* The multiple "Capture Source" controls confuse alsamixer
3705 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003706 */
3707 /* .name = "Capture Source", */
3708 .name = "Input Source",
3709 .count = 2,
3710 .info = ad198x_mux_enum_info,
3711 .get = ad198x_mux_enum_get,
3712 .put = ad198x_mux_enum_put,
3713 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003714 /* SPDIF controls */
3715 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3716 {
3717 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3718 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3719 /* identical with ad1983 */
3720 .info = ad1983_spdif_route_info,
3721 .get = ad1983_spdif_route_get,
3722 .put = ad1983_spdif_route_put,
3723 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003724 { } /* end */
3725};
3726
3727/* additional verbs */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003728static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003729 /* Port-E (docking station mic) pin */
3730 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3731 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3732 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003733 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003734 /* Analog PC Beeper - allow firmware/ACPI beeps */
3735 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003736 /* Analog mixer - docking mic; mute as default */
3737 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003738 /* enable EAPD bit */
3739 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003740 { } /* end */
3741};
3742
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003743/*
3744 * Dell Precision T3400
3745 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003746static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003747 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3748 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3749 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3750 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3751 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3752 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3753 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3754 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3755 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003756 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
3757 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003758 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3759 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3760 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3761 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3762 {
3763 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3764 /* The multiple "Capture Source" controls confuse alsamixer
3765 * So call somewhat different..
3766 */
3767 /* .name = "Capture Source", */
3768 .name = "Input Source",
3769 .count = 2,
3770 .info = ad198x_mux_enum_info,
3771 .get = ad198x_mux_enum_get,
3772 .put = ad198x_mux_enum_put,
3773 },
3774 { } /* end */
3775};
3776
Takashi Iwai2bac6472007-05-18 18:21:41 +02003777/* Digial MIC ADC NID 0x05 + 0x06 */
3778static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3779 struct hda_codec *codec,
3780 unsigned int stream_tag,
3781 unsigned int format,
3782 struct snd_pcm_substream *substream)
3783{
3784 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3785 stream_tag, 0, format);
3786 return 0;
3787}
3788
3789static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3790 struct hda_codec *codec,
3791 struct snd_pcm_substream *substream)
3792{
Takashi Iwai888afa12008-03-18 09:57:50 +01003793 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003794 return 0;
3795}
3796
Takashi Iwai498f5b12011-05-02 11:33:15 +02003797static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003798 .substreams = 2,
3799 .channels_min = 2,
3800 .channels_max = 2,
3801 .nid = 0x05,
3802 .ops = {
3803 .prepare = ad1984_pcm_dmic_prepare,
3804 .cleanup = ad1984_pcm_dmic_cleanup
3805 },
3806};
3807
3808static int ad1984_build_pcms(struct hda_codec *codec)
3809{
3810 struct ad198x_spec *spec = codec->spec;
3811 struct hda_pcm *info;
3812 int err;
3813
3814 err = ad198x_build_pcms(codec);
3815 if (err < 0)
3816 return err;
3817
3818 info = spec->pcm_rec + codec->num_pcms;
3819 codec->num_pcms++;
3820 info->name = "AD1984 Digital Mic";
3821 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3822 return 0;
3823}
3824
3825/* models */
3826enum {
3827 AD1984_BASIC,
3828 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003829 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003830 AD1984_MODELS
3831};
3832
Takashi Iwaiea734962011-01-17 11:29:34 +01003833static const char * const ad1984_models[AD1984_MODELS] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003834 [AD1984_BASIC] = "basic",
3835 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003836 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003837};
3838
Takashi Iwai498f5b12011-05-02 11:33:15 +02003839static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003840 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003841 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003842 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003843 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003844 {}
3845};
3846
3847static int patch_ad1984(struct hda_codec *codec)
3848{
3849 struct ad198x_spec *spec;
3850 int board_config, err;
3851
3852 err = patch_ad1884(codec);
3853 if (err < 0)
3854 return err;
3855 spec = codec->spec;
3856 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3857 ad1984_models, ad1984_cfg_tbl);
3858 switch (board_config) {
3859 case AD1984_BASIC:
3860 /* additional digital mics */
3861 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3862 codec->patch_ops.build_pcms = ad1984_build_pcms;
3863 break;
3864 case AD1984_THINKPAD:
Jerone Young68c18692010-08-03 01:46:44 -05003865 if (codec->subsystem_id == 0x17aa20fb) {
3866 /* Thinpad X300 does not have the ability to do SPDIF,
3867 or attach to docking station to use SPDIF */
3868 spec->multiout.dig_out_nid = 0;
3869 } else
3870 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003871 spec->input_mux = &ad1984_thinkpad_capture_source;
3872 spec->mixers[0] = ad1984_thinkpad_mixers;
3873 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003874 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003875 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003876 case AD1984_DELL_DESKTOP:
3877 spec->multiout.dig_out_nid = 0;
3878 spec->input_mux = &ad1984_dell_desktop_capture_source;
3879 spec->mixers[0] = ad1984_dell_desktop_mixers;
3880 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003881 }
3882 return 0;
3883}
3884
3885
3886/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003887 * AD1883 / AD1884A / AD1984A / AD1984B
3888 *
3889 * port-B (0x14) - front mic-in
3890 * port-E (0x1c) - rear mic-in
3891 * port-F (0x16) - CD / ext out
3892 * port-C (0x15) - rear line-in
3893 * port-D (0x12) - rear line-out
3894 * port-A (0x11) - front hp-out
3895 *
3896 * AD1984A = AD1884A + digital-mic
3897 * AD1883 = equivalent with AD1984A
3898 * AD1984B = AD1984A + extra SPDIF-out
3899 *
3900 * FIXME:
3901 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3902 */
3903
Takashi Iwai498f5b12011-05-02 11:33:15 +02003904static const hda_nid_t ad1884a_dac_nids[1] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003905 0x03,
3906};
3907
3908#define ad1884a_adc_nids ad1884_adc_nids
3909#define ad1884a_capsrc_nids ad1884_capsrc_nids
3910
3911#define AD1884A_SPDIF_OUT 0x02
3912
Takashi Iwai498f5b12011-05-02 11:33:15 +02003913static const struct hda_input_mux ad1884a_capture_source = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003914 .num_items = 5,
3915 .items = {
3916 { "Front Mic", 0x0 },
3917 { "Mic", 0x4 },
3918 { "Line", 0x1 },
3919 { "CD", 0x2 },
3920 { "Mix", 0x3 },
3921 },
3922};
3923
Takashi Iwai498f5b12011-05-02 11:33:15 +02003924static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003925 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3926 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3927 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3928 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3929 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3930 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3931 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3932 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3933 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3934 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3935 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3936 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3937 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3938 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3939 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3940 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003941 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3942 HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
3943 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003944 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3945 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3946 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3947 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3948 {
3949 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3950 /* The multiple "Capture Source" controls confuse alsamixer
3951 * So call somewhat different..
3952 */
3953 /* .name = "Capture Source", */
3954 .name = "Input Source",
3955 .count = 2,
3956 .info = ad198x_mux_enum_info,
3957 .get = ad198x_mux_enum_get,
3958 .put = ad198x_mux_enum_put,
3959 },
3960 /* SPDIF controls */
3961 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3962 {
3963 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3964 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3965 /* identical with ad1983 */
3966 .info = ad1983_spdif_route_info,
3967 .get = ad1983_spdif_route_get,
3968 .put = ad1983_spdif_route_put,
3969 },
3970 { } /* end */
3971};
3972
3973/*
3974 * initialization verbs
3975 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003976static const struct hda_verb ad1884a_init_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003977 /* DACs; unmute as default */
3978 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3979 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3980 /* Port-A (HP) mixer - route only from analog mixer */
3981 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3982 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3983 /* Port-A pin */
3984 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3985 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3986 /* Port-D (Line-out) mixer - route only from analog mixer */
3987 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3988 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3989 /* Port-D pin */
3990 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3991 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3992 /* Mono-out mixer - route only from analog mixer */
3993 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3994 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3995 /* Mono-out pin */
3996 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3997 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3998 /* Port-B (front mic) pin */
3999 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004000 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004001 /* Port-C (rear line-in) pin */
4002 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004003 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004004 /* Port-E (rear mic) pin */
4005 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4006 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4007 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
4008 /* Port-F (CD) pin */
4009 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4010 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4011 /* Analog mixer; mute as default */
4012 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4013 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4014 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4015 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4016 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
4017 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4018 /* Analog Mix output amp */
4019 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4020 /* capture sources */
4021 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
4022 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4023 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4024 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4025 /* SPDIF output amp */
4026 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4027 { } /* end */
4028};
4029
4030#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02004031static const struct hda_amp_list ad1884a_loopbacks[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004032 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4033 { 0x20, HDA_INPUT, 1 }, /* Mic */
4034 { 0x20, HDA_INPUT, 2 }, /* CD */
4035 { 0x20, HDA_INPUT, 4 }, /* Docking */
4036 { } /* end */
4037};
4038#endif
4039
4040/*
4041 * Laptop model
4042 *
4043 * Port A: Headphone jack
4044 * Port B: MIC jack
4045 * Port C: Internal MIC
4046 * Port D: Dock Line Out (if enabled)
4047 * Port E: Dock Line In (if enabled)
4048 * Port F: Internal speakers
4049 */
4050
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004051static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
4052 struct snd_ctl_elem_value *ucontrol)
4053{
4054 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4055 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
4056 int mute = (!ucontrol->value.integer.value[0] &&
4057 !ucontrol->value.integer.value[1]);
4058 /* toggle GPIO1 according to the mute state */
4059 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
4060 mute ? 0x02 : 0x0);
4061 return ret;
4062}
Takashi Iwaic5059252008-02-16 09:43:56 +01004063
Takashi Iwai498f5b12011-05-02 11:33:15 +02004064static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004065 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004066 {
4067 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4068 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004069 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004070 .info = snd_hda_mixer_amp_switch_info,
4071 .get = snd_hda_mixer_amp_switch_get,
4072 .put = ad1884a_mobile_master_sw_put,
4073 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4074 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004075 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4076 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4077 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4078 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4079 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4080 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4081 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4082 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4083 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004084 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4085 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4086 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004087 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4088 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004089 { } /* end */
4090};
4091
Takashi Iwai498f5b12011-05-02 11:33:15 +02004092static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004093 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db172009-07-02 16:10:23 +02004094 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4095 {
4096 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4097 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004098 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db172009-07-02 16:10:23 +02004099 .info = snd_hda_mixer_amp_switch_info,
4100 .get = snd_hda_mixer_amp_switch_get,
4101 .put = ad1884a_mobile_master_sw_put,
4102 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4103 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004104 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4105 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02004106 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
4107 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004108 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4109 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004110 { } /* end */
4111};
4112
Takashi Iwaic5059252008-02-16 09:43:56 +01004113/* mute internal speaker if HP is plugged */
4114static void ad1884a_hp_automute(struct hda_codec *codec)
4115{
4116 unsigned int present;
4117
Takashi Iwaid56757a2009-11-18 08:00:14 +01004118 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01004119 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4120 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4121 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4122 present ? 0x00 : 0x02);
4123}
4124
Takashi Iwai269ef192008-05-30 15:32:15 +02004125/* switch to external mic if plugged */
4126static void ad1884a_hp_automic(struct hda_codec *codec)
4127{
4128 unsigned int present;
4129
Takashi Iwaid56757a2009-11-18 08:00:14 +01004130 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02004131 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
4132 present ? 0 : 1);
4133}
4134
Takashi Iwaic5059252008-02-16 09:43:56 +01004135#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02004136#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01004137
4138/* unsolicited event for HP jack sensing */
4139static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
4140{
Takashi Iwai269ef192008-05-30 15:32:15 +02004141 switch (res >> 26) {
4142 case AD1884A_HP_EVENT:
4143 ad1884a_hp_automute(codec);
4144 break;
4145 case AD1884A_MIC_EVENT:
4146 ad1884a_hp_automic(codec);
4147 break;
4148 }
Takashi Iwaic5059252008-02-16 09:43:56 +01004149}
4150
4151/* initialize jack-sensing, too */
4152static int ad1884a_hp_init(struct hda_codec *codec)
4153{
4154 ad198x_init(codec);
4155 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02004156 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01004157 return 0;
4158}
4159
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004160/* mute internal speaker if HP or docking HP is plugged */
4161static void ad1884a_laptop_automute(struct hda_codec *codec)
4162{
4163 unsigned int present;
4164
Takashi Iwaid56757a2009-11-18 08:00:14 +01004165 present = snd_hda_jack_detect(codec, 0x11);
4166 if (!present)
4167 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004168 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4169 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4170 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4171 present ? 0x00 : 0x02);
4172}
4173
4174/* switch to external mic if plugged */
4175static void ad1884a_laptop_automic(struct hda_codec *codec)
4176{
4177 unsigned int idx;
4178
Takashi Iwaid56757a2009-11-18 08:00:14 +01004179 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004180 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01004181 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004182 idx = 4;
4183 else
4184 idx = 1;
4185 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
4186}
4187
4188/* unsolicited event for HP jack sensing */
4189static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
4190 unsigned int res)
4191{
4192 switch (res >> 26) {
4193 case AD1884A_HP_EVENT:
4194 ad1884a_laptop_automute(codec);
4195 break;
4196 case AD1884A_MIC_EVENT:
4197 ad1884a_laptop_automic(codec);
4198 break;
4199 }
4200}
4201
4202/* initialize jack-sensing, too */
4203static int ad1884a_laptop_init(struct hda_codec *codec)
4204{
4205 ad198x_init(codec);
4206 ad1884a_laptop_automute(codec);
4207 ad1884a_laptop_automic(codec);
4208 return 0;
4209}
4210
Takashi Iwaic5059252008-02-16 09:43:56 +01004211/* additional verbs for laptop model */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004212static const struct hda_verb ad1884a_laptop_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004213 /* Port-A (HP) pin - always unmuted */
4214 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4215 /* Port-F (int speaker) mixer - route only from analog mixer */
4216 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4217 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004218 /* Port-F (int speaker) pin */
4219 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004220 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004221 /* required for compaq 6530s/6531s speaker output */
4222 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004223 /* Port-C pin - internal mic-in */
4224 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4225 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4226 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004227 /* Port-D (docking line-out) pin - default unmuted */
4228 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004229 /* analog mix */
4230 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4231 /* unsolicited event for pin-sense */
4232 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004233 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004234 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004235 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004236 /* allow to touch GPIO1 (for mute control) */
4237 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4238 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4239 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004240 { } /* end */
4241};
4242
Takashi Iwai498f5b12011-05-02 11:33:15 +02004243static const struct hda_verb ad1884a_mobile_verbs[] = {
Takashi Iwai73156132009-04-23 08:24:48 +02004244 /* DACs; unmute as default */
4245 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4246 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4247 /* Port-A (HP) mixer - route only from analog mixer */
4248 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4249 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4250 /* Port-A pin */
4251 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4252 /* Port-A (HP) pin - always unmuted */
4253 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4254 /* Port-B (mic jack) pin */
4255 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4256 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4257 /* Port-C (int mic) pin */
4258 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4259 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4260 /* Port-F (int speaker) mixer - route only from analog mixer */
4261 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4262 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4263 /* Port-F pin */
4264 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4265 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4266 /* Analog mixer; mute as default */
4267 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4268 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4269 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4270 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4271 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4272 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4273 /* Analog Mix output amp */
4274 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4275 /* capture sources */
4276 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4277 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4278 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4279 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4280 /* unsolicited event for pin-sense */
4281 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4282 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db172009-07-02 16:10:23 +02004283 /* allow to touch GPIO1 (for mute control) */
4284 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4285 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4286 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004287 { } /* end */
4288};
4289
Takashi Iwaic5059252008-02-16 09:43:56 +01004290/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004291 * Thinkpad X300
4292 * 0x11 - HP
4293 * 0x12 - speaker
4294 * 0x14 - mic-in
4295 * 0x17 - built-in mic
4296 */
4297
Takashi Iwai498f5b12011-05-02 11:33:15 +02004298static const struct hda_verb ad1984a_thinkpad_verbs[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004299 /* HP unmute */
4300 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4301 /* analog mix */
4302 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4303 /* turn on EAPD */
4304 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4305 /* unsolicited event for pin-sense */
4306 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4307 /* internal mic - dmic */
4308 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004309 /* set magic COEFs for dmic */
4310 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4311 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004312 { } /* end */
4313};
4314
Takashi Iwai498f5b12011-05-02 11:33:15 +02004315static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004316 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4317 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4318 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4319 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4320 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4321 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004322 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4323 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004324 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4325 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4326 {
4327 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4328 .name = "Capture Source",
4329 .info = ad198x_mux_enum_info,
4330 .get = ad198x_mux_enum_get,
4331 .put = ad198x_mux_enum_put,
4332 },
4333 { } /* end */
4334};
4335
Takashi Iwai498f5b12011-05-02 11:33:15 +02004336static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004337 .num_items = 3,
4338 .items = {
4339 { "Mic", 0x0 },
4340 { "Internal Mic", 0x5 },
4341 { "Mix", 0x3 },
4342 },
4343};
4344
4345/* mute internal speaker if HP is plugged */
4346static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4347{
4348 unsigned int present;
4349
Takashi Iwaid56757a2009-11-18 08:00:14 +01004350 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004351 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4352 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4353}
4354
4355/* unsolicited event for HP jack sensing */
4356static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4357 unsigned int res)
4358{
4359 if ((res >> 26) != AD1884A_HP_EVENT)
4360 return;
4361 ad1984a_thinkpad_automute(codec);
4362}
4363
4364/* initialize jack-sensing, too */
4365static int ad1984a_thinkpad_init(struct hda_codec *codec)
4366{
4367 ad198x_init(codec);
4368 ad1984a_thinkpad_automute(codec);
4369 return 0;
4370}
4371
4372/*
David Henningsson677cd902011-02-07 15:19:34 +01004373 * Precision R5500
4374 * 0x12 - HP/line-out
4375 * 0x13 - speaker (mono)
4376 * 0x15 - mic-in
4377 */
4378
Takashi Iwai498f5b12011-05-02 11:33:15 +02004379static const struct hda_verb ad1984a_precision_verbs[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004380 /* Unmute main output path */
4381 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4382 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
4383 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
4384 /* Analog mixer; mute as default */
4385 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4386 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4387 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4388 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4389 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4390 /* Select mic as input */
4391 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
4392 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
4393 /* Configure as mic */
4394 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4395 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4396 /* HP unmute */
4397 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4398 /* turn on EAPD */
4399 {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4400 /* unsolicited event for pin-sense */
4401 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4402 { } /* end */
4403};
4404
Takashi Iwai498f5b12011-05-02 11:33:15 +02004405static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004406 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4407 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4408 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4409 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4410 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4411 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4412 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4413 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4414 HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
4415 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4416 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4417 { } /* end */
4418};
4419
4420
4421/* mute internal speaker if HP is plugged */
4422static void ad1984a_precision_automute(struct hda_codec *codec)
4423{
4424 unsigned int present;
4425
4426 present = snd_hda_jack_detect(codec, 0x12);
4427 snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
4428 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4429}
4430
4431
4432/* unsolicited event for HP jack sensing */
4433static void ad1984a_precision_unsol_event(struct hda_codec *codec,
4434 unsigned int res)
4435{
4436 if ((res >> 26) != AD1884A_HP_EVENT)
4437 return;
4438 ad1984a_precision_automute(codec);
4439}
4440
4441/* initialize jack-sensing, too */
4442static int ad1984a_precision_init(struct hda_codec *codec)
4443{
4444 ad198x_init(codec);
4445 ad1984a_precision_automute(codec);
4446 return 0;
4447}
4448
4449
4450/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004451 * HP Touchsmart
4452 * port-A (0x11) - front hp-out
4453 * port-B (0x14) - unused
4454 * port-C (0x15) - unused
4455 * port-D (0x12) - rear line out
4456 * port-E (0x1c) - front mic-in
4457 * port-F (0x16) - Internal speakers
4458 * digital-mic (0x17) - Internal mic
4459 */
4460
Takashi Iwai498f5b12011-05-02 11:33:15 +02004461static const struct hda_verb ad1984a_touchsmart_verbs[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004462 /* DACs; unmute as default */
4463 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4464 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4465 /* Port-A (HP) mixer - route only from analog mixer */
4466 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4467 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4468 /* Port-A pin */
4469 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4470 /* Port-A (HP) pin - always unmuted */
4471 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4472 /* Port-E (int speaker) mixer - route only from analog mixer */
4473 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4474 /* Port-E pin */
4475 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4476 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4477 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4478 /* Port-F (int speaker) mixer - route only from analog mixer */
4479 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4480 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4481 /* Port-F pin */
4482 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4483 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4484 /* Analog mixer; mute as default */
4485 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4486 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4487 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4488 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4489 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4490 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4491 /* Analog Mix output amp */
4492 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4493 /* capture sources */
4494 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4495 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4496 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4497 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4498 /* unsolicited event for pin-sense */
4499 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4500 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4501 /* allow to touch GPIO1 (for mute control) */
4502 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4503 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4504 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4505 /* internal mic - dmic */
4506 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4507 /* set magic COEFs for dmic */
4508 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4509 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4510 { } /* end */
4511};
4512
Takashi Iwai498f5b12011-05-02 11:33:15 +02004513static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004514 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4515/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4516 {
4517 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004518 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004519 .name = "Master Playback Switch",
4520 .info = snd_hda_mixer_amp_switch_info,
4521 .get = snd_hda_mixer_amp_switch_get,
4522 .put = ad1884a_mobile_master_sw_put,
4523 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4524 },
4525 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4526 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4527 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4528 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004529 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
4530 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004531 { } /* end */
4532};
4533
4534/* switch to external mic if plugged */
4535static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4536{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004537 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004538 snd_hda_codec_write(codec, 0x0c, 0,
4539 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004540 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004541 snd_hda_codec_write(codec, 0x0c, 0,
4542 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004543}
4544
4545
4546/* unsolicited event for HP jack sensing */
4547static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4548 unsigned int res)
4549{
4550 switch (res >> 26) {
4551 case AD1884A_HP_EVENT:
4552 ad1884a_hp_automute(codec);
4553 break;
4554 case AD1884A_MIC_EVENT:
4555 ad1984a_touchsmart_automic(codec);
4556 break;
4557 }
4558}
4559
4560/* initialize jack-sensing, too */
4561static int ad1984a_touchsmart_init(struct hda_codec *codec)
4562{
4563 ad198x_init(codec);
4564 ad1884a_hp_automute(codec);
4565 ad1984a_touchsmart_automic(codec);
4566 return 0;
4567}
4568
4569
4570/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004571 */
4572
4573enum {
4574 AD1884A_DESKTOP,
4575 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004576 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004577 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004578 AD1984A_TOUCHSMART,
David Henningsson677cd902011-02-07 15:19:34 +01004579 AD1984A_PRECISION,
Takashi Iwaic5059252008-02-16 09:43:56 +01004580 AD1884A_MODELS
4581};
4582
Takashi Iwaiea734962011-01-17 11:29:34 +01004583static const char * const ad1884a_models[AD1884A_MODELS] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004584 [AD1884A_DESKTOP] = "desktop",
4585 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004586 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004587 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004588 [AD1984A_TOUCHSMART] = "touchsmart",
David Henningsson677cd902011-02-07 15:19:34 +01004589 [AD1984A_PRECISION] = "precision",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004590};
4591
Takashi Iwai498f5b12011-05-02 11:33:15 +02004592static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004593 SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004594 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004595 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004596 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004597 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004598 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004599 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4600 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004601 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004602 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004603 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004604 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004605};
4606
4607static int patch_ad1884a(struct hda_codec *codec)
4608{
4609 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004610 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004611
4612 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4613 if (spec == NULL)
4614 return -ENOMEM;
4615
Takashi Iwaic5059252008-02-16 09:43:56 +01004616 codec->spec = spec;
4617
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004618 err = snd_hda_attach_beep_device(codec, 0x10);
4619 if (err < 0) {
4620 ad198x_free(codec);
4621 return err;
4622 }
4623 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4624
Takashi Iwaic5059252008-02-16 09:43:56 +01004625 spec->multiout.max_channels = 2;
4626 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4627 spec->multiout.dac_nids = ad1884a_dac_nids;
4628 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4629 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4630 spec->adc_nids = ad1884a_adc_nids;
4631 spec->capsrc_nids = ad1884a_capsrc_nids;
4632 spec->input_mux = &ad1884a_capture_source;
4633 spec->num_mixers = 1;
4634 spec->mixers[0] = ad1884a_base_mixers;
4635 spec->num_init_verbs = 1;
4636 spec->init_verbs[0] = ad1884a_init_verbs;
4637 spec->spdif_route = 0;
4638#ifdef CONFIG_SND_HDA_POWER_SAVE
4639 spec->loopback.amplist = ad1884a_loopbacks;
4640#endif
4641 codec->patch_ops = ad198x_patch_ops;
4642
4643 /* override some parameters */
4644 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004645 ad1884a_models,
4646 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004647 switch (board_config) {
4648 case AD1884A_LAPTOP:
4649 spec->mixers[0] = ad1884a_laptop_mixers;
4650 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4651 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004652 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4653 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004654 /* set the upper-limit for mixer amp to 0dB for avoiding the
4655 * possible damage by overloading
4656 */
4657 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4658 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4659 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4660 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4661 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004662 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004663 case AD1884A_MOBILE:
4664 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004665 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004666 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004667 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4668 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004669 /* set the upper-limit for mixer amp to 0dB for avoiding the
4670 * possible damage by overloading
4671 */
4672 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4673 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4674 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4675 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4676 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004677 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004678 case AD1884A_THINKPAD:
4679 spec->mixers[0] = ad1984a_thinkpad_mixers;
4680 spec->init_verbs[spec->num_init_verbs++] =
4681 ad1984a_thinkpad_verbs;
4682 spec->multiout.dig_out_nid = 0;
4683 spec->input_mux = &ad1984a_thinkpad_capture_source;
4684 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4685 codec->patch_ops.init = ad1984a_thinkpad_init;
4686 break;
David Henningsson677cd902011-02-07 15:19:34 +01004687 case AD1984A_PRECISION:
4688 spec->mixers[0] = ad1984a_precision_mixers;
4689 spec->init_verbs[spec->num_init_verbs++] =
4690 ad1984a_precision_verbs;
4691 spec->multiout.dig_out_nid = 0;
4692 codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
4693 codec->patch_ops.init = ad1984a_precision_init;
4694 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004695 case AD1984A_TOUCHSMART:
4696 spec->mixers[0] = ad1984a_touchsmart_mixers;
4697 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4698 spec->multiout.dig_out_nid = 0;
4699 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4700 codec->patch_ops.init = ad1984a_touchsmart_init;
4701 /* set the upper-limit for mixer amp to 0dB for avoiding the
4702 * possible damage by overloading
4703 */
4704 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4705 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4706 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4707 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4708 (1 << AC_AMPCAP_MUTE_SHIFT));
4709 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004710 }
4711
Takashi Iwai729d55b2009-12-25 22:49:01 +01004712 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004713 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004714
Takashi Iwaic5059252008-02-16 09:43:56 +01004715 return 0;
4716}
4717
4718
4719/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004720 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004721 *
4722 * port-A - front hp-out
4723 * port-B - front mic-in
4724 * port-C - rear line-in, shared surr-out (3stack)
4725 * port-D - rear line-out
4726 * port-E - rear mic-in, shared clfe-out (3stack)
4727 * port-F - rear surr-out (6stack)
4728 * port-G - rear clfe-out (6stack)
4729 */
4730
Takashi Iwai498f5b12011-05-02 11:33:15 +02004731static const hda_nid_t ad1882_dac_nids[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004732 0x04, 0x03, 0x05
4733};
4734
Takashi Iwai498f5b12011-05-02 11:33:15 +02004735static const hda_nid_t ad1882_adc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004736 0x08, 0x09,
4737};
4738
Takashi Iwai498f5b12011-05-02 11:33:15 +02004739static const hda_nid_t ad1882_capsrc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004740 0x0c, 0x0d,
4741};
4742
4743#define AD1882_SPDIF_OUT 0x02
4744
4745/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004746static const struct hda_input_mux ad1882_capture_source = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004747 .num_items = 5,
4748 .items = {
4749 { "Front Mic", 0x1 },
4750 { "Mic", 0x4 },
4751 { "Line", 0x2 },
4752 { "CD", 0x3 },
4753 { "Mix", 0x7 },
4754 },
4755};
4756
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004757/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004758static const struct hda_input_mux ad1882a_capture_source = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004759 .num_items = 5,
4760 .items = {
4761 { "Front Mic", 0x1 },
4762 { "Mic", 0x4},
4763 { "Line", 0x2 },
4764 { "Digital Mic", 0x06 },
4765 { "Mix", 0x7 },
4766 },
4767};
4768
Takashi Iwai498f5b12011-05-02 11:33:15 +02004769static const struct snd_kcontrol_new ad1882_base_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004770 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4771 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4772 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4773 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4774 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4775 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4776 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4777 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004778
David Henningsson5f99f862011-01-04 15:24:24 +01004779 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
4780 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
4781 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
Takashi Iwai0ac85512007-06-20 15:46:13 +02004782 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4783 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4784 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4785 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4786 {
4787 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4788 /* The multiple "Capture Source" controls confuse alsamixer
4789 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004790 */
4791 /* .name = "Capture Source", */
4792 .name = "Input Source",
4793 .count = 2,
4794 .info = ad198x_mux_enum_info,
4795 .get = ad198x_mux_enum_get,
4796 .put = ad198x_mux_enum_put,
4797 },
4798 /* SPDIF controls */
4799 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4800 {
4801 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4802 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4803 /* identical with ad1983 */
4804 .info = ad1983_spdif_route_info,
4805 .get = ad1983_spdif_route_get,
4806 .put = ad1983_spdif_route_put,
4807 },
4808 { } /* end */
4809};
4810
Takashi Iwai498f5b12011-05-02 11:33:15 +02004811static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004812 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4813 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4814 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4815 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4816 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4817 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4818 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4819 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004820 { } /* end */
4821};
4822
Takashi Iwai498f5b12011-05-02 11:33:15 +02004823static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004824 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4825 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4826 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4827 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4828 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4829 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4830 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4831 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004832 HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004833 { } /* end */
4834};
4835
Takashi Iwai498f5b12011-05-02 11:33:15 +02004836static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004837 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4838 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4839 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4840 {
4841 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4842 .name = "Channel Mode",
4843 .info = ad198x_ch_mode_info,
4844 .get = ad198x_ch_mode_get,
4845 .put = ad198x_ch_mode_put,
4846 },
4847 { } /* end */
4848};
4849
Takashi Iwai498f5b12011-05-02 11:33:15 +02004850static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004851 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4852 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4853 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4854 { } /* end */
4855};
4856
Takashi Iwai498f5b12011-05-02 11:33:15 +02004857static const struct hda_verb ad1882_ch2_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004858 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4859 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4860 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4861 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4862 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4863 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4864 { } /* end */
4865};
4866
Takashi Iwai498f5b12011-05-02 11:33:15 +02004867static const struct hda_verb ad1882_ch4_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004868 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4869 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4870 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4871 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4872 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4873 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4874 { } /* end */
4875};
4876
Takashi Iwai498f5b12011-05-02 11:33:15 +02004877static const struct hda_verb ad1882_ch6_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004878 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4879 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4880 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4881 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4882 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4883 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4884 { } /* end */
4885};
4886
Takashi Iwai498f5b12011-05-02 11:33:15 +02004887static const struct hda_channel_mode ad1882_modes[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004888 { 2, ad1882_ch2_init },
4889 { 4, ad1882_ch4_init },
4890 { 6, ad1882_ch6_init },
4891};
4892
4893/*
4894 * initialization verbs
4895 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004896static const struct hda_verb ad1882_init_verbs[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004897 /* DACs; mute as default */
4898 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4899 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4900 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4901 /* Port-A (HP) mixer */
4902 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4903 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4904 /* Port-A pin */
4905 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4906 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4907 /* HP selector - select DAC2 */
4908 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4909 /* Port-D (Line-out) mixer */
4910 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4911 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4912 /* Port-D pin */
4913 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4914 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4915 /* Mono-out mixer */
4916 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4917 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4918 /* Mono-out pin */
4919 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4920 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4921 /* Port-B (front mic) pin */
4922 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4923 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4924 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4925 /* Port-C (line-in) pin */
4926 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4927 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4928 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4929 /* Port-C mixer - mute as input */
4930 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4931 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4932 /* Port-E (mic-in) pin */
4933 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4934 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4935 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4936 /* Port-E mixer - mute as input */
4937 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4938 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4939 /* Port-F (surround) */
4940 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4941 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4942 /* Port-G (CLFE) */
4943 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4944 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4945 /* Analog mixer; mute as default */
4946 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4947 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4948 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4949 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4950 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4951 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4952 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4953 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4954 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4955 /* Analog Mix output amp */
4956 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4957 /* SPDIF output selector */
4958 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4959 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4960 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4961 { } /* end */
4962};
4963
Takashi Iwaicb53c622007-08-10 17:21:45 +02004964#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02004965static const struct hda_amp_list ad1882_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02004966 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4967 { 0x20, HDA_INPUT, 1 }, /* Mic */
4968 { 0x20, HDA_INPUT, 4 }, /* Line */
4969 { 0x20, HDA_INPUT, 6 }, /* CD */
4970 { } /* end */
4971};
4972#endif
4973
Takashi Iwai0ac85512007-06-20 15:46:13 +02004974/* models */
4975enum {
4976 AD1882_3STACK,
4977 AD1882_6STACK,
4978 AD1882_MODELS
4979};
4980
Takashi Iwaiea734962011-01-17 11:29:34 +01004981static const char * const ad1882_models[AD1986A_MODELS] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004982 [AD1882_3STACK] = "3stack",
4983 [AD1882_6STACK] = "6stack",
4984};
4985
4986
4987static int patch_ad1882(struct hda_codec *codec)
4988{
4989 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004990 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004991
4992 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4993 if (spec == NULL)
4994 return -ENOMEM;
4995
Takashi Iwai0ac85512007-06-20 15:46:13 +02004996 codec->spec = spec;
4997
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004998 err = snd_hda_attach_beep_device(codec, 0x10);
4999 if (err < 0) {
5000 ad198x_free(codec);
5001 return err;
5002 }
5003 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
5004
Takashi Iwai0ac85512007-06-20 15:46:13 +02005005 spec->multiout.max_channels = 6;
5006 spec->multiout.num_dacs = 3;
5007 spec->multiout.dac_nids = ad1882_dac_nids;
5008 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
5009 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
5010 spec->adc_nids = ad1882_adc_nids;
5011 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005012 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005013 spec->input_mux = &ad1882_capture_source;
5014 else
5015 spec->input_mux = &ad1882a_capture_source;
5016 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005017 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005018 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005019 spec->mixers[1] = ad1882_loopback_mixers;
5020 else
5021 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005022 spec->num_init_verbs = 1;
5023 spec->init_verbs[0] = ad1882_init_verbs;
5024 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02005025#ifdef CONFIG_SND_HDA_POWER_SAVE
5026 spec->loopback.amplist = ad1882_loopbacks;
5027#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01005028 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005029
5030 codec->patch_ops = ad198x_patch_ops;
5031
5032 /* override some parameters */
5033 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
5034 ad1882_models, NULL);
5035 switch (board_config) {
5036 default:
5037 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005038 spec->num_mixers = 3;
5039 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005040 spec->channel_mode = ad1882_modes;
5041 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
5042 spec->need_dac_fix = 1;
5043 spec->multiout.max_channels = 2;
5044 spec->multiout.num_dacs = 1;
5045 break;
5046 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005047 spec->num_mixers = 3;
5048 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005049 break;
5050 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01005051
5052 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02005053 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01005054
Takashi Iwai0ac85512007-06-20 15:46:13 +02005055 return 0;
5056}
5057
5058
5059/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07005060 * patch entries
5061 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02005062static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01005063 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02005064 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005065 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005066 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005067 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
5068 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02005069 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
5070 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005071 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005072 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01005073 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02005074 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005075 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02005076 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
5077 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005078 {} /* terminator */
5079};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01005080
5081MODULE_ALIAS("snd-hda-codec-id:11d4*");
5082
5083MODULE_LICENSE("GPL");
5084MODULE_DESCRIPTION("Analog Devices HD-audio codec");
5085
5086static struct hda_codec_preset_list analog_list = {
5087 .preset = snd_hda_preset_analog,
5088 .owner = THIS_MODULE,
5089};
5090
5091static int __init patch_analog_init(void)
5092{
5093 return snd_hda_add_codec_preset(&analog_list);
5094}
5095
5096static void __exit patch_analog_exit(void)
5097{
5098 snd_hda_delete_codec_preset(&analog_list);
5099}
5100
5101module_init(patch_analog_init)
5102module_exit(patch_analog_exit)