blob: a186b3da20b7c08a2dbae36fba6e262a7b0fc635 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/slab.h>
24#include <linux/pci.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040025#include <linux/module.h>
Ingo Molnar62932df2006-01-16 16:34:20 +010026
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <sound/core.h>
28#include "hda_codec.h"
29#include "hda_local.h"
Takashi Iwai128bc4b2012-05-07 17:42:31 +020030#include "hda_auto_parser.h"
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010031#include "hda_beep.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020032#include "hda_jack.h"
Takashi Iwai78bb3cb2012-12-21 15:17:06 +010033#include "hda_generic.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020035struct ad198x_spec {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +010036 struct hda_gen_spec gen;
37
Takashi Iwai498f5b12011-05-02 11:33:15 +020038 const struct snd_kcontrol_new *mixers[6];
Takashi Iwai985be542005-11-02 18:26:49 +010039 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010040 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +010041 hda_nid_t beep_dev_nid;
Raymond Yau28220842011-02-08 19:58:25 +080042 const struct hda_verb *init_verbs[6]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010043 * don't forget NULL termination!
44 */
45 unsigned int num_init_verbs;
46
47 /* playback */
48 struct hda_multi_out multiout; /* playback set-up
49 * max_channels, dacs must be set
50 * dig_out_nid and hp_nid are optional
51 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010052 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020053 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010054
55 /* 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
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010074 unsigned int jack_present: 1;
75 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010076 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai18478e82012-03-09 17:51:10 +010077 unsigned int avoid_init_slave_vol:1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +020078
Takashi Iwai83012a72012-08-24 18:38:08 +020079#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +020080 struct hda_loopback_check loopback;
81#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010082 /* for virtual master */
83 hda_nid_t vmaster_nid;
Takashi Iwaiea734962011-01-17 11:29:34 +010084 const char * const *slave_vols;
85 const char * const *slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086};
87
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020088/*
89 * input MUX handling (common part)
90 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010091static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020092{
93 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
94 struct ad198x_spec *spec = codec->spec;
95
96 return snd_hda_input_mux_info(spec->input_mux, uinfo);
97}
98
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010099static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200100{
101 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
102 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100103 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200104
Takashi Iwai985be542005-11-02 18:26:49 +0100105 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200106 return 0;
107}
108
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100109static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200110{
111 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
112 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100113 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200114
115 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100116 spec->capsrc_nids[adc_idx],
117 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200118}
119
120/*
121 * initialization (common callbacks)
122 */
123static int ad198x_init(struct hda_codec *codec)
124{
125 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100126 int i;
127
128 for (i = 0; i < spec->num_init_verbs; i++)
129 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200130 return 0;
131}
132
Takashi Iwai9322ca52012-02-03 14:28:01 +0100133static const char * const ad_slave_pfxs[] = {
134 "Front", "Surround", "Center", "LFE", "Side",
135 "Headphone", "Mono", "Speaker", "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100136 NULL
137};
138
Takashi Iwai9322ca52012-02-03 14:28:01 +0100139static const char * const ad1988_6stack_fp_slave_pfxs[] = {
140 "Front", "Surround", "Center", "LFE", "Side", "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100141 NULL
142};
143
Takashi Iwai67d634c2009-11-16 15:35:59 +0100144#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100145/* additional beep mixers; the actual parameters are overwritten at build */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200146static const struct snd_kcontrol_new ad_beep_mixer[] = {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100147 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200148 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100149 { } /* end */
150};
151
Takashi Iwai498f5b12011-05-02 11:33:15 +0200152static const struct snd_kcontrol_new ad_beep2_mixer[] = {
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100153 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
154 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
155 { } /* end */
156};
157
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100158#define set_beep_amp(spec, nid, idx, dir) \
159 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100160#else
161#define set_beep_amp(spec, nid, idx, dir) /* NOP */
162#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100163
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100164#ifdef CONFIG_SND_HDA_INPUT_BEEP
165static int create_beep_ctls(struct hda_codec *codec)
166{
167 struct ad198x_spec *spec = codec->spec;
168 const struct snd_kcontrol_new *knew;
169
170 if (!spec->beep_amp)
171 return 0;
172
173 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
174 for ( ; knew->name; knew++) {
175 int err;
176 struct snd_kcontrol *kctl;
177 kctl = snd_ctl_new1(knew, codec);
178 if (!kctl)
179 return -ENOMEM;
180 kctl->private_value = spec->beep_amp;
181 err = snd_hda_ctl_add(codec, 0, kctl);
182 if (err < 0)
183 return err;
184 }
185 return 0;
186}
187#else
188#define create_beep_ctls(codec) 0
189#endif
190
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200191static int ad198x_build_controls(struct hda_codec *codec)
192{
193 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100194 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100195 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200196 int err;
197
Takashi Iwai985be542005-11-02 18:26:49 +0100198 for (i = 0; i < spec->num_mixers; i++) {
199 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
200 if (err < 0)
201 return err;
202 }
203 if (spec->multiout.dig_out_nid) {
Stephen Warren74b654c2011-06-01 11:14:18 -0600204 err = snd_hda_create_spdif_out_ctls(codec,
205 spec->multiout.dig_out_nid,
206 spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100207 if (err < 0)
208 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100209 err = snd_hda_create_spdif_share_sw(codec,
210 &spec->multiout);
211 if (err < 0)
212 return err;
213 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100214 }
215 if (spec->dig_in_nid) {
216 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
217 if (err < 0)
218 return err;
219 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100220
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100221 /* create beep controls if needed */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100222 err = create_beep_ctls(codec);
223 if (err < 0)
224 return err;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100225
Takashi Iwai2134ea42008-01-10 16:53:55 +0100226 /* if we have no master control, let's create it */
227 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100228 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100229 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100230 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai18478e82012-03-09 17:51:10 +0100231 err = __snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100232 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100233 (spec->slave_vols ?
Takashi Iwai9322ca52012-02-03 14:28:01 +0100234 spec->slave_vols : ad_slave_pfxs),
Takashi Iwai18478e82012-03-09 17:51:10 +0100235 "Playback Volume",
Takashi Iwai29e58532012-03-12 12:25:03 +0100236 !spec->avoid_init_slave_vol, NULL);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100237 if (err < 0)
238 return err;
239 }
240 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
241 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
242 NULL,
243 (spec->slave_sws ?
Takashi Iwai9322ca52012-02-03 14:28:01 +0100244 spec->slave_sws : ad_slave_pfxs),
245 "Playback Switch");
Takashi Iwai2134ea42008-01-10 16:53:55 +0100246 if (err < 0)
247 return err;
248 }
249
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100250 /* assign Capture Source enums to NID */
251 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
252 if (!kctl)
253 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
254 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100255 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100256 if (err < 0)
257 return err;
258 }
259
260 /* assign IEC958 enums to NID */
261 kctl = snd_hda_find_mixer_ctl(codec,
262 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
263 if (kctl) {
264 err = snd_hda_add_nid(codec, kctl, 0,
265 spec->multiout.dig_out_nid);
266 if (err < 0)
267 return err;
268 }
269
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200270 return 0;
271}
272
Takashi Iwai83012a72012-08-24 18:38:08 +0200273#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +0200274static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
275{
276 struct ad198x_spec *spec = codec->spec;
277 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
278}
279#endif
280
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200281/*
282 * Analog playback callbacks
283 */
284static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
285 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100286 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200287{
288 struct ad198x_spec *spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100289 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
Takashi Iwai9a081602008-02-12 18:37:26 +0100290 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200291}
292
293static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
294 struct hda_codec *codec,
295 unsigned int stream_tag,
296 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100297 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200298{
299 struct ad198x_spec *spec = codec->spec;
300 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
301 format, substream);
302}
303
304static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
305 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100306 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200307{
308 struct ad198x_spec *spec = codec->spec;
309 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
310}
311
312/*
313 * Digital out
314 */
315static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
316 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100317 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200318{
319 struct ad198x_spec *spec = codec->spec;
320 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
321}
322
323static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
324 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100325 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200326{
327 struct ad198x_spec *spec = codec->spec;
328 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
329}
330
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200331static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
332 struct hda_codec *codec,
333 unsigned int stream_tag,
334 unsigned int format,
335 struct snd_pcm_substream *substream)
336{
337 struct ad198x_spec *spec = codec->spec;
338 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
339 format, substream);
340}
341
Takashi Iwai9411e212009-02-13 11:32:28 +0100342static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
343 struct hda_codec *codec,
344 struct snd_pcm_substream *substream)
345{
346 struct ad198x_spec *spec = codec->spec;
347 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
348}
349
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200350/*
351 * Analog capture
352 */
353static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
354 struct hda_codec *codec,
355 unsigned int stream_tag,
356 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100357 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200358{
359 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100360 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
361 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200362 return 0;
363}
364
365static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
366 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100367 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200368{
369 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100370 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200371 return 0;
372}
373
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200374/*
375 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200376static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200377 .substreams = 1,
378 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100379 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200380 .nid = 0, /* fill later */
381 .ops = {
382 .open = ad198x_playback_pcm_open,
383 .prepare = ad198x_playback_pcm_prepare,
Raymond Yau34588702011-09-23 19:03:25 +0800384 .cleanup = ad198x_playback_pcm_cleanup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200385 },
386};
387
Takashi Iwai498f5b12011-05-02 11:33:15 +0200388static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100389 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200390 .channels_min = 2,
391 .channels_max = 2,
392 .nid = 0, /* fill later */
393 .ops = {
394 .prepare = ad198x_capture_pcm_prepare,
395 .cleanup = ad198x_capture_pcm_cleanup
396 },
397};
398
Takashi Iwai498f5b12011-05-02 11:33:15 +0200399static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200400 .substreams = 1,
401 .channels_min = 2,
402 .channels_max = 2,
403 .nid = 0, /* fill later */
404 .ops = {
405 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200406 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100407 .prepare = ad198x_dig_playback_pcm_prepare,
408 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200409 },
410};
411
Takashi Iwai498f5b12011-05-02 11:33:15 +0200412static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100413 .substreams = 1,
414 .channels_min = 2,
415 .channels_max = 2,
416 /* NID is set in alc_build_pcms */
417};
418
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200419static int ad198x_build_pcms(struct hda_codec *codec)
420{
421 struct ad198x_spec *spec = codec->spec;
422 struct hda_pcm *info = spec->pcm_rec;
423
424 codec->num_pcms = 1;
425 codec->pcm_info = info;
426
427 info->name = "AD198x Analog";
428 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
429 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
430 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
431 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100432 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
433 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200434
435 if (spec->multiout.dig_out_nid) {
436 info++;
437 codec->num_pcms++;
Takashi Iwaiae24c312012-11-05 12:32:46 +0100438 codec->spdif_status_reset = 1;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200439 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100440 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200441 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
442 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100443 if (spec->dig_in_nid) {
444 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
445 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
446 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200447 }
448
449 return 0;
450}
451
Daniel T Chenea52bf22009-12-27 18:48:29 -0500452static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
453 hda_nid_t hp)
454{
Raymond Yaua01ef052011-06-01 15:09:48 +0800455 if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
456 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100457 !codec->inv_eapd ? 0x00 : 0x02);
Raymond Yaua01ef052011-06-01 15:09:48 +0800458 if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
459 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100460 !codec->inv_eapd ? 0x00 : 0x02);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500461}
462
463static void ad198x_power_eapd(struct hda_codec *codec)
464{
465 /* We currently only handle front, HP */
466 switch (codec->vendor_id) {
467 case 0x11d41882:
468 case 0x11d4882a:
469 case 0x11d41884:
470 case 0x11d41984:
471 case 0x11d41883:
472 case 0x11d4184a:
473 case 0x11d4194a:
474 case 0x11d4194b:
Takashi Iwai4dffbe02011-06-03 10:05:02 +0200475 case 0x11d41988:
476 case 0x11d4198b:
477 case 0x11d4989a:
478 case 0x11d4989b:
Daniel T Chenea52bf22009-12-27 18:48:29 -0500479 ad198x_power_eapd_write(codec, 0x12, 0x11);
480 break;
481 case 0x11d41981:
482 case 0x11d41983:
483 ad198x_power_eapd_write(codec, 0x05, 0x06);
484 break;
485 case 0x11d41986:
486 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
487 break;
Daniel T Chenea52bf22009-12-27 18:48:29 -0500488 }
489}
490
Takashi Iwai0da26922011-04-26 15:18:33 +0200491static void ad198x_shutup(struct hda_codec *codec)
492{
493 snd_hda_shutup_pins(codec);
494 ad198x_power_eapd(codec);
495}
496
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200497static void ad198x_free(struct hda_codec *codec)
498{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100499 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100500
Takashi Iwai603c4012008-07-30 15:01:44 +0200501 if (!spec)
502 return;
503
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100504 snd_hda_gen_spec_free(&spec->gen);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100505 kfree(spec);
506 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200507}
508
Takashi Iwai2a439522011-07-26 09:52:50 +0200509#ifdef CONFIG_PM
Takashi Iwai68cb2b52012-07-02 15:20:37 +0200510static int ad198x_suspend(struct hda_codec *codec)
Daniel T Chenea52bf22009-12-27 18:48:29 -0500511{
512 ad198x_shutup(codec);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500513 return 0;
514}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500515#endif
516
Takashi Iwai498f5b12011-05-02 11:33:15 +0200517static const struct hda_codec_ops ad198x_patch_ops = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200518 .build_controls = ad198x_build_controls,
519 .build_pcms = ad198x_build_pcms,
520 .init = ad198x_init,
521 .free = ad198x_free,
Takashi Iwai2a439522011-07-26 09:52:50 +0200522#ifdef CONFIG_PM
Takashi Iwai83012a72012-08-24 18:38:08 +0200523 .check_power_status = ad198x_check_power_status,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500524 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500525#endif
526 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200527};
528
529
530/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100531 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100532 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100533 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200534#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100535
536static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
537 struct snd_ctl_elem_value *ucontrol)
538{
539 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
540 struct ad198x_spec *spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100541 if (codec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100542 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
543 else
544 ucontrol->value.integer.value[0] = spec->cur_eapd;
545 return 0;
546}
547
548static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
549 struct snd_ctl_elem_value *ucontrol)
550{
551 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
552 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100553 hda_nid_t nid = kcontrol->private_value & 0xff;
554 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100555 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100556 if (codec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100557 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200558 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100559 return 0;
560 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200561 snd_hda_codec_write_cache(codec, nid,
562 0, AC_VERB_SET_EAPD_BTLENABLE,
563 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100564 return 1;
565}
566
Takashi Iwai9230d212006-03-13 13:49:49 +0100567static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
568 struct snd_ctl_elem_info *uinfo);
569static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
570 struct snd_ctl_elem_value *ucontrol);
571static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
572 struct snd_ctl_elem_value *ucontrol);
573
574
Takashi Iwai18a815d2006-03-01 19:54:39 +0100575/*
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100576 * Automatic parse of I/O pins from the BIOS configuration
577 */
578
579static int ad198x_auto_build_controls(struct hda_codec *codec)
580{
581 int err;
582
583 err = snd_hda_gen_build_controls(codec);
584 if (err < 0)
585 return err;
586 err = create_beep_ctls(codec);
587 if (err < 0)
588 return err;
589 return 0;
590}
591
592static const struct hda_codec_ops ad198x_auto_patch_ops = {
593 .build_controls = ad198x_auto_build_controls,
594 .build_pcms = snd_hda_gen_build_pcms,
595 .init = snd_hda_gen_init,
596 .free = ad198x_free,
Takashi Iwai8a6c21a2013-01-18 07:51:17 +0100597 .unsol_event = snd_hda_jack_unsol_event,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100598#ifdef CONFIG_PM
599 .check_power_status = snd_hda_gen_check_power_status,
600 .suspend = ad198x_suspend,
601#endif
602 .reboot_notify = ad198x_shutup,
603};
604
605
606static int ad198x_parse_auto_config(struct hda_codec *codec)
607{
608 struct ad198x_spec *spec = codec->spec;
609 struct auto_pin_cfg *cfg = &spec->gen.autocfg;
610 int err;
611
612 codec->spdif_status_reset = 1;
613 codec->no_trigger_sense = 1;
614 codec->no_sticky_stream = 1;
615
616 spec->gen.indep_hp = 1;
617
618 err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
619 if (err < 0)
620 return err;
621 err = snd_hda_gen_parse_auto_config(codec, cfg);
622 if (err < 0)
623 return err;
624
625 if (spec->beep_dev_nid) {
626 err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid);
627 if (err < 0)
628 return err;
629 }
630
631 codec->patch_ops = ad198x_auto_patch_ops;
632
633 return 0;
634}
635
636/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200637 * AD1986A specific
638 */
639
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640#define AD1986A_SPDIF_OUT 0x02
641#define AD1986A_FRONT_DAC 0x03
642#define AD1986A_SURR_DAC 0x04
643#define AD1986A_CLFE_DAC 0x05
644#define AD1986A_ADC 0x06
645
Takashi Iwai498f5b12011-05-02 11:33:15 +0200646static const hda_nid_t ad1986a_dac_nids[3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
648};
Takashi Iwai498f5b12011-05-02 11:33:15 +0200649static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
650static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Takashi Iwai498f5b12011-05-02 11:33:15 +0200652static const struct hda_input_mux ad1986a_capture_source = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 .num_items = 7,
654 .items = {
655 { "Mic", 0x0 },
656 { "CD", 0x1 },
657 { "Aux", 0x3 },
658 { "Line", 0x4 },
659 { "Mix", 0x5 },
660 { "Mono", 0x6 },
661 { "Phone", 0x7 },
662 },
663};
664
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
Takashi Iwai498f5b12011-05-02 11:33:15 +0200666static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200667 .ops = &snd_hda_bind_vol,
668 .values = {
669 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
670 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
671 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
672 0
673 },
674};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Takashi Iwai498f5b12011-05-02 11:33:15 +0200676static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200677 .ops = &snd_hda_bind_sw,
678 .values = {
679 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
680 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
681 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
682 0
683 },
684};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
686/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 * mixers
688 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200689static const struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200690 /*
691 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
692 */
693 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
694 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
696 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
697 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
698 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
699 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
700 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
701 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
702 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
703 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
704 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
705 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
706 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
707 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
708 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
709 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
710 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
711 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
712 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100713 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
715 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
716 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
717 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
718 {
719 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
720 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200721 .info = ad198x_mux_enum_info,
722 .get = ad198x_mux_enum_get,
723 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 },
725 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
726 { } /* end */
727};
728
Takashi Iwai9230d212006-03-13 13:49:49 +0100729/* additional mixers for 3stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200730static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100731 {
732 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
733 .name = "Channel Mode",
734 .info = ad198x_ch_mode_info,
735 .get = ad198x_ch_mode_get,
736 .put = ad198x_ch_mode_put,
737 },
738 { } /* end */
739};
740
741/* laptop model - 2ch only */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200742static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
Takashi Iwai9230d212006-03-13 13:49:49 +0100743
Takashi Iwai20a45e82007-08-15 22:20:45 +0200744/* master controls both pins 0x1a and 0x1b */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200745static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200746 .ops = &snd_hda_bind_vol,
747 .values = {
748 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
749 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
750 0,
751 },
752};
753
Takashi Iwai498f5b12011-05-02 11:33:15 +0200754static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200755 .ops = &snd_hda_bind_sw,
756 .values = {
757 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
758 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
759 0,
760 },
761};
762
Takashi Iwai498f5b12011-05-02 11:33:15 +0200763static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100764 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
765 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200766 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
767 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100768 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
769 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
770 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
771 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
772 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
773 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
774 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
775 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100776 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100777 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100778 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
779 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
780 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
781 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
782 {
783 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
784 .name = "Capture Source",
785 .info = ad198x_mux_enum_info,
786 .get = ad198x_mux_enum_get,
787 .put = ad198x_mux_enum_put,
788 },
789 { } /* end */
790};
791
Takashi Iwai825aa9722006-03-17 10:50:49 +0100792/* laptop-eapd model - 2ch only */
793
Takashi Iwai498f5b12011-05-02 11:33:15 +0200794static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100795 .num_items = 3,
796 .items = {
797 { "Mic", 0x0 },
798 { "Internal Mic", 0x4 },
799 { "Mix", 0x5 },
800 },
801};
802
Takashi Iwai498f5b12011-05-02 11:33:15 +0200803static const struct hda_input_mux ad1986a_automic_capture_source = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100804 .num_items = 2,
805 .items = {
806 { "Mic", 0x0 },
807 { "Mix", 0x5 },
808 },
809};
810
Takashi Iwai498f5b12011-05-02 11:33:15 +0200811static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200812 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
813 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200814 { } /* end */
815};
816
Takashi Iwai498f5b12011-05-02 11:33:15 +0200817static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100818 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
819 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100820 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
821 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100822 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100823 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
824 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
825 {
826 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
827 .name = "Capture Source",
828 .info = ad198x_mux_enum_info,
829 .get = ad198x_mux_enum_get,
830 .put = ad198x_mux_enum_put,
831 },
832 {
833 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
834 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100835 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100836 .info = ad198x_eapd_info,
837 .get = ad198x_eapd_get,
838 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100839 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100840 },
841 { } /* end */
842};
843
Takashi Iwai498f5b12011-05-02 11:33:15 +0200844static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
Takashi Iwai16d11a82009-06-24 14:07:53 +0200845 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
846 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100847 { } /* end */
848};
849
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100850/* re-connect the mic boost input according to the jack sensing */
851static void ad1986a_automic(struct hda_codec *codec)
852{
853 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100854 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100855 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
856 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100857 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100858}
859
860#define AD1986A_MIC_EVENT 0x36
861
862static void ad1986a_automic_unsol_event(struct hda_codec *codec,
863 unsigned int res)
864{
865 if ((res >> 26) != AD1986A_MIC_EVENT)
866 return;
867 ad1986a_automic(codec);
868}
869
870static int ad1986a_automic_init(struct hda_codec *codec)
871{
872 ad198x_init(codec);
873 ad1986a_automic(codec);
874 return 0;
875}
876
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200877/* laptop-automute - 2ch only */
878
879static void ad1986a_update_hp(struct hda_codec *codec)
880{
881 struct ad198x_spec *spec = codec->spec;
882 unsigned int mute;
883
884 if (spec->jack_present)
885 mute = HDA_AMP_MUTE; /* mute internal speaker */
886 else
887 /* unmute internal speaker if necessary */
888 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
889 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
890 HDA_AMP_MUTE, mute);
891}
892
893static void ad1986a_hp_automute(struct hda_codec *codec)
894{
895 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200896
Takashi Iwaid56757a2009-11-18 08:00:14 +0100897 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200898 if (spec->inv_jack_detect)
899 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200900 ad1986a_update_hp(codec);
901}
902
903#define AD1986A_HP_EVENT 0x37
904
905static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
906{
907 if ((res >> 26) != AD1986A_HP_EVENT)
908 return;
909 ad1986a_hp_automute(codec);
910}
911
912static int ad1986a_hp_init(struct hda_codec *codec)
913{
914 ad198x_init(codec);
915 ad1986a_hp_automute(codec);
916 return 0;
917}
918
919/* bind hp and internal speaker mute (with plug check) */
920static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
921 struct snd_ctl_elem_value *ucontrol)
922{
923 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai8092e602012-12-13 17:03:30 +0100924 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200925 if (change)
926 ad1986a_update_hp(codec);
927 return change;
928}
929
Takashi Iwai498f5b12011-05-02 11:33:15 +0200930static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200931 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
932 {
933 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
934 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100935 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200936 .info = snd_hda_mixer_amp_switch_info,
937 .get = snd_hda_mixer_amp_switch_get,
938 .put = ad1986a_hp_master_sw_put,
939 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
940 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200941 { } /* end */
942};
943
Takashi Iwai16d11a82009-06-24 14:07:53 +0200944
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945/*
946 * initialization verbs
947 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200948static const struct hda_verb ad1986a_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 /* Front, Surround, CLFE DAC; mute as default */
950 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
951 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
952 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
953 /* Downmix - off */
954 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
955 /* HP, Line-Out, Surround, CLFE selectors */
956 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
957 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
958 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
959 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
960 /* Mono selector */
961 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
962 /* Mic selector: Mic 1/2 pin */
963 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
964 /* Line-in selector: Line-in */
965 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
966 /* Mic 1/2 swap */
967 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
968 /* Record selector: mic */
969 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
970 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
971 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
972 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
973 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
974 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
975 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
976 /* PC beep */
977 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
978 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
979 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
980 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
981 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
982 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
983 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200984 /* HP Pin */
985 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
986 /* Front, Surround, CLFE Pins */
987 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
988 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
989 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
990 /* Mono Pin */
991 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
992 /* Mic Pin */
993 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
994 /* Line, Aux, CD, Beep-In Pin */
995 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
996 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
997 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
998 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
999 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 { } /* end */
1001};
1002
Takashi Iwai498f5b12011-05-02 11:33:15 +02001003static const struct hda_verb ad1986a_ch2_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001004 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001005 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1006 /* Line-in selectors */
1007 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001008 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001009 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1010 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
1011 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001012 { } /* end */
1013};
1014
Takashi Iwai498f5b12011-05-02 11:33:15 +02001015static const struct hda_verb ad1986a_ch4_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001016 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001017 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1018 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001019 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001020 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1021 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001022 { } /* end */
1023};
1024
Takashi Iwai498f5b12011-05-02 11:33:15 +02001025static const struct hda_verb ad1986a_ch6_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001026 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001027 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1028 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001029 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001030 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1031 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001032 { } /* end */
1033};
1034
Takashi Iwai498f5b12011-05-02 11:33:15 +02001035static const struct hda_channel_mode ad1986a_modes[3] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001036 { 2, ad1986a_ch2_init },
1037 { 4, ad1986a_ch4_init },
1038 { 6, ad1986a_ch6_init },
1039};
1040
Takashi Iwai825aa9722006-03-17 10:50:49 +01001041/* eapd initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001042static const struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001043 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +01001044 {}
1045};
1046
Takashi Iwai498f5b12011-05-02 11:33:15 +02001047static const struct hda_verb ad1986a_automic_verbs[] = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001048 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1049 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1050 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1051 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1052 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1053 {}
1054};
1055
Tobin Davisf36090f2007-01-08 11:07:12 +01001056/* Ultra initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001057static const struct hda_verb ad1986a_ultra_init[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001058 /* eapd initialization */
1059 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1060 /* CLFE -> Mic in */
1061 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1062 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1063 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1064 { } /* end */
1065};
1066
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001067/* pin sensing on HP jack */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001068static const struct hda_verb ad1986a_hp_init_verbs[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001069 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1070 {}
1071};
1072
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001073static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1074 unsigned int res)
1075{
1076 switch (res >> 26) {
1077 case AD1986A_HP_EVENT:
1078 ad1986a_hp_automute(codec);
1079 break;
1080 case AD1986A_MIC_EVENT:
1081 ad1986a_automic(codec);
1082 break;
1083 }
1084}
1085
1086static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1087{
1088 ad198x_init(codec);
1089 ad1986a_hp_automute(codec);
1090 ad1986a_automic(codec);
1091 return 0;
1092}
1093
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001094
Takashi Iwai9230d212006-03-13 13:49:49 +01001095/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001096enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001097 AD1986A_AUTO,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001098 AD1986A_6STACK,
1099 AD1986A_3STACK,
1100 AD1986A_LAPTOP,
1101 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001102 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001103 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001104 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001105 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001106 AD1986A_MODELS
1107};
Takashi Iwai9230d212006-03-13 13:49:49 +01001108
Takashi Iwaiea734962011-01-17 11:29:34 +01001109static const char * const ad1986a_models[AD1986A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001110 [AD1986A_AUTO] = "auto",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001111 [AD1986A_6STACK] = "6stack",
1112 [AD1986A_3STACK] = "3stack",
1113 [AD1986A_LAPTOP] = "laptop",
1114 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001115 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001116 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001117 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001118 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001119};
1120
Takashi Iwai498f5b12011-05-02 11:33:15 +02001121static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001122 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001123 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001124 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001125 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001126 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1127 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1128 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1129 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001130 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001131 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001132 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1133 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1134 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1135 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1136 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001137 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001138 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001139 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001140 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001141 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001142 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001143 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001144 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001145 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001146 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001147 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001148 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001149 {}
1150};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
Takashi Iwai83012a72012-08-24 18:38:08 +02001152#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001153static const struct hda_amp_list ad1986a_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001154 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1155 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1156 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1157 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1158 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1159 { } /* end */
1160};
1161#endif
1162
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001163static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1164{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001165 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001166 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1167}
1168
Takashi Iwai361dab32012-05-09 14:35:27 +02001169static int alloc_ad_spec(struct hda_codec *codec)
1170{
1171 struct ad198x_spec *spec;
1172
1173 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1174 if (!spec)
1175 return -ENOMEM;
1176 codec->spec = spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001177 snd_hda_gen_spec_init(&spec->gen);
Takashi Iwai361dab32012-05-09 14:35:27 +02001178 return 0;
1179}
1180
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001181/*
1182 */
1183static int ad1986a_parse_auto_config(struct hda_codec *codec)
1184{
1185 struct ad198x_spec *spec = codec->spec;
1186
1187 /* AD1986A has the inverted EAPD implementation */
1188 codec->inv_eapd = 1;
1189
Takashi Iwaif2f8be42013-01-21 16:40:16 +01001190 spec->gen.mixer_nid = 0x07;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001191 spec->beep_dev_nid = 0x19;
1192 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1193
1194 /* AD1986A has a hardware problem that it can't share a stream
1195 * with multiple output pins. The copy of front to surrounds
1196 * causes noisy or silent outputs at a certain timing, e.g.
1197 * changing the volume.
1198 * So, let's disable the shared stream.
1199 */
1200 spec->gen.multiout.no_share_stream = 1;
1201
1202 return ad198x_parse_auto_config(codec);
1203}
1204
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205static int patch_ad1986a(struct hda_codec *codec)
1206{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001207 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001208 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
Takashi Iwai361dab32012-05-09 14:35:27 +02001210 err = alloc_ad_spec(codec);
1211 if (err < 0)
1212 return err;
1213 spec = codec->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001215 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1216 ad1986a_models,
1217 ad1986a_cfg_tbl);
1218 if (board_config == AD1986A_AUTO) {
1219 err = ad1986a_parse_auto_config(codec);
1220 if (err < 0) {
1221 ad198x_free(codec);
1222 return err;
1223 }
1224 return 0;
1225 }
1226
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001227 err = snd_hda_attach_beep_device(codec, 0x19);
1228 if (err < 0) {
1229 ad198x_free(codec);
1230 return err;
1231 }
1232 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1233
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 spec->multiout.max_channels = 6;
1235 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1236 spec->multiout.dac_nids = ad1986a_dac_nids;
1237 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001238 spec->num_adc_nids = 1;
1239 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001240 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001241 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001242 spec->num_mixers = 1;
1243 spec->mixers[0] = ad1986a_mixers;
1244 spec->num_init_verbs = 1;
1245 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwai83012a72012-08-24 18:38:08 +02001246#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02001247 spec->loopback.amplist = ad1986a_loopbacks;
1248#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001249 spec->vmaster_nid = 0x1b;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001250 codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001252 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
Takashi Iwai9230d212006-03-13 13:49:49 +01001254 /* override some parameters */
Takashi Iwai9230d212006-03-13 13:49:49 +01001255 switch (board_config) {
1256 case AD1986A_3STACK:
1257 spec->num_mixers = 2;
1258 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001259 spec->num_init_verbs = 2;
1260 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001261 spec->channel_mode = ad1986a_modes;
1262 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001263 spec->need_dac_fix = 1;
1264 spec->multiout.max_channels = 2;
1265 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001266 break;
1267 case AD1986A_LAPTOP:
1268 spec->mixers[0] = ad1986a_laptop_mixers;
1269 spec->multiout.max_channels = 2;
1270 spec->multiout.num_dacs = 1;
1271 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1272 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001273 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001274 spec->num_mixers = 3;
1275 spec->mixers[0] = ad1986a_laptop_master_mixers;
1276 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1277 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001278 spec->num_init_verbs = 2;
1279 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1280 spec->multiout.max_channels = 2;
1281 spec->multiout.num_dacs = 1;
1282 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1283 if (!is_jack_available(codec, 0x25))
1284 spec->multiout.dig_out_nid = 0;
1285 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1286 break;
1287 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001288 spec->num_mixers = 2;
1289 spec->mixers[0] = ad1986a_laptop_master_mixers;
1290 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001291 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001292 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001293 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001294 spec->multiout.max_channels = 2;
1295 spec->multiout.num_dacs = 1;
1296 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001297 if (!is_jack_available(codec, 0x25))
1298 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001299 spec->input_mux = &ad1986a_automic_capture_source;
1300 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1301 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001302 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001303 case AD1986A_SAMSUNG_P50:
1304 spec->num_mixers = 2;
1305 spec->mixers[0] = ad1986a_automute_master_mixers;
1306 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1307 spec->num_init_verbs = 4;
1308 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1309 spec->init_verbs[2] = ad1986a_automic_verbs;
1310 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1311 spec->multiout.max_channels = 2;
1312 spec->multiout.num_dacs = 1;
1313 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1314 if (!is_jack_available(codec, 0x25))
1315 spec->multiout.dig_out_nid = 0;
1316 spec->input_mux = &ad1986a_automic_capture_source;
1317 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1318 codec->patch_ops.init = ad1986a_samsung_p50_init;
1319 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001320 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001321 spec->num_mixers = 3;
1322 spec->mixers[0] = ad1986a_automute_master_mixers;
1323 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1324 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001325 spec->num_init_verbs = 3;
1326 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1327 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1328 spec->multiout.max_channels = 2;
1329 spec->multiout.num_dacs = 1;
1330 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001331 if (!is_jack_available(codec, 0x25))
1332 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001333 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1334 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1335 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001336 /* Lenovo N100 seems to report the reversed bit
1337 * for HP jack-sensing
1338 */
1339 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001340 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001341 case AD1986A_ULTRA:
1342 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1343 spec->num_init_verbs = 2;
1344 spec->init_verbs[1] = ad1986a_ultra_init;
1345 spec->multiout.max_channels = 2;
1346 spec->multiout.num_dacs = 1;
1347 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1348 spec->multiout.dig_out_nid = 0;
1349 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001350 }
1351
Takashi Iwaid29240c2007-10-26 12:35:56 +02001352 /* AD1986A has a hardware problem that it can't share a stream
1353 * with multiple output pins. The copy of front to surrounds
1354 * causes noisy or silent outputs at a certain timing, e.g.
1355 * changing the volume.
1356 * So, let's disable the shared stream.
1357 */
1358 spec->multiout.no_share_stream = 1;
1359
Takashi Iwai729d55b2009-12-25 22:49:01 +01001360 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001361 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001362
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return 0;
1364}
1365
1366/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001367 * AD1983 specific
1368 */
1369
1370#define AD1983_SPDIF_OUT 0x02
1371#define AD1983_DAC 0x03
1372#define AD1983_ADC 0x04
1373
Takashi Iwai498f5b12011-05-02 11:33:15 +02001374static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
1375static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
1376static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001377
Takashi Iwai498f5b12011-05-02 11:33:15 +02001378static const struct hda_input_mux ad1983_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001379 .num_items = 4,
1380 .items = {
1381 { "Mic", 0x0 },
1382 { "Line", 0x1 },
1383 { "Mix", 0x2 },
1384 { "Mix Mono", 0x3 },
1385 },
1386};
1387
1388/*
1389 * SPDIF playback route
1390 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001391static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001392{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001393 static const char * const texts[] = { "PCM", "ADC" };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001394
1395 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1396 uinfo->count = 1;
1397 uinfo->value.enumerated.items = 2;
1398 if (uinfo->value.enumerated.item > 1)
1399 uinfo->value.enumerated.item = 1;
1400 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1401 return 0;
1402}
1403
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001404static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001405{
1406 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1407 struct ad198x_spec *spec = codec->spec;
1408
1409 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1410 return 0;
1411}
1412
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001413static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001414{
1415 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1416 struct ad198x_spec *spec = codec->spec;
1417
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001418 if (ucontrol->value.enumerated.item[0] > 1)
1419 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001420 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1421 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001422 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1423 AC_VERB_SET_CONNECT_SEL,
1424 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001425 return 1;
1426 }
1427 return 0;
1428}
1429
Takashi Iwai498f5b12011-05-02 11:33:15 +02001430static const struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001431 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1432 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1433 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1434 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1435 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1436 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1437 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1438 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1439 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1440 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1441 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1442 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001443 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001444 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1445 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1446 {
1447 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1448 .name = "Capture Source",
1449 .info = ad198x_mux_enum_info,
1450 .get = ad198x_mux_enum_get,
1451 .put = ad198x_mux_enum_put,
1452 },
1453 {
1454 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001455 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001456 .info = ad1983_spdif_route_info,
1457 .get = ad1983_spdif_route_get,
1458 .put = ad1983_spdif_route_put,
1459 },
1460 { } /* end */
1461};
1462
Takashi Iwai498f5b12011-05-02 11:33:15 +02001463static const struct hda_verb ad1983_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001464 /* Front, HP, Mono; mute as default */
1465 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1466 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1467 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1468 /* Beep, PCM, Mic, Line-In: mute */
1469 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1470 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1471 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1472 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1473 /* Front, HP selectors; from Mix */
1474 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1475 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1476 /* Mono selector; from Mix */
1477 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1478 /* Mic selector; Mic */
1479 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1480 /* Line-in selector: Line-in */
1481 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1482 /* Mic boost: 0dB */
1483 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1484 /* Record selector: mic */
1485 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1486 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1487 /* SPDIF route: PCM */
1488 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1489 /* Front Pin */
1490 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1491 /* HP Pin */
1492 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1493 /* Mono Pin */
1494 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1495 /* Mic Pin */
1496 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1497 /* Line Pin */
1498 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1499 { } /* end */
1500};
1501
Takashi Iwai83012a72012-08-24 18:38:08 +02001502#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001503static const struct hda_amp_list ad1983_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001504 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1505 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1506 { } /* end */
1507};
1508#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001509
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001510/* models */
1511enum {
1512 AD1983_AUTO,
1513 AD1983_BASIC,
1514 AD1983_MODELS
1515};
1516
1517static const char * const ad1983_models[AD1983_MODELS] = {
1518 [AD1983_AUTO] = "auto",
1519 [AD1983_BASIC] = "basic",
1520};
1521
1522static int ad1983_parse_auto_config(struct hda_codec *codec)
1523{
1524 struct ad198x_spec *spec = codec->spec;
1525
1526 spec->beep_dev_nid = 0x10;
1527 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1528 return ad198x_parse_auto_config(codec);
1529}
1530
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001531static int patch_ad1983(struct hda_codec *codec)
1532{
1533 struct ad198x_spec *spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001534 int board_config;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001535 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001536
Takashi Iwai361dab32012-05-09 14:35:27 +02001537 err = alloc_ad_spec(codec);
1538 if (err < 0)
1539 return err;
1540 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001541
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001542 board_config = snd_hda_check_board_config(codec, AD1983_MODELS,
1543 ad1983_models, NULL);
1544 if (board_config == AD1983_AUTO) {
1545 err = ad1983_parse_auto_config(codec);
1546 if (err < 0) {
1547 ad198x_free(codec);
1548 return err;
1549 }
1550 return 0;
1551 }
1552
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001553 err = snd_hda_attach_beep_device(codec, 0x10);
1554 if (err < 0) {
1555 ad198x_free(codec);
1556 return err;
1557 }
1558 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1559
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001560 spec->multiout.max_channels = 2;
1561 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1562 spec->multiout.dac_nids = ad1983_dac_nids;
1563 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001564 spec->num_adc_nids = 1;
1565 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001566 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001567 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001568 spec->num_mixers = 1;
1569 spec->mixers[0] = ad1983_mixers;
1570 spec->num_init_verbs = 1;
1571 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001572 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02001573#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02001574 spec->loopback.amplist = ad1983_loopbacks;
1575#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001576 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001577
1578 codec->patch_ops = ad198x_patch_ops;
1579
Takashi Iwai729d55b2009-12-25 22:49:01 +01001580 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001581 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001582
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001583 return 0;
1584}
1585
1586
1587/*
1588 * AD1981 HD specific
1589 */
1590
1591#define AD1981_SPDIF_OUT 0x02
1592#define AD1981_DAC 0x03
1593#define AD1981_ADC 0x04
1594
Takashi Iwai498f5b12011-05-02 11:33:15 +02001595static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
1596static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
1597static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001598
1599/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001600static const struct hda_input_mux ad1981_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001601 .num_items = 7,
1602 .items = {
1603 { "Front Mic", 0x0 },
1604 { "Line", 0x1 },
1605 { "Mix", 0x2 },
1606 { "Mix Mono", 0x3 },
1607 { "CD", 0x4 },
1608 { "Mic", 0x6 },
1609 { "Aux", 0x7 },
1610 },
1611};
1612
Takashi Iwai498f5b12011-05-02 11:33:15 +02001613static const struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001614 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1615 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1616 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1617 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1618 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1619 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1620 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1621 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1622 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1623 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1624 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1625 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1626 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1627 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1628 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1629 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1630 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1631 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001632 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1633 HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001634 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1635 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1636 {
1637 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1638 .name = "Capture Source",
1639 .info = ad198x_mux_enum_info,
1640 .get = ad198x_mux_enum_get,
1641 .put = ad198x_mux_enum_put,
1642 },
1643 /* identical with AD1983 */
1644 {
1645 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001646 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001647 .info = ad1983_spdif_route_info,
1648 .get = ad1983_spdif_route_get,
1649 .put = ad1983_spdif_route_put,
1650 },
1651 { } /* end */
1652};
1653
Takashi Iwai498f5b12011-05-02 11:33:15 +02001654static const struct hda_verb ad1981_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001655 /* Front, HP, Mono; mute as default */
1656 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1657 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1658 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1659 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1660 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1661 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1662 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1663 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1664 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1665 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1666 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1667 /* Front, HP selectors; from Mix */
1668 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1669 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1670 /* Mono selector; from Mix */
1671 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1672 /* Mic Mixer; select Front Mic */
1673 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1674 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1675 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001676 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1677 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001678 /* Record selector: Front mic */
1679 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1680 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1681 /* SPDIF route: PCM */
1682 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1683 /* Front Pin */
1684 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1685 /* HP Pin */
1686 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1687 /* Mono Pin */
1688 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1689 /* Front & Rear Mic Pins */
1690 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1691 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1692 /* Line Pin */
1693 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1694 /* Digital Beep */
1695 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1696 /* Line-Out as Input: disabled */
1697 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1698 { } /* end */
1699};
1700
Takashi Iwai83012a72012-08-24 18:38:08 +02001701#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001702static const struct hda_amp_list ad1981_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001703 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1704 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1705 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1706 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1707 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1708 { } /* end */
1709};
1710#endif
1711
Takashi Iwai18a815d2006-03-01 19:54:39 +01001712/*
1713 * Patch for HP nx6320
1714 *
Tobin Davis18768992007-03-12 22:20:51 +01001715 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001716 * speaker output enabled _and_ mute-LED off.
1717 */
1718
1719#define AD1981_HP_EVENT 0x37
1720#define AD1981_MIC_EVENT 0x38
1721
Takashi Iwai498f5b12011-05-02 11:33:15 +02001722static const struct hda_verb ad1981_hp_init_verbs[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001723 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1724 /* pin sensing on HP and Mic jacks */
1725 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1726 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1727 {}
1728};
1729
1730/* turn on/off EAPD (+ mute HP) as a master switch */
1731static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1732 struct snd_ctl_elem_value *ucontrol)
1733{
1734 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1735 struct ad198x_spec *spec = codec->spec;
1736
1737 if (! ad198x_eapd_put(kcontrol, ucontrol))
1738 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001739 /* change speaker pin appropriately */
Takashi Iwaicdd03ce2012-04-20 12:34:50 +02001740 snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001741 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001742 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1743 HDA_AMP_MUTE,
1744 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001745 return 1;
1746}
1747
1748/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001749static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001750 .ops = &snd_hda_bind_vol,
1751 .values = {
1752 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1753 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1754 0
1755 },
1756};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001757
1758/* mute internal speaker if HP is plugged */
1759static void ad1981_hp_automute(struct hda_codec *codec)
1760{
1761 unsigned int present;
1762
Takashi Iwaid56757a2009-11-18 08:00:14 +01001763 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001764 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1765 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001766}
1767
1768/* toggle input of built-in and mic jack appropriately */
1769static void ad1981_hp_automic(struct hda_codec *codec)
1770{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001771 static const struct hda_verb mic_jack_on[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001772 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1773 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1774 {}
1775 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02001776 static const struct hda_verb mic_jack_off[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001777 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1778 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1779 {}
1780 };
1781 unsigned int present;
1782
Takashi Iwaid56757a2009-11-18 08:00:14 +01001783 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001784 if (present)
1785 snd_hda_sequence_write(codec, mic_jack_on);
1786 else
1787 snd_hda_sequence_write(codec, mic_jack_off);
1788}
1789
1790/* unsolicited event for HP jack sensing */
1791static void ad1981_hp_unsol_event(struct hda_codec *codec,
1792 unsigned int res)
1793{
1794 res >>= 26;
1795 switch (res) {
1796 case AD1981_HP_EVENT:
1797 ad1981_hp_automute(codec);
1798 break;
1799 case AD1981_MIC_EVENT:
1800 ad1981_hp_automic(codec);
1801 break;
1802 }
1803}
1804
Takashi Iwai498f5b12011-05-02 11:33:15 +02001805static const struct hda_input_mux ad1981_hp_capture_source = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001806 .num_items = 3,
1807 .items = {
1808 { "Mic", 0x0 },
David Henningssonc40bd912012-09-19 12:19:47 +02001809 { "Dock Mic", 0x1 },
Takashi Iwai18a815d2006-03-01 19:54:39 +01001810 { "Mix", 0x2 },
1811 },
1812};
1813
Takashi Iwai498f5b12011-05-02 11:33:15 +02001814static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001815 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001816 {
1817 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001818 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001819 .name = "Master Playback Switch",
1820 .info = ad198x_eapd_info,
1821 .get = ad198x_eapd_get,
1822 .put = ad1981_hp_master_sw_put,
1823 .private_value = 0x05,
1824 },
1825 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1826 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1827#if 0
1828 /* FIXME: analog mic/line loopback doesn't work with my tests...
1829 * (although recording is OK)
1830 */
1831 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1832 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
David Henningssonc40bd912012-09-19 12:19:47 +02001833 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1834 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001835 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1836 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1837 /* FIXME: does this laptop have analog CD connection? */
1838 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1839 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1840#endif
David Henningsson5f99f862011-01-04 15:24:24 +01001841 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1842 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001843 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1844 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1845 {
1846 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1847 .name = "Capture Source",
1848 .info = ad198x_mux_enum_info,
1849 .get = ad198x_mux_enum_get,
1850 .put = ad198x_mux_enum_put,
1851 },
1852 { } /* end */
1853};
1854
1855/* initialize jack-sensing, too */
1856static int ad1981_hp_init(struct hda_codec *codec)
1857{
1858 ad198x_init(codec);
1859 ad1981_hp_automute(codec);
1860 ad1981_hp_automic(codec);
1861 return 0;
1862}
1863
Tobin Davis18768992007-03-12 22:20:51 +01001864/* configuration for Toshiba Laptops */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001865static const struct hda_verb ad1981_toshiba_init_verbs[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001866 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1867 /* pin sensing on HP and Mic jacks */
1868 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1869 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1870 {}
1871};
1872
Takashi Iwai498f5b12011-05-02 11:33:15 +02001873static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001874 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1875 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1876 { }
1877};
1878
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001879/* configuration for Lenovo Thinkpad T60 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001880static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001881 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1882 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1883 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1884 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1885 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1886 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1887 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1888 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001889 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001890 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1891 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1892 {
1893 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1894 .name = "Capture Source",
1895 .info = ad198x_mux_enum_info,
1896 .get = ad198x_mux_enum_get,
1897 .put = ad198x_mux_enum_put,
1898 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001899 /* identical with AD1983 */
1900 {
1901 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1902 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1903 .info = ad1983_spdif_route_info,
1904 .get = ad1983_spdif_route_get,
1905 .put = ad1983_spdif_route_put,
1906 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001907 { } /* end */
1908};
1909
Takashi Iwai498f5b12011-05-02 11:33:15 +02001910static const struct hda_input_mux ad1981_thinkpad_capture_source = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001911 .num_items = 3,
1912 .items = {
1913 { "Mic", 0x0 },
1914 { "Mix", 0x2 },
1915 { "CD", 0x4 },
1916 },
1917};
1918
Takashi Iwai18a815d2006-03-01 19:54:39 +01001919/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001920enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001921 AD1981_AUTO,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001922 AD1981_BASIC,
1923 AD1981_HP,
1924 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001925 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001926 AD1981_MODELS
1927};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001928
Takashi Iwaiea734962011-01-17 11:29:34 +01001929static const char * const ad1981_models[AD1981_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001930 [AD1981_AUTO] = "auto",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001931 [AD1981_HP] = "hp",
1932 [AD1981_THINKPAD] = "thinkpad",
1933 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001934 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001935};
1936
Takashi Iwai498f5b12011-05-02 11:33:15 +02001937static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001938 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001939 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001940 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001941 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001942 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001943 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001944 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001945 /* HP nx6320 (reversed SSID, H/W bug) */
1946 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001947 {}
1948};
1949
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001950static int ad1981_parse_auto_config(struct hda_codec *codec)
1951{
1952 struct ad198x_spec *spec = codec->spec;
1953
Takashi Iwaif2f8be42013-01-21 16:40:16 +01001954 spec->gen.mixer_nid = 0x0e;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001955 spec->beep_dev_nid = 0x10;
1956 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1957 return ad198x_parse_auto_config(codec);
1958}
1959
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001960static int patch_ad1981(struct hda_codec *codec)
1961{
1962 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001963 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001964
Takashi Iwai361dab32012-05-09 14:35:27 +02001965 err = alloc_ad_spec(codec);
1966 if (err < 0)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001967 return -ENOMEM;
Takashi Iwai361dab32012-05-09 14:35:27 +02001968 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001969
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001970 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1971 ad1981_models,
1972 ad1981_cfg_tbl);
1973 if (board_config == AD1981_AUTO) {
1974 err = ad1981_parse_auto_config(codec);
1975 if (err < 0) {
1976 ad198x_free(codec);
1977 return err;
1978 }
1979 return 0;
1980 }
1981
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001982 err = snd_hda_attach_beep_device(codec, 0x10);
1983 if (err < 0) {
1984 ad198x_free(codec);
1985 return err;
1986 }
1987 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1988
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001989 spec->multiout.max_channels = 2;
1990 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1991 spec->multiout.dac_nids = ad1981_dac_nids;
1992 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001993 spec->num_adc_nids = 1;
1994 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001995 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001996 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001997 spec->num_mixers = 1;
1998 spec->mixers[0] = ad1981_mixers;
1999 spec->num_init_verbs = 1;
2000 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002001 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02002002#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02002003 spec->loopback.amplist = ad1981_loopbacks;
2004#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002005 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002006
2007 codec->patch_ops = ad198x_patch_ops;
2008
Takashi Iwai18a815d2006-03-01 19:54:39 +01002009 /* override some parameters */
Takashi Iwai18a815d2006-03-01 19:54:39 +01002010 switch (board_config) {
2011 case AD1981_HP:
2012 spec->mixers[0] = ad1981_hp_mixers;
2013 spec->num_init_verbs = 2;
2014 spec->init_verbs[1] = ad1981_hp_init_verbs;
Takashi Iwai695cd4a2011-06-10 14:37:04 +02002015 if (!is_jack_available(codec, 0x0a))
2016 spec->multiout.dig_out_nid = 0;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002017 spec->input_mux = &ad1981_hp_capture_source;
2018
2019 codec->patch_ops.init = ad1981_hp_init;
2020 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05002021 /* set the upper-limit for mixer amp to 0dB for avoiding the
2022 * possible damage by overloading
2023 */
2024 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2025 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2026 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2027 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2028 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01002029 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002030 case AD1981_THINKPAD:
2031 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002032 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04002033 /* set the upper-limit for mixer amp to 0dB for avoiding the
2034 * possible damage by overloading
2035 */
2036 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2037 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2038 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2039 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2040 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002041 break;
Tobin Davis18768992007-03-12 22:20:51 +01002042 case AD1981_TOSHIBA:
2043 spec->mixers[0] = ad1981_hp_mixers;
2044 spec->mixers[1] = ad1981_toshiba_mixers;
2045 spec->num_init_verbs = 2;
2046 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
2047 spec->multiout.dig_out_nid = 0;
2048 spec->input_mux = &ad1981_hp_capture_source;
2049 codec->patch_ops.init = ad1981_hp_init;
2050 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
2051 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002052 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01002053
2054 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002055 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002056
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002057 return 0;
2058}
2059
2060
2061/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002062 * AD1988
2063 *
2064 * Output pins and routes
2065 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002066 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002067 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
2068 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
2069 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
2070 * port-D 0x12 (mute/hp) <- 0x29 <- 04
2071 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
2072 * port-F 0x16 (mute) <- 0x2a <- 06
2073 * port-G 0x24 (mute) <- 0x27 <- 05
2074 * port-H 0x25 (mute) <- 0x28 <- 0a
2075 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
2076 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002077 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
2078 * (*) 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 +01002079 *
2080 * Input pins and routes
2081 *
2082 * pin boost mix input # / adc input #
2083 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
2084 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
2085 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
2086 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
2087 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
2088 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
2089 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
2090 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
2091 *
2092 *
2093 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01002094 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002095 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002096 *
2097 * Inputs of Analog Mix (0x20)
2098 * 0:Port-B (front mic)
2099 * 1:Port-C/G/H (line-in)
2100 * 2:Port-A
2101 * 3:Port-D (line-in/2)
2102 * 4:Port-E/G/H (mic-in)
2103 * 5:Port-F (mic2-in)
2104 * 6:CD
2105 * 7:Beep
2106 *
2107 * ADC selection
2108 * 0:Port-A
2109 * 1:Port-B (front mic-in)
2110 * 2:Port-C (line-in)
2111 * 3:Port-F (mic2-in)
2112 * 4:Port-E (mic-in)
2113 * 5:CD
2114 * 6:Port-G
2115 * 7:Port-H
2116 * 8:Port-D (line-in/2)
2117 * 9:Mix
2118 *
2119 * Proposed pin assignments by the datasheet
2120 *
2121 * 6-stack
2122 * Port-A front headphone
2123 * B front mic-in
2124 * C rear line-in
2125 * D rear front-out
2126 * E rear mic-in
2127 * F rear surround
2128 * G rear CLFE
2129 * H rear side
2130 *
2131 * 3-stack
2132 * Port-A front headphone
2133 * B front mic
2134 * C rear line-in/surround
2135 * D rear front-out
2136 * E rear mic-in/CLFE
2137 *
2138 * laptop
2139 * Port-A headphone
2140 * B mic-in
2141 * C docking station
2142 * D internal speaker (with EAPD)
2143 * E/F quad mic array
2144 */
2145
2146
2147/* models */
2148enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002149 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002150 AD1988_6STACK,
2151 AD1988_6STACK_DIG,
2152 AD1988_3STACK,
2153 AD1988_3STACK_DIG,
2154 AD1988_LAPTOP,
2155 AD1988_LAPTOP_DIG,
2156 AD1988_MODEL_LAST,
2157};
2158
Takashi Iwaid32410b12005-11-24 16:06:23 +01002159/* reivision id to check workarounds */
2160#define AD1988A_REV2 0x100200
2161
Takashi Iwai1a806f42006-07-03 15:58:16 +02002162#define is_rev2(codec) \
2163 ((codec)->vendor_id == 0x11d41988 && \
2164 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002165
2166/*
2167 * mixers
2168 */
2169
Takashi Iwai498f5b12011-05-02 11:33:15 +02002170static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002171 0x04, 0x06, 0x05, 0x0a
2172};
2173
Takashi Iwai498f5b12011-05-02 11:33:15 +02002174static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002175 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002176};
2177
2178/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002179static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002180 0x04, 0x05, 0x0a, 0x06
2181};
2182
Takashi Iwai498f5b12011-05-02 11:33:15 +02002183static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002184 0x03
2185};
2186
Takashi Iwai498f5b12011-05-02 11:33:15 +02002187static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002188 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002189};
2190
Takashi Iwai498f5b12011-05-02 11:33:15 +02002191static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002192 0x08, 0x09, 0x0f
2193};
2194
Takashi Iwai498f5b12011-05-02 11:33:15 +02002195static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002196 0x0c, 0x0d, 0x0e
2197};
2198
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002199#define AD1988_SPDIF_OUT 0x02
2200#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002201#define AD1988_SPDIF_IN 0x07
2202
Takashi Iwai498f5b12011-05-02 11:33:15 +02002203static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01002204 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002205};
2206
Takashi Iwai498f5b12011-05-02 11:33:15 +02002207static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002208 .num_items = 5,
2209 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002210 { "Front Mic", 0x1 }, /* port-B */
2211 { "Line", 0x2 }, /* port-C */
2212 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002213 { "CD", 0x5 },
2214 { "Mix", 0x9 },
2215 },
2216};
2217
Takashi Iwai498f5b12011-05-02 11:33:15 +02002218static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002219 .num_items = 3,
2220 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002221 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002222 { "CD", 0x5 },
2223 { "Mix", 0x9 },
2224 },
2225};
2226
2227/*
2228 */
2229static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2230 struct snd_ctl_elem_info *uinfo)
2231{
2232 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2233 struct ad198x_spec *spec = codec->spec;
2234 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2235 spec->num_channel_mode);
2236}
2237
2238static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2239 struct snd_ctl_elem_value *ucontrol)
2240{
2241 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2242 struct ad198x_spec *spec = codec->spec;
2243 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2244 spec->num_channel_mode, spec->multiout.max_channels);
2245}
2246
2247static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2248 struct snd_ctl_elem_value *ucontrol)
2249{
2250 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2251 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002252 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2253 spec->num_channel_mode,
2254 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002255 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002256 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002257 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002258}
2259
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002260/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002261static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002262 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2263 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2264 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2265 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2266 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002267 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002268};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002269
Takashi Iwai498f5b12011-05-02 11:33:15 +02002270static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002271 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2272 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2273 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2274 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2275 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002276 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002277};
2278
Takashi Iwai498f5b12011-05-02 11:33:15 +02002279static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002280 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002281 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2282 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2283 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2284 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2285 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2286 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2287 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2288
2289 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2290 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2291 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2292 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2293 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2294 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2295 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2296 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2297
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002298 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002299 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2300
David Henningsson5f99f862011-01-04 15:24:24 +01002301 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2302 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002303 { } /* end */
2304};
2305
2306/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002307static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002308 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002309 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002310 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2311 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002312 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002313};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002314
Takashi Iwai498f5b12011-05-02 11:33:15 +02002315static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002316 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002317 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2318 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2319 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002320 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002321};
2322
Takashi Iwai498f5b12011-05-02 11:33:15 +02002323static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002324 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002325 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002326 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2327 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2328 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002329 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2330 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2331
2332 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2333 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2334 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2335 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2336 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2337 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2338 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2339 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2340
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002341 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002342 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2343
David Henningsson5f99f862011-01-04 15:24:24 +01002344 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2345 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002346 {
2347 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2348 .name = "Channel Mode",
2349 .info = ad198x_ch_mode_info,
2350 .get = ad198x_ch_mode_get,
2351 .put = ad198x_ch_mode_put,
2352 },
2353
2354 { } /* end */
2355};
2356
2357/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002358static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002359 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002360 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2361 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2362 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2363
2364 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2365 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2366 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2367 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2368 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2369 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2370
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002371 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002372 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2373
David Henningsson5f99f862011-01-04 15:24:24 +01002374 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002375
2376 {
2377 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2378 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002379 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002380 .info = ad198x_eapd_info,
2381 .get = ad198x_eapd_get,
2382 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002383 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002384 },
2385
2386 { } /* end */
2387};
2388
2389/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002390static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002391 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2392 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2393 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2394 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2395 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2396 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2397 {
2398 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2399 /* The multiple "Capture Source" controls confuse alsamixer
2400 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002401 */
2402 /* .name = "Capture Source", */
2403 .name = "Input Source",
2404 .count = 3,
2405 .info = ad198x_mux_enum_info,
2406 .get = ad198x_mux_enum_get,
2407 .put = ad198x_mux_enum_put,
2408 },
2409 { } /* end */
2410};
2411
2412static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2413 struct snd_ctl_elem_info *uinfo)
2414{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002415 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002416 "PCM", "ADC1", "ADC2", "ADC3"
2417 };
2418 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2419 uinfo->count = 1;
2420 uinfo->value.enumerated.items = 4;
2421 if (uinfo->value.enumerated.item >= 4)
2422 uinfo->value.enumerated.item = 3;
2423 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2424 return 0;
2425}
2426
2427static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2428 struct snd_ctl_elem_value *ucontrol)
2429{
2430 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2431 unsigned int sel;
2432
Takashi Iwaibddcf542007-07-24 18:04:05 +02002433 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2434 AC_AMP_GET_INPUT);
2435 if (!(sel & 0x80))
2436 ucontrol->value.enumerated.item[0] = 0;
2437 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002438 sel = snd_hda_codec_read(codec, 0x0b, 0,
2439 AC_VERB_GET_CONNECT_SEL, 0);
2440 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002441 sel++;
2442 else
2443 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002444 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002445 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002446 return 0;
2447}
2448
2449static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2450 struct snd_ctl_elem_value *ucontrol)
2451{
2452 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002453 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002454 int change;
2455
Takashi Iwai35b26722007-05-05 12:17:17 +02002456 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002457 if (val > 3)
2458 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002459 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002460 sel = snd_hda_codec_read(codec, 0x1d, 0,
2461 AC_VERB_GET_AMP_GAIN_MUTE,
2462 AC_AMP_GET_INPUT);
2463 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002464 if (change) {
2465 snd_hda_codec_write_cache(codec, 0x1d, 0,
2466 AC_VERB_SET_AMP_GAIN_MUTE,
2467 AMP_IN_UNMUTE(0));
2468 snd_hda_codec_write_cache(codec, 0x1d, 0,
2469 AC_VERB_SET_AMP_GAIN_MUTE,
2470 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002471 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002472 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002473 sel = snd_hda_codec_read(codec, 0x1d, 0,
2474 AC_VERB_GET_AMP_GAIN_MUTE,
2475 AC_AMP_GET_INPUT | 0x01);
2476 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002477 if (change) {
2478 snd_hda_codec_write_cache(codec, 0x1d, 0,
2479 AC_VERB_SET_AMP_GAIN_MUTE,
2480 AMP_IN_MUTE(0));
2481 snd_hda_codec_write_cache(codec, 0x1d, 0,
2482 AC_VERB_SET_AMP_GAIN_MUTE,
2483 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002484 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002485 sel = snd_hda_codec_read(codec, 0x0b, 0,
2486 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2487 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002488 if (change)
2489 snd_hda_codec_write_cache(codec, 0x0b, 0,
2490 AC_VERB_SET_CONNECT_SEL,
2491 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002492 }
2493 return change;
2494}
2495
Takashi Iwai498f5b12011-05-02 11:33:15 +02002496static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002497 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2498 {
2499 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2500 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002501 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002502 .info = ad1988_spdif_playback_source_info,
2503 .get = ad1988_spdif_playback_source_get,
2504 .put = ad1988_spdif_playback_source_put,
2505 },
2506 { } /* end */
2507};
2508
Takashi Iwai498f5b12011-05-02 11:33:15 +02002509static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002510 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2511 { } /* end */
2512};
2513
Takashi Iwai498f5b12011-05-02 11:33:15 +02002514static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002515 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002516 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002517 { } /* end */
2518};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002519
2520/*
2521 * initialization verbs
2522 */
2523
2524/*
2525 * for 6-stack (+dig)
2526 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002527static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002528 /* Front, Surround, CLFE, side DAC; unmute as default */
2529 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2530 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2531 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2532 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002533 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002534 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002535 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2536 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2537 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2538 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2539 /* Port-D line-out path */
2540 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2541 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2542 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2543 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2544 /* Port-F surround path */
2545 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2546 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2547 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2548 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2549 /* Port-G CLFE path */
2550 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2551 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2552 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2553 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2554 /* Port-H side path */
2555 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2556 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2557 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2558 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2559 /* Mono out path */
2560 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2561 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2562 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2563 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2564 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2565 /* Port-B front mic-in path */
2566 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2567 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2568 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2569 /* Port-C line-in path */
2570 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2571 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2572 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2573 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2574 /* Port-E mic-in path */
2575 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2576 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2577 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2578 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002579 /* Analog CD Input */
2580 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002581 /* Analog Mix output amp */
2582 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002583
2584 { }
2585};
2586
Takashi Iwai498f5b12011-05-02 11:33:15 +02002587static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002588 /* Headphone; unmute as default */
2589 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2590 /* Port-A front headphon path */
2591 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2592 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2593 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2594 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2595 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002596
2597 { }
2598};
2599
Takashi Iwai498f5b12011-05-02 11:33:15 +02002600static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002601 /* mute analog mix */
2602 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2603 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2604 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2605 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2606 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2607 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2608 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2609 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2610 /* select ADCs - front-mic */
2611 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2612 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2613 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002614
2615 { }
2616};
2617
Takashi Iwai498f5b12011-05-02 11:33:15 +02002618static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002619 /* SPDIF out sel */
2620 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2621 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2622 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002623 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002624 /* SPDIF out pin */
2625 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002626
2627 { }
2628};
2629
Takashi Iwai498f5b12011-05-02 11:33:15 +02002630static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002631 /* unmute SPDIF input pin */
2632 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2633 { }
2634};
2635
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002636/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002637static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002638 /* SPDIF-1 out pin */
2639 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002640 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002641 /* SPDIF-2/HDMI out pin */
2642 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2643 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002644 { }
2645};
2646
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002647/*
2648 * verbs for 3stack (+dig)
2649 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002650static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002651 /* set port-C to line-in */
2652 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2653 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2654 /* set port-E to mic-in */
2655 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2656 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2657 { } /* end */
2658};
2659
Takashi Iwai498f5b12011-05-02 11:33:15 +02002660static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002661 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002662 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002663 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002664 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002665 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002666 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002667 { } /* end */
2668};
2669
Takashi Iwai498f5b12011-05-02 11:33:15 +02002670static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002671 { 2, ad1988_3stack_ch2_init },
2672 { 6, ad1988_3stack_ch6_init },
2673};
2674
Takashi Iwai498f5b12011-05-02 11:33:15 +02002675static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002676 /* Front, Surround, CLFE, side DAC; unmute as default */
2677 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2678 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2679 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2680 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002681 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002682 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002683 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2684 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2685 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2686 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2687 /* Port-D line-out path */
2688 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2689 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2690 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2691 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2692 /* Mono out path */
2693 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2694 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2695 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2696 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2697 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2698 /* Port-B front mic-in path */
2699 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2700 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2701 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002702 /* Port-C line-in/surround path - 6ch mode as default */
2703 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2704 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002705 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002706 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002707 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002708 /* Port-E mic-in/CLFE path - 6ch mode as default */
2709 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2710 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002711 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002712 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002713 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2714 /* mute analog mix */
2715 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2716 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2717 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2718 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2719 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2720 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2721 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2722 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2723 /* select ADCs - front-mic */
2724 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2725 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2726 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002727 /* Analog Mix output amp */
2728 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002729 { }
2730};
2731
2732/*
2733 * verbs for laptop mode (+dig)
2734 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002735static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002736 /* unmute port-A and mute port-D */
2737 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2738 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2739 { } /* end */
2740};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002741static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002742 /* mute port-A and unmute port-D */
2743 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2744 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2745 { } /* end */
2746};
2747
2748#define AD1988_HP_EVENT 0x01
2749
Takashi Iwai498f5b12011-05-02 11:33:15 +02002750static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002751 /* Front, Surround, CLFE, side DAC; unmute as default */
2752 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2753 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2754 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2755 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002756 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002757 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002758 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2759 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2760 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2761 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2762 /* unsolicited event for pin-sense */
2763 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2764 /* Port-D line-out path + EAPD */
2765 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2766 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2767 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2768 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2769 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2770 /* Mono out path */
2771 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2772 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2773 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2774 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2775 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2776 /* Port-B mic-in path */
2777 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2778 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2779 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2780 /* Port-C docking station - try to output */
2781 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2782 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2783 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2784 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2785 /* mute analog mix */
2786 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2787 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2788 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2789 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2790 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2791 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2792 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2793 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2794 /* select ADCs - mic */
2795 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2796 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2797 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002798 /* Analog Mix output amp */
2799 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002800 { }
2801};
2802
2803static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2804{
2805 if ((res >> 26) != AD1988_HP_EVENT)
2806 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002807 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002808 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2809 else
2810 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2811}
2812
Takashi Iwai83012a72012-08-24 18:38:08 +02002813#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02002814static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002815 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2816 { 0x20, HDA_INPUT, 1 }, /* Line */
2817 { 0x20, HDA_INPUT, 4 }, /* Mic */
2818 { 0x20, HDA_INPUT, 6 }, /* CD */
2819 { } /* end */
2820};
2821#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002822
2823/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002824 */
2825
Takashi Iwaid32410b12005-11-24 16:06:23 +01002826static int ad1988_parse_auto_config(struct hda_codec *codec)
2827{
2828 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002829
Takashi Iwaif2f8be42013-01-21 16:40:16 +01002830 spec->gen.mixer_nid = 0x20;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002831 spec->beep_dev_nid = 0x10;
2832 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
2833 return ad198x_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002834}
2835
Takashi Iwaid32410b12005-11-24 16:06:23 +01002836/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002837 */
2838
Takashi Iwaiea734962011-01-17 11:29:34 +01002839static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002840 [AD1988_6STACK] = "6stack",
2841 [AD1988_6STACK_DIG] = "6stack-dig",
2842 [AD1988_3STACK] = "3stack",
2843 [AD1988_3STACK_DIG] = "3stack-dig",
2844 [AD1988_LAPTOP] = "laptop",
2845 [AD1988_LAPTOP_DIG] = "laptop-dig",
2846 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002847};
2848
Takashi Iwai498f5b12011-05-02 11:33:15 +02002849static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002850 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002851 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002852 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01002853 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002854 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002855 {}
2856};
2857
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002858static int patch_ad1988(struct hda_codec *codec)
2859{
2860 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002861 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002862
Takashi Iwai361dab32012-05-09 14:35:27 +02002863 err = alloc_ad_spec(codec);
2864 if (err < 0)
2865 return err;
2866 spec = codec->spec;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002867
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002868 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002869 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002870 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02002871 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
2872 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002873 board_config = AD1988_AUTO;
2874 }
2875
2876 if (board_config == AD1988_AUTO) {
2877 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002878 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002879 if (err < 0) {
2880 ad198x_free(codec);
2881 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002882 }
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002883 return 0;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002884 }
2885
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002886 if (is_rev2(codec))
2887 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2888
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002889 err = snd_hda_attach_beep_device(codec, 0x10);
2890 if (err < 0) {
2891 ad198x_free(codec);
2892 return err;
2893 }
2894 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
2895
Raymond Yau356aab72011-08-31 10:30:59 +08002896 if (!spec->multiout.hp_nid)
Raymond Yau34588702011-09-23 19:03:25 +08002897 spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002898 switch (board_config) {
2899 case AD1988_6STACK:
2900 case AD1988_6STACK_DIG:
2901 spec->multiout.max_channels = 8;
2902 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002903 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002904 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
2905 else
2906 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002907 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002908 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002909 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002910 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
2911 else
2912 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08002913 spec->mixers[1] = ad1988_6stack_mixers2;
2914 spec->num_init_verbs = 1;
2915 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yau34588702011-09-23 19:03:25 +08002916 if (board_config == AD1988_6STACK_DIG) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002917 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2918 spec->dig_in_nid = AD1988_SPDIF_IN;
2919 }
2920 break;
2921 case AD1988_3STACK:
2922 case AD1988_3STACK_DIG:
2923 spec->multiout.max_channels = 6;
2924 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002925 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002926 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
2927 else
2928 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002929 spec->input_mux = &ad1988_6stack_capture_source;
2930 spec->channel_mode = ad1988_3stack_modes;
2931 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002932 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002933 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002934 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
2935 else
2936 spec->mixers[0] = ad1988_3stack_mixers1;
2937 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002938 spec->num_init_verbs = 1;
2939 spec->init_verbs[0] = ad1988_3stack_init_verbs;
2940 if (board_config == AD1988_3STACK_DIG)
2941 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2942 break;
2943 case AD1988_LAPTOP:
2944 case AD1988_LAPTOP_DIG:
2945 spec->multiout.max_channels = 2;
2946 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002947 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002948 spec->input_mux = &ad1988_laptop_capture_source;
2949 spec->num_mixers = 1;
2950 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002951 codec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002952 spec->num_init_verbs = 1;
2953 spec->init_verbs[0] = ad1988_laptop_init_verbs;
2954 if (board_config == AD1988_LAPTOP_DIG)
2955 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2956 break;
2957 }
2958
Takashi Iwaid32410b12005-11-24 16:06:23 +01002959 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
2960 spec->adc_nids = ad1988_adc_nids;
2961 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002962 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
2963 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
2964 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002965 if (codec->vendor_id >= 0x11d4989a) {
2966 spec->mixers[spec->num_mixers++] =
2967 ad1989_spdif_out_mixers;
2968 spec->init_verbs[spec->num_init_verbs++] =
2969 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002970 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002971 } else {
2972 spec->mixers[spec->num_mixers++] =
2973 ad1988_spdif_out_mixers;
2974 spec->init_verbs[spec->num_init_verbs++] =
2975 ad1988_spdif_init_verbs;
2976 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002977 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002978 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002979 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002980 spec->init_verbs[spec->num_init_verbs++] =
2981 ad1988_spdif_in_init_verbs;
2982 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002983
2984 codec->patch_ops = ad198x_patch_ops;
2985 switch (board_config) {
2986 case AD1988_LAPTOP:
2987 case AD1988_LAPTOP_DIG:
2988 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
2989 break;
2990 }
Takashi Iwai83012a72012-08-24 18:38:08 +02002991#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02002992 spec->loopback.amplist = ad1988_loopbacks;
2993#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002994 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002995
Takashi Iwai729d55b2009-12-25 22:49:01 +01002996 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002997 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002998
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002999 return 0;
3000}
3001
3002
3003/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003004 * AD1884 / AD1984
3005 *
3006 * port-B - front line/mic-in
3007 * port-E - aux in/out
3008 * port-F - aux in/out
3009 * port-C - rear line/mic-in
3010 * port-D - rear line/hp-out
3011 * port-A - front line/hp-out
3012 *
3013 * AD1984 = AD1884 + two digital mic-ins
3014 *
3015 * FIXME:
3016 * For simplicity, we share the single DAC for both HP and line-outs
3017 * right now. The inidividual playbacks could be easily implemented,
3018 * but no build-up framework is given, so far.
3019 */
3020
Takashi Iwai498f5b12011-05-02 11:33:15 +02003021static const hda_nid_t ad1884_dac_nids[1] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003022 0x04,
3023};
3024
Takashi Iwai498f5b12011-05-02 11:33:15 +02003025static const hda_nid_t ad1884_adc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003026 0x08, 0x09,
3027};
3028
Takashi Iwai498f5b12011-05-02 11:33:15 +02003029static const hda_nid_t ad1884_capsrc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003030 0x0c, 0x0d,
3031};
3032
3033#define AD1884_SPDIF_OUT 0x02
3034
Takashi Iwai498f5b12011-05-02 11:33:15 +02003035static const struct hda_input_mux ad1884_capture_source = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003036 .num_items = 4,
3037 .items = {
3038 { "Front Mic", 0x0 },
3039 { "Mic", 0x1 },
3040 { "CD", 0x2 },
3041 { "Mix", 0x3 },
3042 },
3043};
3044
Takashi Iwai498f5b12011-05-02 11:33:15 +02003045static const struct snd_kcontrol_new ad1884_base_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003046 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3047 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3048 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3049 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3050 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3051 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3052 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3053 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3054 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3055 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3056 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3057 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003058 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3059 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003060 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3061 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3062 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3063 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3064 {
3065 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3066 /* The multiple "Capture Source" controls confuse alsamixer
3067 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003068 */
3069 /* .name = "Capture Source", */
3070 .name = "Input Source",
3071 .count = 2,
3072 .info = ad198x_mux_enum_info,
3073 .get = ad198x_mux_enum_get,
3074 .put = ad198x_mux_enum_put,
3075 },
3076 /* SPDIF controls */
3077 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3078 {
3079 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3080 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3081 /* identical with ad1983 */
3082 .info = ad1983_spdif_route_info,
3083 .get = ad1983_spdif_route_get,
3084 .put = ad1983_spdif_route_put,
3085 },
3086 { } /* end */
3087};
3088
Takashi Iwai498f5b12011-05-02 11:33:15 +02003089static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003090 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3091 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3092 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003093 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003094 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003095 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003096 { } /* end */
3097};
3098
3099/*
3100 * initialization verbs
3101 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003102static const struct hda_verb ad1884_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003103 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003104 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3105 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003106 /* Port-A (HP) mixer */
3107 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3108 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3109 /* Port-A pin */
3110 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3111 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3112 /* HP selector - select DAC2 */
3113 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3114 /* Port-D (Line-out) mixer */
3115 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3116 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3117 /* Port-D pin */
3118 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3119 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3120 /* Mono-out mixer */
3121 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3122 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3123 /* Mono-out pin */
3124 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3125 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3126 /* Mono selector */
3127 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3128 /* Port-B (front mic) pin */
3129 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003130 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003131 /* Port-C (rear mic) pin */
3132 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003133 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003134 /* Analog mixer; mute as default */
3135 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3136 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3137 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3138 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3139 /* Analog Mix output amp */
3140 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3141 /* SPDIF output selector */
3142 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3143 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3144 { } /* end */
3145};
3146
Takashi Iwai83012a72012-08-24 18:38:08 +02003147#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02003148static const struct hda_amp_list ad1884_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003149 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3150 { 0x20, HDA_INPUT, 1 }, /* Mic */
3151 { 0x20, HDA_INPUT, 2 }, /* CD */
3152 { 0x20, HDA_INPUT, 4 }, /* Docking */
3153 { } /* end */
3154};
3155#endif
3156
Takashi Iwaiea734962011-01-17 11:29:34 +01003157static const char * const ad1884_slave_vols[] = {
Takashi Iwai9322ca52012-02-03 14:28:01 +01003158 "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD",
David Henningssonc40bd912012-09-19 12:19:47 +02003159 "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003160 NULL
3161};
3162
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003163enum {
3164 AD1884_AUTO,
3165 AD1884_BASIC,
3166 AD1884_MODELS
3167};
3168
3169static const char * const ad1884_models[AD1884_MODELS] = {
3170 [AD1884_AUTO] = "auto",
3171 [AD1884_BASIC] = "basic",
3172};
3173
3174static int ad1884_parse_auto_config(struct hda_codec *codec)
3175{
3176 struct ad198x_spec *spec = codec->spec;
3177
Takashi Iwaif2f8be42013-01-21 16:40:16 +01003178 spec->gen.mixer_nid = 0x20;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003179 spec->beep_dev_nid = 0x10;
3180 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3181 return ad198x_parse_auto_config(codec);
3182}
3183
3184static int patch_ad1884_auto(struct hda_codec *codec)
3185{
3186 int err;
3187
3188 err = alloc_ad_spec(codec);
3189 if (err < 0)
3190 return err;
3191
3192 err = ad1884_parse_auto_config(codec);
3193 if (err < 0) {
3194 ad198x_free(codec);
3195 return err;
3196 }
3197 return 0;
3198}
3199
3200static int patch_ad1884_basic(struct hda_codec *codec)
Takashi Iwai2bac6472007-05-18 18:21:41 +02003201{
3202 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003203 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003204
Takashi Iwai361dab32012-05-09 14:35:27 +02003205 err = alloc_ad_spec(codec);
3206 if (err < 0)
3207 return err;
3208 spec = codec->spec;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003209
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003210 err = snd_hda_attach_beep_device(codec, 0x10);
3211 if (err < 0) {
3212 ad198x_free(codec);
3213 return err;
3214 }
3215 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3216
Takashi Iwai2bac6472007-05-18 18:21:41 +02003217 spec->multiout.max_channels = 2;
3218 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3219 spec->multiout.dac_nids = ad1884_dac_nids;
3220 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3221 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3222 spec->adc_nids = ad1884_adc_nids;
3223 spec->capsrc_nids = ad1884_capsrc_nids;
3224 spec->input_mux = &ad1884_capture_source;
3225 spec->num_mixers = 1;
3226 spec->mixers[0] = ad1884_base_mixers;
3227 spec->num_init_verbs = 1;
3228 spec->init_verbs[0] = ad1884_init_verbs;
3229 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02003230#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02003231 spec->loopback.amplist = ad1884_loopbacks;
3232#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003233 spec->vmaster_nid = 0x04;
3234 /* we need to cover all playback volumes */
3235 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai18478e82012-03-09 17:51:10 +01003236 /* slaves may contain input volumes, so we can't raise to 0dB blindly */
3237 spec->avoid_init_slave_vol = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003238
3239 codec->patch_ops = ad198x_patch_ops;
3240
Takashi Iwai729d55b2009-12-25 22:49:01 +01003241 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003242 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003243
Takashi Iwai2bac6472007-05-18 18:21:41 +02003244 return 0;
3245}
3246
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003247static int patch_ad1884(struct hda_codec *codec)
3248{
3249 int board_config;
3250
3251 board_config = snd_hda_check_board_config(codec, AD1884_MODELS,
3252 ad1884_models, NULL);
3253 if (board_config == AD1884_AUTO)
3254 return patch_ad1884_auto(codec);
3255 else
3256 return patch_ad1884_basic(codec);
3257}
3258
Takashi Iwai2bac6472007-05-18 18:21:41 +02003259/*
3260 * Lenovo Thinkpad T61/X61
3261 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003262static const struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003263 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003264 .items = {
3265 { "Mic", 0x0 },
3266 { "Internal Mic", 0x1 },
3267 { "Mix", 0x3 },
David Henningssonc40bd912012-09-19 12:19:47 +02003268 { "Dock Mic", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003269 },
3270};
3271
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003272
3273/*
3274 * Dell Precision T3400
3275 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003276static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003277 .num_items = 3,
3278 .items = {
3279 { "Front Mic", 0x0 },
3280 { "Line-In", 0x1 },
3281 { "Mix", 0x3 },
3282 },
3283};
3284
3285
Takashi Iwai498f5b12011-05-02 11:33:15 +02003286static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003287 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3288 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3289 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3290 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3291 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3292 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3293 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3294 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003295 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3296 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
David Henningssonc40bd912012-09-19 12:19:47 +02003297 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3298 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003299 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3300 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3301 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003302 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3303 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3304 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3305 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3306 {
3307 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3308 /* The multiple "Capture Source" controls confuse alsamixer
3309 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003310 */
3311 /* .name = "Capture Source", */
3312 .name = "Input Source",
3313 .count = 2,
3314 .info = ad198x_mux_enum_info,
3315 .get = ad198x_mux_enum_get,
3316 .put = ad198x_mux_enum_put,
3317 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003318 /* SPDIF controls */
3319 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3320 {
3321 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3322 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3323 /* identical with ad1983 */
3324 .info = ad1983_spdif_route_info,
3325 .get = ad1983_spdif_route_get,
3326 .put = ad1983_spdif_route_put,
3327 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003328 { } /* end */
3329};
3330
3331/* additional verbs */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003332static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003333 /* Port-E (docking station mic) pin */
3334 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3335 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3336 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003337 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003338 /* Analog PC Beeper - allow firmware/ACPI beeps */
3339 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003340 /* Analog mixer - docking mic; mute as default */
3341 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003342 /* enable EAPD bit */
3343 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003344 { } /* end */
3345};
3346
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003347/*
3348 * Dell Precision T3400
3349 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003350static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003351 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3352 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3353 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3354 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3355 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3356 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3357 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3358 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3359 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003360 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
3361 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003362 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3363 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3364 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3365 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3366 {
3367 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3368 /* The multiple "Capture Source" controls confuse alsamixer
3369 * So call somewhat different..
3370 */
3371 /* .name = "Capture Source", */
3372 .name = "Input Source",
3373 .count = 2,
3374 .info = ad198x_mux_enum_info,
3375 .get = ad198x_mux_enum_get,
3376 .put = ad198x_mux_enum_put,
3377 },
3378 { } /* end */
3379};
3380
Takashi Iwai2bac6472007-05-18 18:21:41 +02003381/* Digial MIC ADC NID 0x05 + 0x06 */
3382static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3383 struct hda_codec *codec,
3384 unsigned int stream_tag,
3385 unsigned int format,
3386 struct snd_pcm_substream *substream)
3387{
3388 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3389 stream_tag, 0, format);
3390 return 0;
3391}
3392
3393static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3394 struct hda_codec *codec,
3395 struct snd_pcm_substream *substream)
3396{
Takashi Iwai888afa12008-03-18 09:57:50 +01003397 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003398 return 0;
3399}
3400
Takashi Iwai498f5b12011-05-02 11:33:15 +02003401static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003402 .substreams = 2,
3403 .channels_min = 2,
3404 .channels_max = 2,
3405 .nid = 0x05,
3406 .ops = {
3407 .prepare = ad1984_pcm_dmic_prepare,
3408 .cleanup = ad1984_pcm_dmic_cleanup
3409 },
3410};
3411
3412static int ad1984_build_pcms(struct hda_codec *codec)
3413{
3414 struct ad198x_spec *spec = codec->spec;
3415 struct hda_pcm *info;
3416 int err;
3417
3418 err = ad198x_build_pcms(codec);
3419 if (err < 0)
3420 return err;
3421
3422 info = spec->pcm_rec + codec->num_pcms;
3423 codec->num_pcms++;
3424 info->name = "AD1984 Digital Mic";
3425 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3426 return 0;
3427}
3428
3429/* models */
3430enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003431 AD1984_AUTO,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003432 AD1984_BASIC,
3433 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003434 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003435 AD1984_MODELS
3436};
3437
Takashi Iwaiea734962011-01-17 11:29:34 +01003438static const char * const ad1984_models[AD1984_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003439 [AD1984_AUTO] = "auto",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003440 [AD1984_BASIC] = "basic",
3441 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003442 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003443};
3444
Takashi Iwai498f5b12011-05-02 11:33:15 +02003445static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003446 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003447 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003448 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003449 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003450 {}
3451};
3452
3453static int patch_ad1984(struct hda_codec *codec)
3454{
3455 struct ad198x_spec *spec;
3456 int board_config, err;
3457
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003458 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3459 ad1984_models, ad1984_cfg_tbl);
3460 if (board_config == AD1984_AUTO)
3461 return patch_ad1884_auto(codec);
3462
3463 err = patch_ad1884_basic(codec);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003464 if (err < 0)
3465 return err;
3466 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003467
Takashi Iwai2bac6472007-05-18 18:21:41 +02003468 switch (board_config) {
3469 case AD1984_BASIC:
3470 /* additional digital mics */
3471 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3472 codec->patch_ops.build_pcms = ad1984_build_pcms;
3473 break;
3474 case AD1984_THINKPAD:
Jerone Young68c18692010-08-03 01:46:44 -05003475 if (codec->subsystem_id == 0x17aa20fb) {
3476 /* Thinpad X300 does not have the ability to do SPDIF,
3477 or attach to docking station to use SPDIF */
3478 spec->multiout.dig_out_nid = 0;
3479 } else
3480 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003481 spec->input_mux = &ad1984_thinkpad_capture_source;
3482 spec->mixers[0] = ad1984_thinkpad_mixers;
3483 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003484 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003485 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003486 case AD1984_DELL_DESKTOP:
3487 spec->multiout.dig_out_nid = 0;
3488 spec->input_mux = &ad1984_dell_desktop_capture_source;
3489 spec->mixers[0] = ad1984_dell_desktop_mixers;
3490 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003491 }
3492 return 0;
3493}
3494
3495
3496/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003497 * AD1883 / AD1884A / AD1984A / AD1984B
3498 *
3499 * port-B (0x14) - front mic-in
3500 * port-E (0x1c) - rear mic-in
3501 * port-F (0x16) - CD / ext out
3502 * port-C (0x15) - rear line-in
3503 * port-D (0x12) - rear line-out
3504 * port-A (0x11) - front hp-out
3505 *
3506 * AD1984A = AD1884A + digital-mic
3507 * AD1883 = equivalent with AD1984A
3508 * AD1984B = AD1984A + extra SPDIF-out
3509 *
3510 * FIXME:
3511 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3512 */
3513
Takashi Iwai498f5b12011-05-02 11:33:15 +02003514static const hda_nid_t ad1884a_dac_nids[1] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003515 0x03,
3516};
3517
3518#define ad1884a_adc_nids ad1884_adc_nids
3519#define ad1884a_capsrc_nids ad1884_capsrc_nids
3520
3521#define AD1884A_SPDIF_OUT 0x02
3522
Takashi Iwai498f5b12011-05-02 11:33:15 +02003523static const struct hda_input_mux ad1884a_capture_source = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003524 .num_items = 5,
3525 .items = {
3526 { "Front Mic", 0x0 },
3527 { "Mic", 0x4 },
3528 { "Line", 0x1 },
3529 { "CD", 0x2 },
3530 { "Mix", 0x3 },
3531 },
3532};
3533
Takashi Iwai498f5b12011-05-02 11:33:15 +02003534static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003535 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3536 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3537 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3538 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3539 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3540 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3541 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3542 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3543 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3544 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3545 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3546 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3547 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3548 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3549 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3550 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003551 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3552 HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
3553 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003554 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3555 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3556 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3557 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3558 {
3559 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3560 /* The multiple "Capture Source" controls confuse alsamixer
3561 * So call somewhat different..
3562 */
3563 /* .name = "Capture Source", */
3564 .name = "Input Source",
3565 .count = 2,
3566 .info = ad198x_mux_enum_info,
3567 .get = ad198x_mux_enum_get,
3568 .put = ad198x_mux_enum_put,
3569 },
3570 /* SPDIF controls */
3571 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3572 {
3573 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3574 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3575 /* identical with ad1983 */
3576 .info = ad1983_spdif_route_info,
3577 .get = ad1983_spdif_route_get,
3578 .put = ad1983_spdif_route_put,
3579 },
3580 { } /* end */
3581};
3582
3583/*
3584 * initialization verbs
3585 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003586static const struct hda_verb ad1884a_init_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003587 /* DACs; unmute as default */
3588 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3589 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3590 /* Port-A (HP) mixer - route only from analog mixer */
3591 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3592 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3593 /* Port-A pin */
3594 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3595 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3596 /* Port-D (Line-out) mixer - route only from analog mixer */
3597 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3598 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3599 /* Port-D pin */
3600 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3601 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3602 /* Mono-out mixer - route only from analog mixer */
3603 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3604 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3605 /* Mono-out pin */
3606 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3607 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3608 /* Port-B (front mic) pin */
3609 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003610 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003611 /* Port-C (rear line-in) pin */
3612 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003613 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003614 /* Port-E (rear mic) pin */
3615 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3616 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3617 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3618 /* Port-F (CD) pin */
3619 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3620 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3621 /* Analog mixer; mute as default */
3622 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3623 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3624 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3625 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3626 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3627 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3628 /* Analog Mix output amp */
3629 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3630 /* capture sources */
3631 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3632 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3633 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3634 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3635 /* SPDIF output amp */
3636 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3637 { } /* end */
3638};
3639
Takashi Iwai83012a72012-08-24 18:38:08 +02003640#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02003641static const struct hda_amp_list ad1884a_loopbacks[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003642 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3643 { 0x20, HDA_INPUT, 1 }, /* Mic */
3644 { 0x20, HDA_INPUT, 2 }, /* CD */
3645 { 0x20, HDA_INPUT, 4 }, /* Docking */
3646 { } /* end */
3647};
3648#endif
3649
3650/*
3651 * Laptop model
3652 *
3653 * Port A: Headphone jack
3654 * Port B: MIC jack
3655 * Port C: Internal MIC
3656 * Port D: Dock Line Out (if enabled)
3657 * Port E: Dock Line In (if enabled)
3658 * Port F: Internal speakers
3659 */
3660
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003661static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3662 struct snd_ctl_elem_value *ucontrol)
3663{
3664 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3665 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3666 int mute = (!ucontrol->value.integer.value[0] &&
3667 !ucontrol->value.integer.value[1]);
3668 /* toggle GPIO1 according to the mute state */
3669 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3670 mute ? 0x02 : 0x0);
3671 return ret;
3672}
Takashi Iwaic5059252008-02-16 09:43:56 +01003673
Takashi Iwai498f5b12011-05-02 11:33:15 +02003674static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003675 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003676 {
3677 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3678 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003679 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003680 .info = snd_hda_mixer_amp_switch_info,
3681 .get = snd_hda_mixer_amp_switch_get,
3682 .put = ad1884a_mobile_master_sw_put,
3683 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3684 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003685 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3686 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3687 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3688 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3689 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3690 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3691 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3692 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3693 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003694 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3695 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3696 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003697 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3698 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003699 { } /* end */
3700};
3701
Takashi Iwai498f5b12011-05-02 11:33:15 +02003702static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003703 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003704 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3705 {
3706 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3707 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003708 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003709 .info = snd_hda_mixer_amp_switch_info,
3710 .get = snd_hda_mixer_amp_switch_get,
3711 .put = ad1884a_mobile_master_sw_put,
3712 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3713 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003714 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3715 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003716 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3717 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003718 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3719 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003720 { } /* end */
3721};
3722
Takashi Iwaic5059252008-02-16 09:43:56 +01003723/* mute internal speaker if HP is plugged */
3724static void ad1884a_hp_automute(struct hda_codec *codec)
3725{
3726 unsigned int present;
3727
Takashi Iwaid56757a2009-11-18 08:00:14 +01003728 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01003729 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3730 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3731 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3732 present ? 0x00 : 0x02);
3733}
3734
Takashi Iwai269ef192008-05-30 15:32:15 +02003735/* switch to external mic if plugged */
3736static void ad1884a_hp_automic(struct hda_codec *codec)
3737{
3738 unsigned int present;
3739
Takashi Iwaid56757a2009-11-18 08:00:14 +01003740 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02003741 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
3742 present ? 0 : 1);
3743}
3744
Takashi Iwaic5059252008-02-16 09:43:56 +01003745#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02003746#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01003747
3748/* unsolicited event for HP jack sensing */
3749static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
3750{
Takashi Iwai269ef192008-05-30 15:32:15 +02003751 switch (res >> 26) {
3752 case AD1884A_HP_EVENT:
3753 ad1884a_hp_automute(codec);
3754 break;
3755 case AD1884A_MIC_EVENT:
3756 ad1884a_hp_automic(codec);
3757 break;
3758 }
Takashi Iwaic5059252008-02-16 09:43:56 +01003759}
3760
3761/* initialize jack-sensing, too */
3762static int ad1884a_hp_init(struct hda_codec *codec)
3763{
3764 ad198x_init(codec);
3765 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02003766 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01003767 return 0;
3768}
3769
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003770/* mute internal speaker if HP or docking HP is plugged */
3771static void ad1884a_laptop_automute(struct hda_codec *codec)
3772{
3773 unsigned int present;
3774
Takashi Iwaid56757a2009-11-18 08:00:14 +01003775 present = snd_hda_jack_detect(codec, 0x11);
3776 if (!present)
3777 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003778 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
3779 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3780 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
3781 present ? 0x00 : 0x02);
3782}
3783
3784/* switch to external mic if plugged */
3785static void ad1884a_laptop_automic(struct hda_codec *codec)
3786{
3787 unsigned int idx;
3788
Takashi Iwaid56757a2009-11-18 08:00:14 +01003789 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003790 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003791 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003792 idx = 4;
3793 else
3794 idx = 1;
3795 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
3796}
3797
3798/* unsolicited event for HP jack sensing */
3799static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
3800 unsigned int res)
3801{
3802 switch (res >> 26) {
3803 case AD1884A_HP_EVENT:
3804 ad1884a_laptop_automute(codec);
3805 break;
3806 case AD1884A_MIC_EVENT:
3807 ad1884a_laptop_automic(codec);
3808 break;
3809 }
3810}
3811
3812/* initialize jack-sensing, too */
3813static int ad1884a_laptop_init(struct hda_codec *codec)
3814{
3815 ad198x_init(codec);
3816 ad1884a_laptop_automute(codec);
3817 ad1884a_laptop_automic(codec);
3818 return 0;
3819}
3820
Takashi Iwaic5059252008-02-16 09:43:56 +01003821/* additional verbs for laptop model */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003822static const struct hda_verb ad1884a_laptop_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003823 /* Port-A (HP) pin - always unmuted */
3824 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3825 /* Port-F (int speaker) mixer - route only from analog mixer */
3826 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3827 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08003828 /* Port-F (int speaker) pin */
3829 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01003830 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08003831 /* required for compaq 6530s/6531s speaker output */
3832 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003833 /* Port-C pin - internal mic-in */
3834 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3835 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3836 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02003837 /* Port-D (docking line-out) pin - default unmuted */
3838 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01003839 /* analog mix */
3840 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3841 /* unsolicited event for pin-sense */
3842 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003843 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02003844 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003845 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02003846 /* allow to touch GPIO1 (for mute control) */
3847 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3848 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3849 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01003850 { } /* end */
3851};
3852
Takashi Iwai498f5b12011-05-02 11:33:15 +02003853static const struct hda_verb ad1884a_mobile_verbs[] = {
Takashi Iwai73156132009-04-23 08:24:48 +02003854 /* DACs; unmute as default */
3855 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3856 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3857 /* Port-A (HP) mixer - route only from analog mixer */
3858 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3859 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3860 /* Port-A pin */
3861 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3862 /* Port-A (HP) pin - always unmuted */
3863 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3864 /* Port-B (mic jack) pin */
3865 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3866 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3867 /* Port-C (int mic) pin */
3868 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3869 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
3870 /* Port-F (int speaker) mixer - route only from analog mixer */
3871 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3872 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3873 /* Port-F pin */
3874 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3875 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3876 /* Analog mixer; mute as default */
3877 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3878 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3879 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3880 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3881 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3882 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3883 /* Analog Mix output amp */
3884 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3885 /* capture sources */
3886 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
3887 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3888 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3889 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3890 /* unsolicited event for pin-sense */
3891 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3892 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02003893 /* allow to touch GPIO1 (for mute control) */
3894 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3895 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3896 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02003897 { } /* end */
3898};
3899
Takashi Iwaic5059252008-02-16 09:43:56 +01003900/*
Takashi Iwaif0813742008-03-18 12:13:03 +01003901 * Thinkpad X300
3902 * 0x11 - HP
3903 * 0x12 - speaker
3904 * 0x14 - mic-in
3905 * 0x17 - built-in mic
3906 */
3907
Takashi Iwai498f5b12011-05-02 11:33:15 +02003908static const struct hda_verb ad1984a_thinkpad_verbs[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01003909 /* HP unmute */
3910 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3911 /* analog mix */
3912 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3913 /* turn on EAPD */
3914 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
3915 /* unsolicited event for pin-sense */
3916 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
3917 /* internal mic - dmic */
3918 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02003919 /* set magic COEFs for dmic */
3920 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
3921 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01003922 { } /* end */
3923};
3924
Takashi Iwai498f5b12011-05-02 11:33:15 +02003925static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01003926 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3927 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3928 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3929 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3930 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3931 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003932 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3933 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01003934 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3935 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3936 {
3937 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3938 .name = "Capture Source",
3939 .info = ad198x_mux_enum_info,
3940 .get = ad198x_mux_enum_get,
3941 .put = ad198x_mux_enum_put,
3942 },
3943 { } /* end */
3944};
3945
Takashi Iwai498f5b12011-05-02 11:33:15 +02003946static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
Takashi Iwaif0813742008-03-18 12:13:03 +01003947 .num_items = 3,
3948 .items = {
3949 { "Mic", 0x0 },
3950 { "Internal Mic", 0x5 },
3951 { "Mix", 0x3 },
3952 },
3953};
3954
3955/* mute internal speaker if HP is plugged */
3956static void ad1984a_thinkpad_automute(struct hda_codec *codec)
3957{
3958 unsigned int present;
3959
Takashi Iwaid56757a2009-11-18 08:00:14 +01003960 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01003961 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
3962 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
3963}
3964
3965/* unsolicited event for HP jack sensing */
3966static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
3967 unsigned int res)
3968{
3969 if ((res >> 26) != AD1884A_HP_EVENT)
3970 return;
3971 ad1984a_thinkpad_automute(codec);
3972}
3973
3974/* initialize jack-sensing, too */
3975static int ad1984a_thinkpad_init(struct hda_codec *codec)
3976{
3977 ad198x_init(codec);
3978 ad1984a_thinkpad_automute(codec);
3979 return 0;
3980}
3981
3982/*
David Henningsson677cd902011-02-07 15:19:34 +01003983 * Precision R5500
3984 * 0x12 - HP/line-out
3985 * 0x13 - speaker (mono)
3986 * 0x15 - mic-in
3987 */
3988
Takashi Iwai498f5b12011-05-02 11:33:15 +02003989static const struct hda_verb ad1984a_precision_verbs[] = {
David Henningsson677cd902011-02-07 15:19:34 +01003990 /* Unmute main output path */
3991 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3992 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
3993 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
3994 /* Analog mixer; mute as default */
3995 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3996 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3997 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3998 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3999 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4000 /* Select mic as input */
4001 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
4002 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
4003 /* Configure as mic */
4004 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4005 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4006 /* HP unmute */
4007 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4008 /* turn on EAPD */
4009 {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4010 /* unsolicited event for pin-sense */
4011 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4012 { } /* end */
4013};
4014
Takashi Iwai498f5b12011-05-02 11:33:15 +02004015static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004016 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4017 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4018 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4019 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4020 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4021 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4022 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4023 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4024 HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
4025 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4026 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4027 { } /* end */
4028};
4029
4030
4031/* mute internal speaker if HP is plugged */
4032static void ad1984a_precision_automute(struct hda_codec *codec)
4033{
4034 unsigned int present;
4035
4036 present = snd_hda_jack_detect(codec, 0x12);
4037 snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
4038 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4039}
4040
4041
4042/* unsolicited event for HP jack sensing */
4043static void ad1984a_precision_unsol_event(struct hda_codec *codec,
4044 unsigned int res)
4045{
4046 if ((res >> 26) != AD1884A_HP_EVENT)
4047 return;
4048 ad1984a_precision_automute(codec);
4049}
4050
4051/* initialize jack-sensing, too */
4052static int ad1984a_precision_init(struct hda_codec *codec)
4053{
4054 ad198x_init(codec);
4055 ad1984a_precision_automute(codec);
4056 return 0;
4057}
4058
4059
4060/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004061 * HP Touchsmart
4062 * port-A (0x11) - front hp-out
4063 * port-B (0x14) - unused
4064 * port-C (0x15) - unused
4065 * port-D (0x12) - rear line out
4066 * port-E (0x1c) - front mic-in
4067 * port-F (0x16) - Internal speakers
4068 * digital-mic (0x17) - Internal mic
4069 */
4070
Takashi Iwai498f5b12011-05-02 11:33:15 +02004071static const struct hda_verb ad1984a_touchsmart_verbs[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004072 /* DACs; unmute as default */
4073 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4074 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4075 /* Port-A (HP) mixer - route only from analog mixer */
4076 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4077 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4078 /* Port-A pin */
4079 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4080 /* Port-A (HP) pin - always unmuted */
4081 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4082 /* Port-E (int speaker) mixer - route only from analog mixer */
4083 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4084 /* Port-E pin */
4085 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4086 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4087 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4088 /* Port-F (int speaker) mixer - route only from analog mixer */
4089 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4090 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4091 /* Port-F pin */
4092 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4093 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4094 /* Analog mixer; mute as default */
4095 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4096 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4097 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4098 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4099 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4100 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4101 /* Analog Mix output amp */
4102 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4103 /* capture sources */
4104 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4105 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4106 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4107 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4108 /* unsolicited event for pin-sense */
4109 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4110 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4111 /* allow to touch GPIO1 (for mute control) */
4112 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4113 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4114 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4115 /* internal mic - dmic */
4116 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4117 /* set magic COEFs for dmic */
4118 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4119 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4120 { } /* end */
4121};
4122
Takashi Iwai498f5b12011-05-02 11:33:15 +02004123static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004124 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4125/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4126 {
4127 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004128 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004129 .name = "Master Playback Switch",
4130 .info = snd_hda_mixer_amp_switch_info,
4131 .get = snd_hda_mixer_amp_switch_get,
4132 .put = ad1884a_mobile_master_sw_put,
4133 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4134 },
4135 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4136 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4137 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4138 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004139 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
4140 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004141 { } /* end */
4142};
4143
4144/* switch to external mic if plugged */
4145static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4146{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004147 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004148 snd_hda_codec_write(codec, 0x0c, 0,
4149 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004150 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004151 snd_hda_codec_write(codec, 0x0c, 0,
4152 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004153}
4154
4155
4156/* unsolicited event for HP jack sensing */
4157static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4158 unsigned int res)
4159{
4160 switch (res >> 26) {
4161 case AD1884A_HP_EVENT:
4162 ad1884a_hp_automute(codec);
4163 break;
4164 case AD1884A_MIC_EVENT:
4165 ad1984a_touchsmart_automic(codec);
4166 break;
4167 }
4168}
4169
4170/* initialize jack-sensing, too */
4171static int ad1984a_touchsmart_init(struct hda_codec *codec)
4172{
4173 ad198x_init(codec);
4174 ad1884a_hp_automute(codec);
4175 ad1984a_touchsmart_automic(codec);
4176 return 0;
4177}
4178
4179
4180/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004181 */
4182
4183enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004184 AD1884A_AUTO,
Takashi Iwaic5059252008-02-16 09:43:56 +01004185 AD1884A_DESKTOP,
4186 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004187 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004188 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004189 AD1984A_TOUCHSMART,
David Henningsson677cd902011-02-07 15:19:34 +01004190 AD1984A_PRECISION,
Takashi Iwaic5059252008-02-16 09:43:56 +01004191 AD1884A_MODELS
4192};
4193
Takashi Iwaiea734962011-01-17 11:29:34 +01004194static const char * const ad1884a_models[AD1884A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004195 [AD1884A_AUTO] = "auto",
Takashi Iwaic5059252008-02-16 09:43:56 +01004196 [AD1884A_DESKTOP] = "desktop",
4197 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004198 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004199 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004200 [AD1984A_TOUCHSMART] = "touchsmart",
David Henningsson677cd902011-02-07 15:19:34 +01004201 [AD1984A_PRECISION] = "precision",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004202};
4203
Takashi Iwai498f5b12011-05-02 11:33:15 +02004204static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004205 SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004206 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004207 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004208 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004209 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004210 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004211 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4212 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004213 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004214 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004215 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004216 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004217};
4218
4219static int patch_ad1884a(struct hda_codec *codec)
4220{
4221 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004222 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004223
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004224 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
4225 ad1884a_models,
4226 ad1884a_cfg_tbl);
4227 if (board_config == AD1884_AUTO)
4228 return patch_ad1884_auto(codec);
4229
Takashi Iwai361dab32012-05-09 14:35:27 +02004230 err = alloc_ad_spec(codec);
4231 if (err < 0)
4232 return err;
4233 spec = codec->spec;
Takashi Iwaic5059252008-02-16 09:43:56 +01004234
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004235 err = snd_hda_attach_beep_device(codec, 0x10);
4236 if (err < 0) {
4237 ad198x_free(codec);
4238 return err;
4239 }
4240 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4241
Takashi Iwaic5059252008-02-16 09:43:56 +01004242 spec->multiout.max_channels = 2;
4243 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4244 spec->multiout.dac_nids = ad1884a_dac_nids;
4245 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4246 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4247 spec->adc_nids = ad1884a_adc_nids;
4248 spec->capsrc_nids = ad1884a_capsrc_nids;
4249 spec->input_mux = &ad1884a_capture_source;
4250 spec->num_mixers = 1;
4251 spec->mixers[0] = ad1884a_base_mixers;
4252 spec->num_init_verbs = 1;
4253 spec->init_verbs[0] = ad1884a_init_verbs;
4254 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02004255#ifdef CONFIG_PM
Takashi Iwaic5059252008-02-16 09:43:56 +01004256 spec->loopback.amplist = ad1884a_loopbacks;
4257#endif
4258 codec->patch_ops = ad198x_patch_ops;
4259
4260 /* override some parameters */
Takashi Iwaic5059252008-02-16 09:43:56 +01004261 switch (board_config) {
4262 case AD1884A_LAPTOP:
4263 spec->mixers[0] = ad1884a_laptop_mixers;
4264 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4265 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004266 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4267 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004268 /* set the upper-limit for mixer amp to 0dB for avoiding the
4269 * possible damage by overloading
4270 */
4271 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4272 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4273 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4274 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4275 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004276 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004277 case AD1884A_MOBILE:
4278 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004279 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004280 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004281 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4282 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004283 /* set the upper-limit for mixer amp to 0dB for avoiding the
4284 * possible damage by overloading
4285 */
4286 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4287 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4288 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4289 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4290 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004291 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004292 case AD1884A_THINKPAD:
4293 spec->mixers[0] = ad1984a_thinkpad_mixers;
4294 spec->init_verbs[spec->num_init_verbs++] =
4295 ad1984a_thinkpad_verbs;
4296 spec->multiout.dig_out_nid = 0;
4297 spec->input_mux = &ad1984a_thinkpad_capture_source;
4298 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4299 codec->patch_ops.init = ad1984a_thinkpad_init;
4300 break;
David Henningsson677cd902011-02-07 15:19:34 +01004301 case AD1984A_PRECISION:
4302 spec->mixers[0] = ad1984a_precision_mixers;
4303 spec->init_verbs[spec->num_init_verbs++] =
4304 ad1984a_precision_verbs;
4305 spec->multiout.dig_out_nid = 0;
4306 codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
4307 codec->patch_ops.init = ad1984a_precision_init;
4308 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004309 case AD1984A_TOUCHSMART:
4310 spec->mixers[0] = ad1984a_touchsmart_mixers;
4311 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4312 spec->multiout.dig_out_nid = 0;
4313 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4314 codec->patch_ops.init = ad1984a_touchsmart_init;
4315 /* set the upper-limit for mixer amp to 0dB for avoiding the
4316 * possible damage by overloading
4317 */
4318 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4319 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4320 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4321 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4322 (1 << AC_AMPCAP_MUTE_SHIFT));
4323 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004324 }
4325
Takashi Iwai729d55b2009-12-25 22:49:01 +01004326 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004327 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004328
Takashi Iwaic5059252008-02-16 09:43:56 +01004329 return 0;
4330}
4331
4332
4333/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004334 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004335 *
4336 * port-A - front hp-out
4337 * port-B - front mic-in
4338 * port-C - rear line-in, shared surr-out (3stack)
4339 * port-D - rear line-out
4340 * port-E - rear mic-in, shared clfe-out (3stack)
4341 * port-F - rear surr-out (6stack)
4342 * port-G - rear clfe-out (6stack)
4343 */
4344
Takashi Iwai498f5b12011-05-02 11:33:15 +02004345static const hda_nid_t ad1882_dac_nids[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004346 0x04, 0x03, 0x05
4347};
4348
Takashi Iwai498f5b12011-05-02 11:33:15 +02004349static const hda_nid_t ad1882_adc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004350 0x08, 0x09,
4351};
4352
Takashi Iwai498f5b12011-05-02 11:33:15 +02004353static const hda_nid_t ad1882_capsrc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004354 0x0c, 0x0d,
4355};
4356
4357#define AD1882_SPDIF_OUT 0x02
4358
4359/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004360static const struct hda_input_mux ad1882_capture_source = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004361 .num_items = 5,
4362 .items = {
4363 { "Front Mic", 0x1 },
4364 { "Mic", 0x4 },
4365 { "Line", 0x2 },
4366 { "CD", 0x3 },
4367 { "Mix", 0x7 },
4368 },
4369};
4370
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004371/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004372static const struct hda_input_mux ad1882a_capture_source = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004373 .num_items = 5,
4374 .items = {
4375 { "Front Mic", 0x1 },
4376 { "Mic", 0x4},
4377 { "Line", 0x2 },
4378 { "Digital Mic", 0x06 },
4379 { "Mix", 0x7 },
4380 },
4381};
4382
Takashi Iwai498f5b12011-05-02 11:33:15 +02004383static const struct snd_kcontrol_new ad1882_base_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004384 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4385 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4386 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4387 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4388 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4389 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4390 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4391 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004392
David Henningsson5f99f862011-01-04 15:24:24 +01004393 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
4394 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
4395 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
Takashi Iwai0ac85512007-06-20 15:46:13 +02004396 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4397 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4398 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4399 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4400 {
4401 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4402 /* The multiple "Capture Source" controls confuse alsamixer
4403 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004404 */
4405 /* .name = "Capture Source", */
4406 .name = "Input Source",
4407 .count = 2,
4408 .info = ad198x_mux_enum_info,
4409 .get = ad198x_mux_enum_get,
4410 .put = ad198x_mux_enum_put,
4411 },
4412 /* SPDIF controls */
4413 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4414 {
4415 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4416 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4417 /* identical with ad1983 */
4418 .info = ad1983_spdif_route_info,
4419 .get = ad1983_spdif_route_get,
4420 .put = ad1983_spdif_route_put,
4421 },
4422 { } /* end */
4423};
4424
Takashi Iwai498f5b12011-05-02 11:33:15 +02004425static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004426 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4427 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4428 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4429 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4430 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4431 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4432 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4433 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004434 { } /* end */
4435};
4436
Takashi Iwai498f5b12011-05-02 11:33:15 +02004437static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004438 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4439 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4440 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4441 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4442 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4443 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4444 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4445 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004446 HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004447 { } /* end */
4448};
4449
Takashi Iwai498f5b12011-05-02 11:33:15 +02004450static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004451 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4452 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4453 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4454 {
4455 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4456 .name = "Channel Mode",
4457 .info = ad198x_ch_mode_info,
4458 .get = ad198x_ch_mode_get,
4459 .put = ad198x_ch_mode_put,
4460 },
4461 { } /* end */
4462};
4463
Takashi Iwai1c868452012-08-13 11:09:35 +02004464/* simple auto-mute control for AD1882 3-stack board */
4465#define AD1882_HP_EVENT 0x01
4466
4467static void ad1882_3stack_automute(struct hda_codec *codec)
4468{
4469 bool mute = snd_hda_jack_detect(codec, 0x11);
4470 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
4471 mute ? 0 : PIN_OUT);
4472}
4473
4474static int ad1882_3stack_automute_init(struct hda_codec *codec)
4475{
4476 ad198x_init(codec);
4477 ad1882_3stack_automute(codec);
4478 return 0;
4479}
4480
4481static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res)
4482{
4483 switch (res >> 26) {
4484 case AD1882_HP_EVENT:
4485 ad1882_3stack_automute(codec);
4486 break;
4487 }
4488}
4489
Takashi Iwai498f5b12011-05-02 11:33:15 +02004490static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004491 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4492 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4493 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4494 { } /* end */
4495};
4496
Takashi Iwai498f5b12011-05-02 11:33:15 +02004497static const struct hda_verb ad1882_ch2_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004498 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4499 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4500 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4501 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4502 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4503 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4504 { } /* end */
4505};
4506
Takashi Iwai498f5b12011-05-02 11:33:15 +02004507static const struct hda_verb ad1882_ch4_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004508 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4509 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4510 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4511 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4512 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4513 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4514 { } /* end */
4515};
4516
Takashi Iwai498f5b12011-05-02 11:33:15 +02004517static const struct hda_verb ad1882_ch6_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004518 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4519 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4520 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4521 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4522 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4523 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4524 { } /* end */
4525};
4526
Takashi Iwai498f5b12011-05-02 11:33:15 +02004527static const struct hda_channel_mode ad1882_modes[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004528 { 2, ad1882_ch2_init },
4529 { 4, ad1882_ch4_init },
4530 { 6, ad1882_ch6_init },
4531};
4532
4533/*
4534 * initialization verbs
4535 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004536static const struct hda_verb ad1882_init_verbs[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004537 /* DACs; mute as default */
4538 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4539 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4540 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4541 /* Port-A (HP) mixer */
4542 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4543 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4544 /* Port-A pin */
4545 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4546 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4547 /* HP selector - select DAC2 */
4548 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4549 /* Port-D (Line-out) mixer */
4550 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4551 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4552 /* Port-D pin */
4553 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4554 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4555 /* Mono-out mixer */
4556 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4557 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4558 /* Mono-out pin */
4559 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4560 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4561 /* Port-B (front mic) pin */
4562 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4563 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4564 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4565 /* Port-C (line-in) pin */
4566 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4567 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4568 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4569 /* Port-C mixer - mute as input */
4570 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4571 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4572 /* Port-E (mic-in) pin */
4573 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4574 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4575 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4576 /* Port-E mixer - mute as input */
4577 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4578 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4579 /* Port-F (surround) */
4580 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4581 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4582 /* Port-G (CLFE) */
4583 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4584 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4585 /* Analog mixer; mute as default */
4586 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4587 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4588 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4589 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4590 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4591 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4592 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4593 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4594 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4595 /* Analog Mix output amp */
4596 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4597 /* SPDIF output selector */
4598 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4599 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4600 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4601 { } /* end */
4602};
4603
Takashi Iwai1c868452012-08-13 11:09:35 +02004604static const struct hda_verb ad1882_3stack_automute_verbs[] = {
4605 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT},
4606 { } /* end */
4607};
4608
Takashi Iwai83012a72012-08-24 18:38:08 +02004609#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02004610static const struct hda_amp_list ad1882_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02004611 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4612 { 0x20, HDA_INPUT, 1 }, /* Mic */
4613 { 0x20, HDA_INPUT, 4 }, /* Line */
4614 { 0x20, HDA_INPUT, 6 }, /* CD */
4615 { } /* end */
4616};
4617#endif
4618
Takashi Iwai0ac85512007-06-20 15:46:13 +02004619/* models */
4620enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004621 AD1882_AUTO,
Takashi Iwai0ac85512007-06-20 15:46:13 +02004622 AD1882_3STACK,
4623 AD1882_6STACK,
Takashi Iwai1c868452012-08-13 11:09:35 +02004624 AD1882_3STACK_AUTOMUTE,
Takashi Iwai0ac85512007-06-20 15:46:13 +02004625 AD1882_MODELS
4626};
4627
Takashi Iwaiea734962011-01-17 11:29:34 +01004628static const char * const ad1882_models[AD1986A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004629 [AD1882_AUTO] = "auto",
Takashi Iwai0ac85512007-06-20 15:46:13 +02004630 [AD1882_3STACK] = "3stack",
4631 [AD1882_6STACK] = "6stack",
Takashi Iwai1c868452012-08-13 11:09:35 +02004632 [AD1882_3STACK_AUTOMUTE] = "3stack-automute",
Takashi Iwai0ac85512007-06-20 15:46:13 +02004633};
4634
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004635static int ad1882_parse_auto_config(struct hda_codec *codec)
4636{
4637 struct ad198x_spec *spec = codec->spec;
4638
Takashi Iwaif2f8be42013-01-21 16:40:16 +01004639 spec->gen.mixer_nid = 0x20;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004640 spec->beep_dev_nid = 0x10;
4641 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4642 return ad198x_parse_auto_config(codec);
4643}
Takashi Iwai0ac85512007-06-20 15:46:13 +02004644
4645static int patch_ad1882(struct hda_codec *codec)
4646{
4647 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004648 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004649
Takashi Iwai361dab32012-05-09 14:35:27 +02004650 err = alloc_ad_spec(codec);
4651 if (err < 0)
4652 return err;
4653 spec = codec->spec;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004654
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004655 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4656 ad1882_models, NULL);
4657 if (board_config == AD1882_AUTO) {
4658 err = ad1882_parse_auto_config(codec);
4659 if (err < 0) {
4660 ad198x_free(codec);
4661 return err;
4662 }
4663 return 0;
4664 }
4665
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004666 err = snd_hda_attach_beep_device(codec, 0x10);
4667 if (err < 0) {
4668 ad198x_free(codec);
4669 return err;
4670 }
4671 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4672
Takashi Iwai0ac85512007-06-20 15:46:13 +02004673 spec->multiout.max_channels = 6;
4674 spec->multiout.num_dacs = 3;
4675 spec->multiout.dac_nids = ad1882_dac_nids;
4676 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4677 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4678 spec->adc_nids = ad1882_adc_nids;
4679 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004680 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004681 spec->input_mux = &ad1882_capture_source;
4682 else
4683 spec->input_mux = &ad1882a_capture_source;
4684 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004685 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004686 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004687 spec->mixers[1] = ad1882_loopback_mixers;
4688 else
4689 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004690 spec->num_init_verbs = 1;
4691 spec->init_verbs[0] = ad1882_init_verbs;
4692 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02004693#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02004694 spec->loopback.amplist = ad1882_loopbacks;
4695#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004696 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004697
4698 codec->patch_ops = ad198x_patch_ops;
4699
4700 /* override some parameters */
Takashi Iwai0ac85512007-06-20 15:46:13 +02004701 switch (board_config) {
4702 default:
4703 case AD1882_3STACK:
Takashi Iwai1c868452012-08-13 11:09:35 +02004704 case AD1882_3STACK_AUTOMUTE:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004705 spec->num_mixers = 3;
4706 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004707 spec->channel_mode = ad1882_modes;
4708 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4709 spec->need_dac_fix = 1;
4710 spec->multiout.max_channels = 2;
4711 spec->multiout.num_dacs = 1;
Takashi Iwai1c868452012-08-13 11:09:35 +02004712 if (board_config != AD1882_3STACK) {
4713 spec->init_verbs[spec->num_init_verbs++] =
4714 ad1882_3stack_automute_verbs;
4715 codec->patch_ops.unsol_event = ad1882_3stack_unsol_event;
4716 codec->patch_ops.init = ad1882_3stack_automute_init;
4717 }
Takashi Iwai0ac85512007-06-20 15:46:13 +02004718 break;
4719 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004720 spec->num_mixers = 3;
4721 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004722 break;
4723 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01004724
4725 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004726 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004727
Takashi Iwai0ac85512007-06-20 15:46:13 +02004728 return 0;
4729}
4730
4731
4732/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004733 * patch entries
4734 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004735static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004736 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004737 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004738 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004739 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004740 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4741 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004742 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4743 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004744 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004745 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004746 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004747 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004748 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004749 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4750 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004751 {} /* terminator */
4752};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004753
4754MODULE_ALIAS("snd-hda-codec-id:11d4*");
4755
4756MODULE_LICENSE("GPL");
4757MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4758
4759static struct hda_codec_preset_list analog_list = {
4760 .preset = snd_hda_preset_analog,
4761 .owner = THIS_MODULE,
4762};
4763
4764static int __init patch_analog_init(void)
4765{
4766 return snd_hda_add_codec_preset(&analog_list);
4767}
4768
4769static void __exit patch_analog_exit(void)
4770{
4771 snd_hda_delete_codec_preset(&analog_list);
4772}
4773
4774module_init(patch_analog_init)
4775module_exit(patch_analog_exit)