blob: a667256984fd90b1458cfe21d2cc24403c97f1fb [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 Iwai9ff4bc82013-01-22 16:45:58 +010035#define ENABLE_AD_STATIC_QUIRKS
36
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020037struct ad198x_spec {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +010038 struct hda_gen_spec gen;
39
Takashi Iwai272f3ea2013-01-22 15:31:33 +010040 /* for auto parser */
41 int smux_paths[4];
42 unsigned int cur_smux;
Takashi Iwaia928bd22013-01-22 18:18:42 +010043 hda_nid_t eapd_nid;
Takashi Iwai272f3ea2013-01-22 15:31:33 +010044
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010045 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Takashi Iwai9ff4bc82013-01-22 16:45:58 +010046
47#ifdef ENABLE_AD_STATIC_QUIRKS
48 const struct snd_kcontrol_new *mixers[6];
49 int num_mixers;
Raymond Yau28220842011-02-08 19:58:25 +080050 const struct hda_verb *init_verbs[6]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010051 * don't forget NULL termination!
52 */
53 unsigned int num_init_verbs;
54
55 /* playback */
56 struct hda_multi_out multiout; /* playback set-up
57 * max_channels, dacs must be set
58 * dig_out_nid and hp_nid are optional
59 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010060 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020061 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010062
63 /* capture */
64 unsigned int num_adc_nids;
Takashi Iwai498f5b12011-05-02 11:33:15 +020065 const hda_nid_t *adc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010066 hda_nid_t dig_in_nid; /* digital-in NID; optional */
67
68 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020069 const struct hda_input_mux *input_mux;
Takashi Iwai498f5b12011-05-02 11:33:15 +020070 const hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010071 unsigned int cur_mux[3];
72
73 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010074 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010075 int num_channel_mode;
76
77 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020078 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010079
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020080 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010081
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010082 unsigned int jack_present: 1;
83 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010084 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai18478e82012-03-09 17:51:10 +010085 unsigned int avoid_init_slave_vol:1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +020086
Takashi Iwai83012a72012-08-24 18:38:08 +020087#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +020088 struct hda_loopback_check loopback;
89#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010090 /* for virtual master */
91 hda_nid_t vmaster_nid;
Takashi Iwaiea734962011-01-17 11:29:34 +010092 const char * const *slave_vols;
93 const char * const *slave_sws;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +010094#endif /* ENABLE_AD_STATIC_QUIRKS */
Linus Torvalds1da177e2005-04-16 15:20:36 -070095};
96
Takashi Iwai9ff4bc82013-01-22 16:45:58 +010097#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020098/*
99 * input MUX handling (common part)
100 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100101static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200102{
103 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
104 struct ad198x_spec *spec = codec->spec;
105
106 return snd_hda_input_mux_info(spec->input_mux, uinfo);
107}
108
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100109static int ad198x_mux_enum_get(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
Takashi Iwai985be542005-11-02 18:26:49 +0100115 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200116 return 0;
117}
118
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100119static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200120{
121 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
122 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100123 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200124
125 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100126 spec->capsrc_nids[adc_idx],
127 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200128}
129
130/*
131 * initialization (common callbacks)
132 */
133static int ad198x_init(struct hda_codec *codec)
134{
135 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100136 int i;
137
138 for (i = 0; i < spec->num_init_verbs; i++)
139 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200140 return 0;
141}
142
Takashi Iwai9322ca52012-02-03 14:28:01 +0100143static const char * const ad_slave_pfxs[] = {
144 "Front", "Surround", "Center", "LFE", "Side",
145 "Headphone", "Mono", "Speaker", "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100146 NULL
147};
148
Takashi Iwai9322ca52012-02-03 14:28:01 +0100149static const char * const ad1988_6stack_fp_slave_pfxs[] = {
150 "Front", "Surround", "Center", "LFE", "Side", "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100151 NULL
152};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100153#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai2134ea42008-01-10 16:53:55 +0100154
Takashi Iwai67d634c2009-11-16 15:35:59 +0100155#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100156/* additional beep mixers; the actual parameters are overwritten at build */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200157static const struct snd_kcontrol_new ad_beep_mixer[] = {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100158 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200159 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100160 { } /* end */
161};
162
Takashi Iwai498f5b12011-05-02 11:33:15 +0200163static const struct snd_kcontrol_new ad_beep2_mixer[] = {
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100164 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
165 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
166 { } /* end */
167};
168
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100169#define set_beep_amp(spec, nid, idx, dir) \
170 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100171#else
172#define set_beep_amp(spec, nid, idx, dir) /* NOP */
173#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100174
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100175#ifdef CONFIG_SND_HDA_INPUT_BEEP
176static int create_beep_ctls(struct hda_codec *codec)
177{
178 struct ad198x_spec *spec = codec->spec;
179 const struct snd_kcontrol_new *knew;
180
181 if (!spec->beep_amp)
182 return 0;
183
184 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
185 for ( ; knew->name; knew++) {
186 int err;
187 struct snd_kcontrol *kctl;
188 kctl = snd_ctl_new1(knew, codec);
189 if (!kctl)
190 return -ENOMEM;
191 kctl->private_value = spec->beep_amp;
192 err = snd_hda_ctl_add(codec, 0, kctl);
193 if (err < 0)
194 return err;
195 }
196 return 0;
197}
198#else
199#define create_beep_ctls(codec) 0
200#endif
201
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100202#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200203static int ad198x_build_controls(struct hda_codec *codec)
204{
205 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100206 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100207 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200208 int err;
209
Takashi Iwai985be542005-11-02 18:26:49 +0100210 for (i = 0; i < spec->num_mixers; i++) {
211 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
212 if (err < 0)
213 return err;
214 }
215 if (spec->multiout.dig_out_nid) {
Stephen Warren74b654c2011-06-01 11:14:18 -0600216 err = snd_hda_create_spdif_out_ctls(codec,
217 spec->multiout.dig_out_nid,
218 spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100219 if (err < 0)
220 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100221 err = snd_hda_create_spdif_share_sw(codec,
222 &spec->multiout);
223 if (err < 0)
224 return err;
225 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100226 }
227 if (spec->dig_in_nid) {
228 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
229 if (err < 0)
230 return err;
231 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100232
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100233 /* create beep controls if needed */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100234 err = create_beep_ctls(codec);
235 if (err < 0)
236 return err;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100237
Takashi Iwai2134ea42008-01-10 16:53:55 +0100238 /* if we have no master control, let's create it */
239 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100240 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100241 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100242 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai18478e82012-03-09 17:51:10 +0100243 err = __snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100244 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100245 (spec->slave_vols ?
Takashi Iwai9322ca52012-02-03 14:28:01 +0100246 spec->slave_vols : ad_slave_pfxs),
Takashi Iwai18478e82012-03-09 17:51:10 +0100247 "Playback Volume",
Takashi Iwai29e58532012-03-12 12:25:03 +0100248 !spec->avoid_init_slave_vol, NULL);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100249 if (err < 0)
250 return err;
251 }
252 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
253 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
254 NULL,
255 (spec->slave_sws ?
Takashi Iwai9322ca52012-02-03 14:28:01 +0100256 spec->slave_sws : ad_slave_pfxs),
257 "Playback Switch");
Takashi Iwai2134ea42008-01-10 16:53:55 +0100258 if (err < 0)
259 return err;
260 }
261
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100262 /* assign Capture Source enums to NID */
263 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
264 if (!kctl)
265 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
266 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100267 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100268 if (err < 0)
269 return err;
270 }
271
272 /* assign IEC958 enums to NID */
273 kctl = snd_hda_find_mixer_ctl(codec,
274 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
275 if (kctl) {
276 err = snd_hda_add_nid(codec, kctl, 0,
277 spec->multiout.dig_out_nid);
278 if (err < 0)
279 return err;
280 }
281
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200282 return 0;
283}
284
Takashi Iwai83012a72012-08-24 18:38:08 +0200285#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +0200286static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
287{
288 struct ad198x_spec *spec = codec->spec;
289 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
290}
291#endif
292
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200293/*
294 * Analog playback callbacks
295 */
296static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
297 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100298 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200299{
300 struct ad198x_spec *spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100301 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
Takashi Iwai9a081602008-02-12 18:37:26 +0100302 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200303}
304
305static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
306 struct hda_codec *codec,
307 unsigned int stream_tag,
308 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100309 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200310{
311 struct ad198x_spec *spec = codec->spec;
312 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
313 format, substream);
314}
315
316static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
317 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100318 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200319{
320 struct ad198x_spec *spec = codec->spec;
321 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
322}
323
324/*
325 * Digital out
326 */
327static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
328 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100329 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200330{
331 struct ad198x_spec *spec = codec->spec;
332 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
333}
334
335static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
336 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100337 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200338{
339 struct ad198x_spec *spec = codec->spec;
340 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
341}
342
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200343static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
344 struct hda_codec *codec,
345 unsigned int stream_tag,
346 unsigned int format,
347 struct snd_pcm_substream *substream)
348{
349 struct ad198x_spec *spec = codec->spec;
350 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
351 format, substream);
352}
353
Takashi Iwai9411e212009-02-13 11:32:28 +0100354static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
355 struct hda_codec *codec,
356 struct snd_pcm_substream *substream)
357{
358 struct ad198x_spec *spec = codec->spec;
359 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
360}
361
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200362/*
363 * Analog capture
364 */
365static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
366 struct hda_codec *codec,
367 unsigned int stream_tag,
368 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100369 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200370{
371 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100372 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
373 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200374 return 0;
375}
376
377static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
378 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100379 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200380{
381 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100382 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200383 return 0;
384}
385
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200386/*
387 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200388static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200389 .substreams = 1,
390 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100391 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200392 .nid = 0, /* fill later */
393 .ops = {
394 .open = ad198x_playback_pcm_open,
395 .prepare = ad198x_playback_pcm_prepare,
Raymond Yau34588702011-09-23 19:03:25 +0800396 .cleanup = ad198x_playback_pcm_cleanup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200397 },
398};
399
Takashi Iwai498f5b12011-05-02 11:33:15 +0200400static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100401 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200402 .channels_min = 2,
403 .channels_max = 2,
404 .nid = 0, /* fill later */
405 .ops = {
406 .prepare = ad198x_capture_pcm_prepare,
407 .cleanup = ad198x_capture_pcm_cleanup
408 },
409};
410
Takashi Iwai498f5b12011-05-02 11:33:15 +0200411static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200412 .substreams = 1,
413 .channels_min = 2,
414 .channels_max = 2,
415 .nid = 0, /* fill later */
416 .ops = {
417 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200418 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100419 .prepare = ad198x_dig_playback_pcm_prepare,
420 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200421 },
422};
423
Takashi Iwai498f5b12011-05-02 11:33:15 +0200424static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100425 .substreams = 1,
426 .channels_min = 2,
427 .channels_max = 2,
428 /* NID is set in alc_build_pcms */
429};
430
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200431static int ad198x_build_pcms(struct hda_codec *codec)
432{
433 struct ad198x_spec *spec = codec->spec;
434 struct hda_pcm *info = spec->pcm_rec;
435
436 codec->num_pcms = 1;
437 codec->pcm_info = info;
438
439 info->name = "AD198x Analog";
440 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
441 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
442 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
443 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100444 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
445 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200446
447 if (spec->multiout.dig_out_nid) {
448 info++;
449 codec->num_pcms++;
Takashi Iwaiae24c312012-11-05 12:32:46 +0100450 codec->spdif_status_reset = 1;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200451 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100452 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200453 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
454 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100455 if (spec->dig_in_nid) {
456 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
457 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
458 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200459 }
460
461 return 0;
462}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100463#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200464
Daniel T Chenea52bf22009-12-27 18:48:29 -0500465static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
466 hda_nid_t hp)
467{
Raymond Yaua01ef052011-06-01 15:09:48 +0800468 if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
469 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100470 !codec->inv_eapd ? 0x00 : 0x02);
Raymond Yaua01ef052011-06-01 15:09:48 +0800471 if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
472 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100473 !codec->inv_eapd ? 0x00 : 0x02);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500474}
475
476static void ad198x_power_eapd(struct hda_codec *codec)
477{
478 /* We currently only handle front, HP */
479 switch (codec->vendor_id) {
480 case 0x11d41882:
481 case 0x11d4882a:
482 case 0x11d41884:
483 case 0x11d41984:
484 case 0x11d41883:
485 case 0x11d4184a:
486 case 0x11d4194a:
487 case 0x11d4194b:
Takashi Iwai4dffbe02011-06-03 10:05:02 +0200488 case 0x11d41988:
489 case 0x11d4198b:
490 case 0x11d4989a:
491 case 0x11d4989b:
Daniel T Chenea52bf22009-12-27 18:48:29 -0500492 ad198x_power_eapd_write(codec, 0x12, 0x11);
493 break;
494 case 0x11d41981:
495 case 0x11d41983:
496 ad198x_power_eapd_write(codec, 0x05, 0x06);
497 break;
498 case 0x11d41986:
499 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
500 break;
Daniel T Chenea52bf22009-12-27 18:48:29 -0500501 }
502}
503
Takashi Iwai0da26922011-04-26 15:18:33 +0200504static void ad198x_shutup(struct hda_codec *codec)
505{
506 snd_hda_shutup_pins(codec);
507 ad198x_power_eapd(codec);
508}
509
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200510static void ad198x_free(struct hda_codec *codec)
511{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100512 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100513
Takashi Iwai603c4012008-07-30 15:01:44 +0200514 if (!spec)
515 return;
516
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100517 snd_hda_gen_spec_free(&spec->gen);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100518 kfree(spec);
519 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200520}
521
Takashi Iwai2a439522011-07-26 09:52:50 +0200522#ifdef CONFIG_PM
Takashi Iwai68cb2b52012-07-02 15:20:37 +0200523static int ad198x_suspend(struct hda_codec *codec)
Daniel T Chenea52bf22009-12-27 18:48:29 -0500524{
525 ad198x_shutup(codec);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500526 return 0;
527}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500528#endif
529
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100530#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +0200531static const struct hda_codec_ops ad198x_patch_ops = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200532 .build_controls = ad198x_build_controls,
533 .build_pcms = ad198x_build_pcms,
534 .init = ad198x_init,
535 .free = ad198x_free,
Takashi Iwai2a439522011-07-26 09:52:50 +0200536#ifdef CONFIG_PM
Takashi Iwai83012a72012-08-24 18:38:08 +0200537 .check_power_status = ad198x_check_power_status,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500538 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500539#endif
540 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200541};
542
543
544/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100545 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100546 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100547 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200548#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100549
550static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
551 struct snd_ctl_elem_value *ucontrol)
552{
553 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
554 struct ad198x_spec *spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100555 if (codec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100556 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
557 else
558 ucontrol->value.integer.value[0] = spec->cur_eapd;
559 return 0;
560}
561
562static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
563 struct snd_ctl_elem_value *ucontrol)
564{
565 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
566 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100567 hda_nid_t nid = kcontrol->private_value & 0xff;
568 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100569 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100570 if (codec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100571 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200572 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100573 return 0;
574 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200575 snd_hda_codec_write_cache(codec, nid,
576 0, AC_VERB_SET_EAPD_BTLENABLE,
577 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100578 return 1;
579}
580
Takashi Iwai9230d212006-03-13 13:49:49 +0100581static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
582 struct snd_ctl_elem_info *uinfo);
583static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
584 struct snd_ctl_elem_value *ucontrol);
585static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
586 struct snd_ctl_elem_value *ucontrol);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100587#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai9230d212006-03-13 13:49:49 +0100588
589
Takashi Iwai18a815d2006-03-01 19:54:39 +0100590/*
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100591 * Automatic parse of I/O pins from the BIOS configuration
592 */
593
594static int ad198x_auto_build_controls(struct hda_codec *codec)
595{
596 int err;
597
598 err = snd_hda_gen_build_controls(codec);
599 if (err < 0)
600 return err;
601 err = create_beep_ctls(codec);
602 if (err < 0)
603 return err;
604 return 0;
605}
606
607static const struct hda_codec_ops ad198x_auto_patch_ops = {
608 .build_controls = ad198x_auto_build_controls,
609 .build_pcms = snd_hda_gen_build_pcms,
610 .init = snd_hda_gen_init,
Takashi Iwai7504b6c2013-03-18 11:25:51 +0100611 .free = snd_hda_gen_free,
Takashi Iwai8a6c21a2013-01-18 07:51:17 +0100612 .unsol_event = snd_hda_jack_unsol_event,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100613#ifdef CONFIG_PM
614 .check_power_status = snd_hda_gen_check_power_status,
615 .suspend = ad198x_suspend,
616#endif
617 .reboot_notify = ad198x_shutup,
618};
619
620
621static int ad198x_parse_auto_config(struct hda_codec *codec)
622{
623 struct ad198x_spec *spec = codec->spec;
624 struct auto_pin_cfg *cfg = &spec->gen.autocfg;
625 int err;
626
627 codec->spdif_status_reset = 1;
628 codec->no_trigger_sense = 1;
629 codec->no_sticky_stream = 1;
630
631 spec->gen.indep_hp = 1;
632
633 err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
634 if (err < 0)
635 return err;
636 err = snd_hda_gen_parse_auto_config(codec, cfg);
637 if (err < 0)
638 return err;
639
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100640 codec->patch_ops = ad198x_auto_patch_ops;
641
642 return 0;
643}
644
645/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200646 * AD1986A specific
647 */
648
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100649#ifdef ENABLE_AD_STATIC_QUIRKS
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650#define AD1986A_SPDIF_OUT 0x02
651#define AD1986A_FRONT_DAC 0x03
652#define AD1986A_SURR_DAC 0x04
653#define AD1986A_CLFE_DAC 0x05
654#define AD1986A_ADC 0x06
655
Takashi Iwai498f5b12011-05-02 11:33:15 +0200656static const hda_nid_t ad1986a_dac_nids[3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
658};
Takashi Iwai498f5b12011-05-02 11:33:15 +0200659static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
660static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Takashi Iwai498f5b12011-05-02 11:33:15 +0200662static const struct hda_input_mux ad1986a_capture_source = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 .num_items = 7,
664 .items = {
665 { "Mic", 0x0 },
666 { "CD", 0x1 },
667 { "Aux", 0x3 },
668 { "Line", 0x4 },
669 { "Mix", 0x5 },
670 { "Mono", 0x6 },
671 { "Phone", 0x7 },
672 },
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_vol = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200677 .ops = &snd_hda_bind_vol,
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
Takashi Iwai498f5b12011-05-02 11:33:15 +0200686static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200687 .ops = &snd_hda_bind_sw,
688 .values = {
689 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
690 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
691 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
692 0
693 },
694};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 * mixers
698 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200699static const struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200700 /*
701 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
702 */
703 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
704 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
706 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
707 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
708 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
709 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
710 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
711 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
712 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
713 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
714 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
715 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
716 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
717 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
718 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
719 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
720 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
721 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
722 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100723 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
725 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
726 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
727 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
728 {
729 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
730 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200731 .info = ad198x_mux_enum_info,
732 .get = ad198x_mux_enum_get,
733 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 },
735 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
736 { } /* end */
737};
738
Takashi Iwai9230d212006-03-13 13:49:49 +0100739/* additional mixers for 3stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200740static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100741 {
742 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
743 .name = "Channel Mode",
744 .info = ad198x_ch_mode_info,
745 .get = ad198x_ch_mode_get,
746 .put = ad198x_ch_mode_put,
747 },
748 { } /* end */
749};
750
751/* laptop model - 2ch only */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200752static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
Takashi Iwai9230d212006-03-13 13:49:49 +0100753
Takashi Iwai20a45e82007-08-15 22:20:45 +0200754/* master controls both pins 0x1a and 0x1b */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200755static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200756 .ops = &snd_hda_bind_vol,
757 .values = {
758 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
759 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
760 0,
761 },
762};
763
Takashi Iwai498f5b12011-05-02 11:33:15 +0200764static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200765 .ops = &snd_hda_bind_sw,
766 .values = {
767 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
768 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
769 0,
770 },
771};
772
Takashi Iwai498f5b12011-05-02 11:33:15 +0200773static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100774 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
775 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200776 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
777 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100778 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
779 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
780 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
781 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
782 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
783 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
784 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
785 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100786 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100787 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100788 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
789 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
790 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
791 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
792 {
793 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
794 .name = "Capture Source",
795 .info = ad198x_mux_enum_info,
796 .get = ad198x_mux_enum_get,
797 .put = ad198x_mux_enum_put,
798 },
799 { } /* end */
800};
801
Takashi Iwai825aa9722006-03-17 10:50:49 +0100802/* laptop-eapd model - 2ch only */
803
Takashi Iwai498f5b12011-05-02 11:33:15 +0200804static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100805 .num_items = 3,
806 .items = {
807 { "Mic", 0x0 },
808 { "Internal Mic", 0x4 },
809 { "Mix", 0x5 },
810 },
811};
812
Takashi Iwai498f5b12011-05-02 11:33:15 +0200813static const struct hda_input_mux ad1986a_automic_capture_source = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100814 .num_items = 2,
815 .items = {
816 { "Mic", 0x0 },
817 { "Mix", 0x5 },
818 },
819};
820
Takashi Iwai498f5b12011-05-02 11:33:15 +0200821static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200822 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
823 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200824 { } /* end */
825};
826
Takashi Iwai498f5b12011-05-02 11:33:15 +0200827static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100828 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
829 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100830 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
831 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100832 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100833 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
834 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
835 {
836 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
837 .name = "Capture Source",
838 .info = ad198x_mux_enum_info,
839 .get = ad198x_mux_enum_get,
840 .put = ad198x_mux_enum_put,
841 },
842 {
843 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
844 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100845 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100846 .info = ad198x_eapd_info,
847 .get = ad198x_eapd_get,
848 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100849 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100850 },
851 { } /* end */
852};
853
Takashi Iwai498f5b12011-05-02 11:33:15 +0200854static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
Takashi Iwai16d11a82009-06-24 14:07:53 +0200855 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
856 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100857 { } /* end */
858};
859
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100860/* re-connect the mic boost input according to the jack sensing */
861static void ad1986a_automic(struct hda_codec *codec)
862{
863 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100864 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100865 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
866 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100867 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100868}
869
870#define AD1986A_MIC_EVENT 0x36
871
872static void ad1986a_automic_unsol_event(struct hda_codec *codec,
873 unsigned int res)
874{
875 if ((res >> 26) != AD1986A_MIC_EVENT)
876 return;
877 ad1986a_automic(codec);
878}
879
880static int ad1986a_automic_init(struct hda_codec *codec)
881{
882 ad198x_init(codec);
883 ad1986a_automic(codec);
884 return 0;
885}
886
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200887/* laptop-automute - 2ch only */
888
889static void ad1986a_update_hp(struct hda_codec *codec)
890{
891 struct ad198x_spec *spec = codec->spec;
892 unsigned int mute;
893
894 if (spec->jack_present)
895 mute = HDA_AMP_MUTE; /* mute internal speaker */
896 else
897 /* unmute internal speaker if necessary */
898 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
899 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
900 HDA_AMP_MUTE, mute);
901}
902
903static void ad1986a_hp_automute(struct hda_codec *codec)
904{
905 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200906
Takashi Iwaid56757a2009-11-18 08:00:14 +0100907 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200908 if (spec->inv_jack_detect)
909 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200910 ad1986a_update_hp(codec);
911}
912
913#define AD1986A_HP_EVENT 0x37
914
915static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
916{
917 if ((res >> 26) != AD1986A_HP_EVENT)
918 return;
919 ad1986a_hp_automute(codec);
920}
921
922static int ad1986a_hp_init(struct hda_codec *codec)
923{
924 ad198x_init(codec);
925 ad1986a_hp_automute(codec);
926 return 0;
927}
928
929/* bind hp and internal speaker mute (with plug check) */
930static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
931 struct snd_ctl_elem_value *ucontrol)
932{
933 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai8092e602012-12-13 17:03:30 +0100934 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200935 if (change)
936 ad1986a_update_hp(codec);
937 return change;
938}
939
Takashi Iwai498f5b12011-05-02 11:33:15 +0200940static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200941 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
942 {
943 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
944 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100945 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200946 .info = snd_hda_mixer_amp_switch_info,
947 .get = snd_hda_mixer_amp_switch_get,
948 .put = ad1986a_hp_master_sw_put,
949 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
950 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200951 { } /* end */
952};
953
Takashi Iwai16d11a82009-06-24 14:07:53 +0200954
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955/*
956 * initialization verbs
957 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200958static const struct hda_verb ad1986a_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 /* Front, Surround, CLFE DAC; mute as default */
960 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
961 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
962 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
963 /* Downmix - off */
964 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
965 /* HP, Line-Out, Surround, CLFE selectors */
966 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
967 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
968 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
969 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
970 /* Mono selector */
971 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
972 /* Mic selector: Mic 1/2 pin */
973 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
974 /* Line-in selector: Line-in */
975 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
976 /* Mic 1/2 swap */
977 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
978 /* Record selector: mic */
979 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
980 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
981 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
982 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
983 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
984 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
985 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
986 /* PC beep */
987 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
988 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
989 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
990 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
991 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
992 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
993 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200994 /* HP Pin */
995 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
996 /* Front, Surround, CLFE Pins */
997 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
998 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
999 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1000 /* Mono Pin */
1001 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1002 /* Mic Pin */
1003 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1004 /* Line, Aux, CD, Beep-In Pin */
1005 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1006 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1007 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1008 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1009 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 { } /* end */
1011};
1012
Takashi Iwai498f5b12011-05-02 11:33:15 +02001013static const struct hda_verb ad1986a_ch2_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001014 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001015 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1016 /* Line-in selectors */
1017 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001018 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001019 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1020 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
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_ch4_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001026 /* Surround out -> Surround */
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 -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001030 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1031 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001032 { } /* end */
1033};
1034
Takashi Iwai498f5b12011-05-02 11:33:15 +02001035static const struct hda_verb ad1986a_ch6_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001036 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001037 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1038 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001039 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001040 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1041 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001042 { } /* end */
1043};
1044
Takashi Iwai498f5b12011-05-02 11:33:15 +02001045static const struct hda_channel_mode ad1986a_modes[3] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001046 { 2, ad1986a_ch2_init },
1047 { 4, ad1986a_ch4_init },
1048 { 6, ad1986a_ch6_init },
1049};
1050
Takashi Iwai825aa9722006-03-17 10:50:49 +01001051/* eapd initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001052static const struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001053 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +01001054 {}
1055};
1056
Takashi Iwai498f5b12011-05-02 11:33:15 +02001057static const struct hda_verb ad1986a_automic_verbs[] = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001058 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1059 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1060 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1061 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1062 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1063 {}
1064};
1065
Tobin Davisf36090f2007-01-08 11:07:12 +01001066/* Ultra initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001067static const struct hda_verb ad1986a_ultra_init[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001068 /* eapd initialization */
1069 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1070 /* CLFE -> Mic in */
1071 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1072 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1073 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1074 { } /* end */
1075};
1076
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001077/* pin sensing on HP jack */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001078static const struct hda_verb ad1986a_hp_init_verbs[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001079 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1080 {}
1081};
1082
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001083static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1084 unsigned int res)
1085{
1086 switch (res >> 26) {
1087 case AD1986A_HP_EVENT:
1088 ad1986a_hp_automute(codec);
1089 break;
1090 case AD1986A_MIC_EVENT:
1091 ad1986a_automic(codec);
1092 break;
1093 }
1094}
1095
1096static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1097{
1098 ad198x_init(codec);
1099 ad1986a_hp_automute(codec);
1100 ad1986a_automic(codec);
1101 return 0;
1102}
1103
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001104
Takashi Iwai9230d212006-03-13 13:49:49 +01001105/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001106enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001107 AD1986A_AUTO,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001108 AD1986A_6STACK,
1109 AD1986A_3STACK,
1110 AD1986A_LAPTOP,
1111 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001112 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001113 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001114 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001115 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001116 AD1986A_MODELS
1117};
Takashi Iwai9230d212006-03-13 13:49:49 +01001118
Takashi Iwaiea734962011-01-17 11:29:34 +01001119static const char * const ad1986a_models[AD1986A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001120 [AD1986A_AUTO] = "auto",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001121 [AD1986A_6STACK] = "6stack",
1122 [AD1986A_3STACK] = "3stack",
1123 [AD1986A_LAPTOP] = "laptop",
1124 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001125 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001126 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001127 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001128 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001129};
1130
Takashi Iwai498f5b12011-05-02 11:33:15 +02001131static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001132 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001133 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001134 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001135 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001136 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1137 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1138 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1139 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001140 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001141 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001142 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1143 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1144 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1145 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1146 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001147 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001148 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001149 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001150 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001151 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001152 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001153 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001154 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001155 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001156 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001157 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001158 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001159 {}
1160};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
Takashi Iwai83012a72012-08-24 18:38:08 +02001162#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001163static const struct hda_amp_list ad1986a_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001164 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1165 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1166 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1167 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1168 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1169 { } /* end */
1170};
1171#endif
1172
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001173static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1174{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001175 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001176 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1177}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001178#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001179
Takashi Iwai361dab32012-05-09 14:35:27 +02001180static int alloc_ad_spec(struct hda_codec *codec)
1181{
1182 struct ad198x_spec *spec;
1183
1184 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1185 if (!spec)
1186 return -ENOMEM;
1187 codec->spec = spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001188 snd_hda_gen_spec_init(&spec->gen);
Takashi Iwai361dab32012-05-09 14:35:27 +02001189 return 0;
1190}
1191
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001192/*
Takashi Iwaia928bd22013-01-22 18:18:42 +01001193 * AD1986A fixup codes
1194 */
1195
1196/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
1197static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
1198 const struct hda_fixup *fix, int action)
1199{
1200 if (action == HDA_FIXUP_ACT_PRE_PROBE)
1201 codec->inv_jack_detect = 1;
1202}
1203
1204enum {
1205 AD1986A_FIXUP_INV_JACK_DETECT,
1206};
1207
1208static const struct hda_fixup ad1986a_fixups[] = {
1209 [AD1986A_FIXUP_INV_JACK_DETECT] = {
1210 .type = HDA_FIXUP_FUNC,
1211 .v.func = ad_fixup_inv_jack_detect,
1212 },
1213};
1214
1215static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
1216 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
1217 {}
1218};
1219
1220/*
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001221 */
1222static int ad1986a_parse_auto_config(struct hda_codec *codec)
1223{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001224 int err;
1225 struct ad198x_spec *spec;
1226
1227 err = alloc_ad_spec(codec);
1228 if (err < 0)
1229 return err;
1230 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001231
1232 /* AD1986A has the inverted EAPD implementation */
1233 codec->inv_eapd = 1;
1234
Takashi Iwaif2f8be42013-01-21 16:40:16 +01001235 spec->gen.mixer_nid = 0x07;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001236 spec->gen.beep_nid = 0x19;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001237 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1238
1239 /* AD1986A has a hardware problem that it can't share a stream
1240 * with multiple output pins. The copy of front to surrounds
1241 * causes noisy or silent outputs at a certain timing, e.g.
1242 * changing the volume.
1243 * So, let's disable the shared stream.
1244 */
1245 spec->gen.multiout.no_share_stream = 1;
1246
Takashi Iwaia928bd22013-01-22 18:18:42 +01001247 snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups);
1248 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
1249
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001250 err = ad198x_parse_auto_config(codec);
1251 if (err < 0) {
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001252 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001253 return err;
1254 }
1255
Takashi Iwaia928bd22013-01-22 18:18:42 +01001256 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
1257
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001258 return 0;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001259}
1260
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001261#ifdef ENABLE_AD_STATIC_QUIRKS
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262static int patch_ad1986a(struct hda_codec *codec)
1263{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001264 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001265 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001267 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1268 ad1986a_models,
1269 ad1986a_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01001270 if (board_config < 0) {
1271 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
1272 codec->chip_name);
1273 board_config = AD1986A_AUTO;
1274 }
1275
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001276 if (board_config == AD1986A_AUTO)
1277 return ad1986a_parse_auto_config(codec);
1278
Takashi Iwai361dab32012-05-09 14:35:27 +02001279 err = alloc_ad_spec(codec);
1280 if (err < 0)
1281 return err;
1282 spec = codec->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001284 err = snd_hda_attach_beep_device(codec, 0x19);
1285 if (err < 0) {
1286 ad198x_free(codec);
1287 return err;
1288 }
1289 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 spec->multiout.max_channels = 6;
1292 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1293 spec->multiout.dac_nids = ad1986a_dac_nids;
1294 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001295 spec->num_adc_nids = 1;
1296 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001297 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001298 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001299 spec->num_mixers = 1;
1300 spec->mixers[0] = ad1986a_mixers;
1301 spec->num_init_verbs = 1;
1302 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwai83012a72012-08-24 18:38:08 +02001303#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02001304 spec->loopback.amplist = ad1986a_loopbacks;
1305#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001306 spec->vmaster_nid = 0x1b;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001307 codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001309 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Takashi Iwai9230d212006-03-13 13:49:49 +01001311 /* override some parameters */
Takashi Iwai9230d212006-03-13 13:49:49 +01001312 switch (board_config) {
1313 case AD1986A_3STACK:
1314 spec->num_mixers = 2;
1315 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001316 spec->num_init_verbs = 2;
1317 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001318 spec->channel_mode = ad1986a_modes;
1319 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001320 spec->need_dac_fix = 1;
1321 spec->multiout.max_channels = 2;
1322 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001323 break;
1324 case AD1986A_LAPTOP:
1325 spec->mixers[0] = ad1986a_laptop_mixers;
1326 spec->multiout.max_channels = 2;
1327 spec->multiout.num_dacs = 1;
1328 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1329 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001330 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001331 spec->num_mixers = 3;
1332 spec->mixers[0] = ad1986a_laptop_master_mixers;
1333 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1334 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001335 spec->num_init_verbs = 2;
1336 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1337 spec->multiout.max_channels = 2;
1338 spec->multiout.num_dacs = 1;
1339 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1340 if (!is_jack_available(codec, 0x25))
1341 spec->multiout.dig_out_nid = 0;
1342 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1343 break;
1344 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001345 spec->num_mixers = 2;
1346 spec->mixers[0] = ad1986a_laptop_master_mixers;
1347 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001348 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001349 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001350 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001351 spec->multiout.max_channels = 2;
1352 spec->multiout.num_dacs = 1;
1353 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001354 if (!is_jack_available(codec, 0x25))
1355 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001356 spec->input_mux = &ad1986a_automic_capture_source;
1357 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1358 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001359 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001360 case AD1986A_SAMSUNG_P50:
1361 spec->num_mixers = 2;
1362 spec->mixers[0] = ad1986a_automute_master_mixers;
1363 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1364 spec->num_init_verbs = 4;
1365 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1366 spec->init_verbs[2] = ad1986a_automic_verbs;
1367 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1368 spec->multiout.max_channels = 2;
1369 spec->multiout.num_dacs = 1;
1370 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1371 if (!is_jack_available(codec, 0x25))
1372 spec->multiout.dig_out_nid = 0;
1373 spec->input_mux = &ad1986a_automic_capture_source;
1374 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1375 codec->patch_ops.init = ad1986a_samsung_p50_init;
1376 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001377 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001378 spec->num_mixers = 3;
1379 spec->mixers[0] = ad1986a_automute_master_mixers;
1380 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1381 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001382 spec->num_init_verbs = 3;
1383 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1384 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1385 spec->multiout.max_channels = 2;
1386 spec->multiout.num_dacs = 1;
1387 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001388 if (!is_jack_available(codec, 0x25))
1389 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001390 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1391 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1392 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001393 /* Lenovo N100 seems to report the reversed bit
1394 * for HP jack-sensing
1395 */
1396 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001397 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001398 case AD1986A_ULTRA:
1399 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1400 spec->num_init_verbs = 2;
1401 spec->init_verbs[1] = ad1986a_ultra_init;
1402 spec->multiout.max_channels = 2;
1403 spec->multiout.num_dacs = 1;
1404 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1405 spec->multiout.dig_out_nid = 0;
1406 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001407 }
1408
Takashi Iwaid29240c2007-10-26 12:35:56 +02001409 /* AD1986A has a hardware problem that it can't share a stream
1410 * with multiple output pins. The copy of front to surrounds
1411 * causes noisy or silent outputs at a certain timing, e.g.
1412 * changing the volume.
1413 * So, let's disable the shared stream.
1414 */
1415 spec->multiout.no_share_stream = 1;
1416
Takashi Iwai729d55b2009-12-25 22:49:01 +01001417 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001418 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001419
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 return 0;
1421}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001422#else /* ENABLE_AD_STATIC_QUIRKS */
1423#define patch_ad1986a ad1986a_parse_auto_config
1424#endif /* ENABLE_AD_STATIC_QUIRKS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425
1426/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001427 * AD1983 specific
1428 */
1429
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001430#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001431#define AD1983_SPDIF_OUT 0x02
1432#define AD1983_DAC 0x03
1433#define AD1983_ADC 0x04
1434
Takashi Iwai498f5b12011-05-02 11:33:15 +02001435static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
1436static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
1437static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001438
Takashi Iwai498f5b12011-05-02 11:33:15 +02001439static const struct hda_input_mux ad1983_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001440 .num_items = 4,
1441 .items = {
1442 { "Mic", 0x0 },
1443 { "Line", 0x1 },
1444 { "Mix", 0x2 },
1445 { "Mix Mono", 0x3 },
1446 },
1447};
1448
1449/*
1450 * SPDIF playback route
1451 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001452static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001453{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001454 static const char * const texts[] = { "PCM", "ADC" };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001455
1456 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1457 uinfo->count = 1;
1458 uinfo->value.enumerated.items = 2;
1459 if (uinfo->value.enumerated.item > 1)
1460 uinfo->value.enumerated.item = 1;
1461 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1462 return 0;
1463}
1464
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001465static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001466{
1467 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1468 struct ad198x_spec *spec = codec->spec;
1469
1470 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1471 return 0;
1472}
1473
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001474static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001475{
1476 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1477 struct ad198x_spec *spec = codec->spec;
1478
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001479 if (ucontrol->value.enumerated.item[0] > 1)
1480 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001481 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1482 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001483 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1484 AC_VERB_SET_CONNECT_SEL,
1485 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001486 return 1;
1487 }
1488 return 0;
1489}
1490
Takashi Iwai498f5b12011-05-02 11:33:15 +02001491static const struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001492 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1493 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1494 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1495 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1496 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1497 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1498 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1499 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001504 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001505 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1507 {
1508 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1509 .name = "Capture Source",
1510 .info = ad198x_mux_enum_info,
1511 .get = ad198x_mux_enum_get,
1512 .put = ad198x_mux_enum_put,
1513 },
1514 {
1515 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001516 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001517 .info = ad1983_spdif_route_info,
1518 .get = ad1983_spdif_route_get,
1519 .put = ad1983_spdif_route_put,
1520 },
1521 { } /* end */
1522};
1523
Takashi Iwai498f5b12011-05-02 11:33:15 +02001524static const struct hda_verb ad1983_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001525 /* Front, HP, Mono; mute as default */
1526 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1527 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1528 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1529 /* Beep, PCM, Mic, Line-In: mute */
1530 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1531 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1532 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1533 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1534 /* Front, HP selectors; from Mix */
1535 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1536 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1537 /* Mono selector; from Mix */
1538 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1539 /* Mic selector; Mic */
1540 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1541 /* Line-in selector: Line-in */
1542 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1543 /* Mic boost: 0dB */
1544 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1545 /* Record selector: mic */
1546 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1547 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1548 /* SPDIF route: PCM */
1549 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1550 /* Front Pin */
1551 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1552 /* HP Pin */
1553 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1554 /* Mono Pin */
1555 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1556 /* Mic Pin */
1557 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1558 /* Line Pin */
1559 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1560 { } /* end */
1561};
1562
Takashi Iwai83012a72012-08-24 18:38:08 +02001563#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001564static const struct hda_amp_list ad1983_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001565 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1566 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1567 { } /* end */
1568};
1569#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001570
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001571/* models */
1572enum {
1573 AD1983_AUTO,
1574 AD1983_BASIC,
1575 AD1983_MODELS
1576};
1577
1578static const char * const ad1983_models[AD1983_MODELS] = {
1579 [AD1983_AUTO] = "auto",
1580 [AD1983_BASIC] = "basic",
1581};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001582#endif /* ENABLE_AD_STATIC_QUIRKS */
1583
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001584
Takashi Iwai272f3ea2013-01-22 15:31:33 +01001585/*
1586 * SPDIF mux control for AD1983 auto-parser
1587 */
1588static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
1589 struct snd_ctl_elem_info *uinfo)
1590{
1591 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1592 struct ad198x_spec *spec = codec->spec;
1593 static const char * const texts2[] = { "PCM", "ADC" };
1594 static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
1595 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1596 int num_conns = snd_hda_get_num_conns(codec, dig_out);
1597
1598 if (num_conns == 2)
1599 return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
1600 else if (num_conns == 3)
1601 return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
1602 else
1603 return -EINVAL;
1604}
1605
1606static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
1607 struct snd_ctl_elem_value *ucontrol)
1608{
1609 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1610 struct ad198x_spec *spec = codec->spec;
1611
1612 ucontrol->value.enumerated.item[0] = spec->cur_smux;
1613 return 0;
1614}
1615
1616static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
1617 struct snd_ctl_elem_value *ucontrol)
1618{
1619 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1620 struct ad198x_spec *spec = codec->spec;
1621 unsigned int val = ucontrol->value.enumerated.item[0];
1622 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1623 int num_conns = snd_hda_get_num_conns(codec, dig_out);
1624
1625 if (val >= num_conns)
1626 return -EINVAL;
1627 if (spec->cur_smux == val)
1628 return 0;
1629 spec->cur_smux = val;
1630 snd_hda_codec_write_cache(codec, dig_out, 0,
1631 AC_VERB_SET_CONNECT_SEL, val);
1632 return 1;
1633}
1634
1635static struct snd_kcontrol_new ad1983_auto_smux_mixer = {
1636 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1637 .name = "IEC958 Playback Source",
1638 .info = ad1983_auto_smux_enum_info,
1639 .get = ad1983_auto_smux_enum_get,
1640 .put = ad1983_auto_smux_enum_put,
1641};
1642
1643static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
1644{
1645 struct ad198x_spec *spec = codec->spec;
1646 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1647 int num_conns;
1648
1649 if (!dig_out)
1650 return 0;
1651 num_conns = snd_hda_get_num_conns(codec, dig_out);
1652 if (num_conns != 2 && num_conns != 3)
1653 return 0;
1654 if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
1655 return -ENOMEM;
1656 return 0;
1657}
1658
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001659static int ad1983_parse_auto_config(struct hda_codec *codec)
1660{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001661 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001662 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001663
Takashi Iwai361dab32012-05-09 14:35:27 +02001664 err = alloc_ad_spec(codec);
1665 if (err < 0)
1666 return err;
1667 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001668
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001669 spec->gen.beep_nid = 0x10;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001670 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1671 err = ad198x_parse_auto_config(codec);
1672 if (err < 0)
1673 goto error;
1674 err = ad1983_add_spdif_mux_ctl(codec);
1675 if (err < 0)
1676 goto error;
1677 return 0;
1678
1679 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001680 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001681 return err;
1682}
1683
1684#ifdef ENABLE_AD_STATIC_QUIRKS
1685static int patch_ad1983(struct hda_codec *codec)
1686{
1687 struct ad198x_spec *spec;
1688 int board_config;
1689 int err;
1690
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001691 board_config = snd_hda_check_board_config(codec, AD1983_MODELS,
1692 ad1983_models, NULL);
Takashi Iwai657e1b92013-01-22 18:42:39 +01001693 if (board_config < 0) {
1694 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
1695 codec->chip_name);
1696 board_config = AD1983_AUTO;
1697 }
1698
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001699 if (board_config == AD1983_AUTO)
1700 return ad1983_parse_auto_config(codec);
1701
1702 err = alloc_ad_spec(codec);
1703 if (err < 0)
1704 return err;
1705 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001706
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001707 err = snd_hda_attach_beep_device(codec, 0x10);
1708 if (err < 0) {
1709 ad198x_free(codec);
1710 return err;
1711 }
1712 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1713
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001714 spec->multiout.max_channels = 2;
1715 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1716 spec->multiout.dac_nids = ad1983_dac_nids;
1717 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001718 spec->num_adc_nids = 1;
1719 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001720 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001721 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001722 spec->num_mixers = 1;
1723 spec->mixers[0] = ad1983_mixers;
1724 spec->num_init_verbs = 1;
1725 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001726 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02001727#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02001728 spec->loopback.amplist = ad1983_loopbacks;
1729#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001730 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001731
1732 codec->patch_ops = ad198x_patch_ops;
1733
Takashi Iwai729d55b2009-12-25 22:49:01 +01001734 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001735 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001736
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001737 return 0;
1738}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001739#else /* ENABLE_AD_STATIC_QUIRKS */
1740#define patch_ad1983 ad1983_parse_auto_config
1741#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001742
1743
1744/*
1745 * AD1981 HD specific
1746 */
1747
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001748#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001749#define AD1981_SPDIF_OUT 0x02
1750#define AD1981_DAC 0x03
1751#define AD1981_ADC 0x04
1752
Takashi Iwai498f5b12011-05-02 11:33:15 +02001753static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
1754static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
1755static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001756
1757/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001758static const struct hda_input_mux ad1981_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001759 .num_items = 7,
1760 .items = {
1761 { "Front Mic", 0x0 },
1762 { "Line", 0x1 },
1763 { "Mix", 0x2 },
1764 { "Mix Mono", 0x3 },
1765 { "CD", 0x4 },
1766 { "Mic", 0x6 },
1767 { "Aux", 0x7 },
1768 },
1769};
1770
Takashi Iwai498f5b12011-05-02 11:33:15 +02001771static const struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001772 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1773 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1774 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1775 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1776 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1777 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1778 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1779 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1780 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1781 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1782 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1783 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1784 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1785 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1786 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1787 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1788 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1789 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001790 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1791 HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001792 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1793 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1794 {
1795 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1796 .name = "Capture Source",
1797 .info = ad198x_mux_enum_info,
1798 .get = ad198x_mux_enum_get,
1799 .put = ad198x_mux_enum_put,
1800 },
1801 /* identical with AD1983 */
1802 {
1803 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001804 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001805 .info = ad1983_spdif_route_info,
1806 .get = ad1983_spdif_route_get,
1807 .put = ad1983_spdif_route_put,
1808 },
1809 { } /* end */
1810};
1811
Takashi Iwai498f5b12011-05-02 11:33:15 +02001812static const struct hda_verb ad1981_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001813 /* Front, HP, Mono; mute as default */
1814 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1815 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1816 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1817 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1818 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1819 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1820 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1821 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1822 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1823 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1824 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1825 /* Front, HP selectors; from Mix */
1826 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1827 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1828 /* Mono selector; from Mix */
1829 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1830 /* Mic Mixer; select Front Mic */
1831 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1832 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1833 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001834 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1835 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001836 /* Record selector: Front mic */
1837 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1838 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1839 /* SPDIF route: PCM */
1840 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1841 /* Front Pin */
1842 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1843 /* HP Pin */
1844 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1845 /* Mono Pin */
1846 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1847 /* Front & Rear Mic Pins */
1848 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1849 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1850 /* Line Pin */
1851 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1852 /* Digital Beep */
1853 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1854 /* Line-Out as Input: disabled */
1855 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1856 { } /* end */
1857};
1858
Takashi Iwai83012a72012-08-24 18:38:08 +02001859#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001860static const struct hda_amp_list ad1981_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001861 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1862 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1863 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1864 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1865 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1866 { } /* end */
1867};
1868#endif
1869
Takashi Iwai18a815d2006-03-01 19:54:39 +01001870/*
1871 * Patch for HP nx6320
1872 *
Tobin Davis18768992007-03-12 22:20:51 +01001873 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001874 * speaker output enabled _and_ mute-LED off.
1875 */
1876
1877#define AD1981_HP_EVENT 0x37
1878#define AD1981_MIC_EVENT 0x38
1879
Takashi Iwai498f5b12011-05-02 11:33:15 +02001880static const struct hda_verb ad1981_hp_init_verbs[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001881 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1882 /* pin sensing on HP and Mic jacks */
1883 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1884 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1885 {}
1886};
1887
1888/* turn on/off EAPD (+ mute HP) as a master switch */
1889static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1890 struct snd_ctl_elem_value *ucontrol)
1891{
1892 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1893 struct ad198x_spec *spec = codec->spec;
1894
1895 if (! ad198x_eapd_put(kcontrol, ucontrol))
1896 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001897 /* change speaker pin appropriately */
Takashi Iwaicdd03ce2012-04-20 12:34:50 +02001898 snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001899 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001900 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1901 HDA_AMP_MUTE,
1902 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001903 return 1;
1904}
1905
1906/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001907static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001908 .ops = &snd_hda_bind_vol,
1909 .values = {
1910 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1911 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1912 0
1913 },
1914};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001915
1916/* mute internal speaker if HP is plugged */
1917static void ad1981_hp_automute(struct hda_codec *codec)
1918{
1919 unsigned int present;
1920
Takashi Iwaid56757a2009-11-18 08:00:14 +01001921 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001922 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1923 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001924}
1925
1926/* toggle input of built-in and mic jack appropriately */
1927static void ad1981_hp_automic(struct hda_codec *codec)
1928{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001929 static const struct hda_verb mic_jack_on[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001930 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1931 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1932 {}
1933 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02001934 static const struct hda_verb mic_jack_off[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001935 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1936 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1937 {}
1938 };
1939 unsigned int present;
1940
Takashi Iwaid56757a2009-11-18 08:00:14 +01001941 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001942 if (present)
1943 snd_hda_sequence_write(codec, mic_jack_on);
1944 else
1945 snd_hda_sequence_write(codec, mic_jack_off);
1946}
1947
1948/* unsolicited event for HP jack sensing */
1949static void ad1981_hp_unsol_event(struct hda_codec *codec,
1950 unsigned int res)
1951{
1952 res >>= 26;
1953 switch (res) {
1954 case AD1981_HP_EVENT:
1955 ad1981_hp_automute(codec);
1956 break;
1957 case AD1981_MIC_EVENT:
1958 ad1981_hp_automic(codec);
1959 break;
1960 }
1961}
1962
Takashi Iwai498f5b12011-05-02 11:33:15 +02001963static const struct hda_input_mux ad1981_hp_capture_source = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001964 .num_items = 3,
1965 .items = {
1966 { "Mic", 0x0 },
David Henningssonc40bd912012-09-19 12:19:47 +02001967 { "Dock Mic", 0x1 },
Takashi Iwai18a815d2006-03-01 19:54:39 +01001968 { "Mix", 0x2 },
1969 },
1970};
1971
Takashi Iwai498f5b12011-05-02 11:33:15 +02001972static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001973 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001974 {
1975 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001976 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001977 .name = "Master Playback Switch",
1978 .info = ad198x_eapd_info,
1979 .get = ad198x_eapd_get,
1980 .put = ad1981_hp_master_sw_put,
1981 .private_value = 0x05,
1982 },
1983 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1984 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1985#if 0
1986 /* FIXME: analog mic/line loopback doesn't work with my tests...
1987 * (although recording is OK)
1988 */
1989 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1990 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
David Henningssonc40bd912012-09-19 12:19:47 +02001991 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1992 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001993 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1994 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1995 /* FIXME: does this laptop have analog CD connection? */
1996 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1997 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1998#endif
David Henningsson5f99f862011-01-04 15:24:24 +01001999 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
2000 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01002001 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
2002 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
2003 {
2004 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2005 .name = "Capture Source",
2006 .info = ad198x_mux_enum_info,
2007 .get = ad198x_mux_enum_get,
2008 .put = ad198x_mux_enum_put,
2009 },
2010 { } /* end */
2011};
2012
2013/* initialize jack-sensing, too */
2014static int ad1981_hp_init(struct hda_codec *codec)
2015{
2016 ad198x_init(codec);
2017 ad1981_hp_automute(codec);
2018 ad1981_hp_automic(codec);
2019 return 0;
2020}
2021
Tobin Davis18768992007-03-12 22:20:51 +01002022/* configuration for Toshiba Laptops */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002023static const struct hda_verb ad1981_toshiba_init_verbs[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002024 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
2025 /* pin sensing on HP and Mic jacks */
2026 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
2027 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
2028 {}
2029};
2030
Takashi Iwai498f5b12011-05-02 11:33:15 +02002031static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002032 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
2033 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
2034 { }
2035};
2036
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002037/* configuration for Lenovo Thinkpad T60 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002038static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002039 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2040 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
2041 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
2042 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
2043 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
2044 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
2045 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
2046 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01002047 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002048 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
2049 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
2050 {
2051 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2052 .name = "Capture Source",
2053 .info = ad198x_mux_enum_info,
2054 .get = ad198x_mux_enum_get,
2055 .put = ad198x_mux_enum_put,
2056 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02002057 /* identical with AD1983 */
2058 {
2059 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2060 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
2061 .info = ad1983_spdif_route_info,
2062 .get = ad1983_spdif_route_get,
2063 .put = ad1983_spdif_route_put,
2064 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002065 { } /* end */
2066};
2067
Takashi Iwai498f5b12011-05-02 11:33:15 +02002068static const struct hda_input_mux ad1981_thinkpad_capture_source = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002069 .num_items = 3,
2070 .items = {
2071 { "Mic", 0x0 },
2072 { "Mix", 0x2 },
2073 { "CD", 0x4 },
2074 },
2075};
2076
Takashi Iwai18a815d2006-03-01 19:54:39 +01002077/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002078enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002079 AD1981_AUTO,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002080 AD1981_BASIC,
2081 AD1981_HP,
2082 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01002083 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002084 AD1981_MODELS
2085};
Takashi Iwai18a815d2006-03-01 19:54:39 +01002086
Takashi Iwaiea734962011-01-17 11:29:34 +01002087static const char * const ad1981_models[AD1981_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002088 [AD1981_AUTO] = "auto",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002089 [AD1981_HP] = "hp",
2090 [AD1981_THINKPAD] = "thinkpad",
2091 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01002092 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002093};
2094
Takashi Iwai498f5b12011-05-02 11:33:15 +02002095static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002096 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02002097 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02002098 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01002099 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002100 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002101 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01002102 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002103 /* HP nx6320 (reversed SSID, H/W bug) */
2104 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01002105 {}
2106};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002107#endif /* ENABLE_AD_STATIC_QUIRKS */
2108
Takashi Iwai18a815d2006-03-01 19:54:39 +01002109
Takashi Iwaia928bd22013-01-22 18:18:42 +01002110/* follow EAPD via vmaster hook */
2111static void ad_vmaster_eapd_hook(void *private_data, int enabled)
2112{
2113 struct hda_codec *codec = private_data;
2114 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02002115
2116 if (!spec->eapd_nid)
2117 return;
Takashi Iwaia928bd22013-01-22 18:18:42 +01002118 snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
2119 AC_VERB_SET_EAPD_BTLENABLE,
2120 enabled ? 0x02 : 0x00);
2121}
2122
2123static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
2124 const struct hda_fixup *fix, int action)
2125{
2126 struct ad198x_spec *spec = codec->spec;
2127
2128 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
2129 spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
2130 spec->eapd_nid = 0x05;
2131 }
2132}
2133
2134/* set the upper-limit for mixer amp to 0dB for avoiding the possible
2135 * damage by overloading
2136 */
2137static void ad1981_fixup_amp_override(struct hda_codec *codec,
2138 const struct hda_fixup *fix, int action)
2139{
2140 if (action == HDA_FIXUP_ACT_PRE_PROBE)
2141 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2142 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2143 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2144 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2145 (1 << AC_AMPCAP_MUTE_SHIFT));
2146}
2147
2148enum {
2149 AD1981_FIXUP_AMP_OVERRIDE,
2150 AD1981_FIXUP_HP_EAPD,
2151};
2152
2153static const struct hda_fixup ad1981_fixups[] = {
2154 [AD1981_FIXUP_AMP_OVERRIDE] = {
2155 .type = HDA_FIXUP_FUNC,
2156 .v.func = ad1981_fixup_amp_override,
2157 },
2158 [AD1981_FIXUP_HP_EAPD] = {
2159 .type = HDA_FIXUP_FUNC,
2160 .v.func = ad1981_fixup_hp_eapd,
2161 .chained = true,
2162 .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
2163 },
2164};
2165
2166static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
2167 SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
2168 SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
2169 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
2170 /* HP nx6320 (reversed SSID, H/W bug) */
2171 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
2172 {}
2173};
2174
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002175static int ad1981_parse_auto_config(struct hda_codec *codec)
2176{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002177 struct ad198x_spec *spec;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002178 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002179
Takashi Iwai361dab32012-05-09 14:35:27 +02002180 err = alloc_ad_spec(codec);
2181 if (err < 0)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002182 return -ENOMEM;
Takashi Iwai361dab32012-05-09 14:35:27 +02002183 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002184
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002185 spec->gen.mixer_nid = 0x0e;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002186 spec->gen.beep_nid = 0x10;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002187 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
Takashi Iwaia928bd22013-01-22 18:18:42 +01002188
2189 snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
2190 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
2191
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002192 err = ad198x_parse_auto_config(codec);
2193 if (err < 0)
2194 goto error;
2195 err = ad1983_add_spdif_mux_ctl(codec);
2196 if (err < 0)
2197 goto error;
Takashi Iwaia928bd22013-01-22 18:18:42 +01002198
2199 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
2200
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002201 return 0;
2202
2203 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002204 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002205 return err;
2206}
2207
2208#ifdef ENABLE_AD_STATIC_QUIRKS
2209static int patch_ad1981(struct hda_codec *codec)
2210{
2211 struct ad198x_spec *spec;
2212 int err, board_config;
2213
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002214 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
2215 ad1981_models,
2216 ad1981_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01002217 if (board_config < 0) {
2218 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
2219 codec->chip_name);
2220 board_config = AD1981_AUTO;
2221 }
2222
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002223 if (board_config == AD1981_AUTO)
2224 return ad1981_parse_auto_config(codec);
2225
2226 err = alloc_ad_spec(codec);
2227 if (err < 0)
2228 return -ENOMEM;
2229 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002230
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002231 err = snd_hda_attach_beep_device(codec, 0x10);
2232 if (err < 0) {
2233 ad198x_free(codec);
2234 return err;
2235 }
2236 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
2237
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002238 spec->multiout.max_channels = 2;
2239 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
2240 spec->multiout.dac_nids = ad1981_dac_nids;
2241 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01002242 spec->num_adc_nids = 1;
2243 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002244 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002245 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01002246 spec->num_mixers = 1;
2247 spec->mixers[0] = ad1981_mixers;
2248 spec->num_init_verbs = 1;
2249 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002250 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02002251#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02002252 spec->loopback.amplist = ad1981_loopbacks;
2253#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002254 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002255
2256 codec->patch_ops = ad198x_patch_ops;
2257
Takashi Iwai18a815d2006-03-01 19:54:39 +01002258 /* override some parameters */
Takashi Iwai18a815d2006-03-01 19:54:39 +01002259 switch (board_config) {
2260 case AD1981_HP:
2261 spec->mixers[0] = ad1981_hp_mixers;
2262 spec->num_init_verbs = 2;
2263 spec->init_verbs[1] = ad1981_hp_init_verbs;
Takashi Iwai695cd4a2011-06-10 14:37:04 +02002264 if (!is_jack_available(codec, 0x0a))
2265 spec->multiout.dig_out_nid = 0;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002266 spec->input_mux = &ad1981_hp_capture_source;
2267
2268 codec->patch_ops.init = ad1981_hp_init;
2269 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05002270 /* set the upper-limit for mixer amp to 0dB for avoiding the
2271 * possible damage by overloading
2272 */
2273 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2274 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2275 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2276 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2277 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01002278 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002279 case AD1981_THINKPAD:
2280 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002281 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04002282 /* set the upper-limit for mixer amp to 0dB for avoiding the
2283 * possible damage by overloading
2284 */
2285 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2286 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2287 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2288 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2289 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002290 break;
Tobin Davis18768992007-03-12 22:20:51 +01002291 case AD1981_TOSHIBA:
2292 spec->mixers[0] = ad1981_hp_mixers;
2293 spec->mixers[1] = ad1981_toshiba_mixers;
2294 spec->num_init_verbs = 2;
2295 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
2296 spec->multiout.dig_out_nid = 0;
2297 spec->input_mux = &ad1981_hp_capture_source;
2298 codec->patch_ops.init = ad1981_hp_init;
2299 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
2300 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002301 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01002302
2303 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002304 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002305
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002306 return 0;
2307}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002308#else /* ENABLE_AD_STATIC_QUIRKS */
2309#define patch_ad1981 ad1981_parse_auto_config
2310#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002311
2312
2313/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002314 * AD1988
2315 *
2316 * Output pins and routes
2317 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002318 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002319 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
2320 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
2321 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
2322 * port-D 0x12 (mute/hp) <- 0x29 <- 04
2323 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
2324 * port-F 0x16 (mute) <- 0x2a <- 06
2325 * port-G 0x24 (mute) <- 0x27 <- 05
2326 * port-H 0x25 (mute) <- 0x28 <- 0a
2327 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
2328 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002329 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
2330 * (*) 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 +01002331 *
2332 * Input pins and routes
2333 *
2334 * pin boost mix input # / adc input #
2335 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
2336 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
2337 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
2338 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
2339 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
2340 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
2341 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
2342 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
2343 *
2344 *
2345 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01002346 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002347 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002348 *
2349 * Inputs of Analog Mix (0x20)
2350 * 0:Port-B (front mic)
2351 * 1:Port-C/G/H (line-in)
2352 * 2:Port-A
2353 * 3:Port-D (line-in/2)
2354 * 4:Port-E/G/H (mic-in)
2355 * 5:Port-F (mic2-in)
2356 * 6:CD
2357 * 7:Beep
2358 *
2359 * ADC selection
2360 * 0:Port-A
2361 * 1:Port-B (front mic-in)
2362 * 2:Port-C (line-in)
2363 * 3:Port-F (mic2-in)
2364 * 4:Port-E (mic-in)
2365 * 5:CD
2366 * 6:Port-G
2367 * 7:Port-H
2368 * 8:Port-D (line-in/2)
2369 * 9:Mix
2370 *
2371 * Proposed pin assignments by the datasheet
2372 *
2373 * 6-stack
2374 * Port-A front headphone
2375 * B front mic-in
2376 * C rear line-in
2377 * D rear front-out
2378 * E rear mic-in
2379 * F rear surround
2380 * G rear CLFE
2381 * H rear side
2382 *
2383 * 3-stack
2384 * Port-A front headphone
2385 * B front mic
2386 * C rear line-in/surround
2387 * D rear front-out
2388 * E rear mic-in/CLFE
2389 *
2390 * laptop
2391 * Port-A headphone
2392 * B mic-in
2393 * C docking station
2394 * D internal speaker (with EAPD)
2395 * E/F quad mic array
2396 */
2397
2398
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002399#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002400/* models */
2401enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002402 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002403 AD1988_6STACK,
2404 AD1988_6STACK_DIG,
2405 AD1988_3STACK,
2406 AD1988_3STACK_DIG,
2407 AD1988_LAPTOP,
2408 AD1988_LAPTOP_DIG,
2409 AD1988_MODEL_LAST,
2410};
2411
Takashi Iwaid32410b12005-11-24 16:06:23 +01002412/* reivision id to check workarounds */
2413#define AD1988A_REV2 0x100200
2414
Takashi Iwai1a806f42006-07-03 15:58:16 +02002415#define is_rev2(codec) \
2416 ((codec)->vendor_id == 0x11d41988 && \
2417 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002418
2419/*
2420 * mixers
2421 */
2422
Takashi Iwai498f5b12011-05-02 11:33:15 +02002423static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002424 0x04, 0x06, 0x05, 0x0a
2425};
2426
Takashi Iwai498f5b12011-05-02 11:33:15 +02002427static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002428 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002429};
2430
2431/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002432static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002433 0x04, 0x05, 0x0a, 0x06
2434};
2435
Takashi Iwai498f5b12011-05-02 11:33:15 +02002436static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002437 0x03
2438};
2439
Takashi Iwai498f5b12011-05-02 11:33:15 +02002440static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002441 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002442};
2443
Takashi Iwai498f5b12011-05-02 11:33:15 +02002444static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002445 0x08, 0x09, 0x0f
2446};
2447
Takashi Iwai498f5b12011-05-02 11:33:15 +02002448static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002449 0x0c, 0x0d, 0x0e
2450};
2451
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002452#define AD1988_SPDIF_OUT 0x02
2453#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002454#define AD1988_SPDIF_IN 0x07
2455
Takashi Iwai498f5b12011-05-02 11:33:15 +02002456static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01002457 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002458};
2459
Takashi Iwai498f5b12011-05-02 11:33:15 +02002460static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002461 .num_items = 5,
2462 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002463 { "Front Mic", 0x1 }, /* port-B */
2464 { "Line", 0x2 }, /* port-C */
2465 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002466 { "CD", 0x5 },
2467 { "Mix", 0x9 },
2468 },
2469};
2470
Takashi Iwai498f5b12011-05-02 11:33:15 +02002471static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002472 .num_items = 3,
2473 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002474 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002475 { "CD", 0x5 },
2476 { "Mix", 0x9 },
2477 },
2478};
2479
2480/*
2481 */
2482static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2483 struct snd_ctl_elem_info *uinfo)
2484{
2485 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2486 struct ad198x_spec *spec = codec->spec;
2487 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2488 spec->num_channel_mode);
2489}
2490
2491static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2492 struct snd_ctl_elem_value *ucontrol)
2493{
2494 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2495 struct ad198x_spec *spec = codec->spec;
2496 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2497 spec->num_channel_mode, spec->multiout.max_channels);
2498}
2499
2500static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2501 struct snd_ctl_elem_value *ucontrol)
2502{
2503 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2504 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002505 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2506 spec->num_channel_mode,
2507 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002508 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002509 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002510 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002511}
2512
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002513/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002514static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002515 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2516 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2517 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2518 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2519 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002520 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002521};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002522
Takashi Iwai498f5b12011-05-02 11:33:15 +02002523static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002524 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2525 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2526 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2527 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2528 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002529 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002530};
2531
Takashi Iwai498f5b12011-05-02 11:33:15 +02002532static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002533 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002534 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2535 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2536 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2537 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2538 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2539 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2540 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2541
2542 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2543 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2544 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2545 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2546 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2547 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2548 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2549 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2550
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002551 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002552 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2553
David Henningsson5f99f862011-01-04 15:24:24 +01002554 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2555 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002556 { } /* end */
2557};
2558
2559/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002560static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002561 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002562 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002563 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2564 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002565 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002566};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002567
Takashi Iwai498f5b12011-05-02 11:33:15 +02002568static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002569 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002570 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2571 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2572 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002573 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002574};
2575
Takashi Iwai498f5b12011-05-02 11:33:15 +02002576static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002577 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002578 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002579 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2580 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2581 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002582 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2583 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2584
2585 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2586 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2587 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2588 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2589 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2590 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2591 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2592 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2593
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002594 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002595 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2596
David Henningsson5f99f862011-01-04 15:24:24 +01002597 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2598 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002599 {
2600 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2601 .name = "Channel Mode",
2602 .info = ad198x_ch_mode_info,
2603 .get = ad198x_ch_mode_get,
2604 .put = ad198x_ch_mode_put,
2605 },
2606
2607 { } /* end */
2608};
2609
2610/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002611static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002612 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002613 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2614 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2615 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2616
2617 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2618 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2619 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2620 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2621 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2622 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2623
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002624 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002625 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2626
David Henningsson5f99f862011-01-04 15:24:24 +01002627 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002628
2629 {
2630 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2631 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002632 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002633 .info = ad198x_eapd_info,
2634 .get = ad198x_eapd_get,
2635 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002636 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002637 },
2638
2639 { } /* end */
2640};
2641
2642/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002643static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002644 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2645 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2646 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2647 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2648 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2649 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2650 {
2651 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2652 /* The multiple "Capture Source" controls confuse alsamixer
2653 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002654 */
2655 /* .name = "Capture Source", */
2656 .name = "Input Source",
2657 .count = 3,
2658 .info = ad198x_mux_enum_info,
2659 .get = ad198x_mux_enum_get,
2660 .put = ad198x_mux_enum_put,
2661 },
2662 { } /* end */
2663};
2664
2665static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2666 struct snd_ctl_elem_info *uinfo)
2667{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002668 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002669 "PCM", "ADC1", "ADC2", "ADC3"
2670 };
2671 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2672 uinfo->count = 1;
2673 uinfo->value.enumerated.items = 4;
2674 if (uinfo->value.enumerated.item >= 4)
2675 uinfo->value.enumerated.item = 3;
2676 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2677 return 0;
2678}
2679
2680static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2681 struct snd_ctl_elem_value *ucontrol)
2682{
2683 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2684 unsigned int sel;
2685
Takashi Iwaibddcf542007-07-24 18:04:05 +02002686 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2687 AC_AMP_GET_INPUT);
2688 if (!(sel & 0x80))
2689 ucontrol->value.enumerated.item[0] = 0;
2690 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002691 sel = snd_hda_codec_read(codec, 0x0b, 0,
2692 AC_VERB_GET_CONNECT_SEL, 0);
2693 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002694 sel++;
2695 else
2696 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002697 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002698 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002699 return 0;
2700}
2701
2702static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2703 struct snd_ctl_elem_value *ucontrol)
2704{
2705 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002706 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002707 int change;
2708
Takashi Iwai35b26722007-05-05 12:17:17 +02002709 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002710 if (val > 3)
2711 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002712 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002713 sel = snd_hda_codec_read(codec, 0x1d, 0,
2714 AC_VERB_GET_AMP_GAIN_MUTE,
2715 AC_AMP_GET_INPUT);
2716 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002717 if (change) {
2718 snd_hda_codec_write_cache(codec, 0x1d, 0,
2719 AC_VERB_SET_AMP_GAIN_MUTE,
2720 AMP_IN_UNMUTE(0));
2721 snd_hda_codec_write_cache(codec, 0x1d, 0,
2722 AC_VERB_SET_AMP_GAIN_MUTE,
2723 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002724 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002725 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002726 sel = snd_hda_codec_read(codec, 0x1d, 0,
2727 AC_VERB_GET_AMP_GAIN_MUTE,
2728 AC_AMP_GET_INPUT | 0x01);
2729 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002730 if (change) {
2731 snd_hda_codec_write_cache(codec, 0x1d, 0,
2732 AC_VERB_SET_AMP_GAIN_MUTE,
2733 AMP_IN_MUTE(0));
2734 snd_hda_codec_write_cache(codec, 0x1d, 0,
2735 AC_VERB_SET_AMP_GAIN_MUTE,
2736 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002737 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002738 sel = snd_hda_codec_read(codec, 0x0b, 0,
2739 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2740 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002741 if (change)
2742 snd_hda_codec_write_cache(codec, 0x0b, 0,
2743 AC_VERB_SET_CONNECT_SEL,
2744 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002745 }
2746 return change;
2747}
2748
Takashi Iwai498f5b12011-05-02 11:33:15 +02002749static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002750 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2751 {
2752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2753 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002754 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002755 .info = ad1988_spdif_playback_source_info,
2756 .get = ad1988_spdif_playback_source_get,
2757 .put = ad1988_spdif_playback_source_put,
2758 },
2759 { } /* end */
2760};
2761
Takashi Iwai498f5b12011-05-02 11:33:15 +02002762static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002763 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2764 { } /* end */
2765};
2766
Takashi Iwai498f5b12011-05-02 11:33:15 +02002767static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002768 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002769 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002770 { } /* end */
2771};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002772
2773/*
2774 * initialization verbs
2775 */
2776
2777/*
2778 * for 6-stack (+dig)
2779 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002780static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002781 /* Front, Surround, CLFE, side DAC; unmute as default */
2782 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2783 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2784 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2785 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002786 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002787 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002788 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2789 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2790 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2791 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2792 /* Port-D line-out path */
2793 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2794 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2795 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2796 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2797 /* Port-F surround path */
2798 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2799 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2800 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2801 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2802 /* Port-G CLFE path */
2803 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2804 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2805 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2806 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2807 /* Port-H side path */
2808 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2809 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2810 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2811 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2812 /* Mono out path */
2813 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2814 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2815 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2816 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2817 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2818 /* Port-B front mic-in path */
2819 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2820 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2821 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2822 /* Port-C line-in path */
2823 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2824 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2825 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2826 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2827 /* Port-E mic-in path */
2828 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2829 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2830 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2831 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002832 /* Analog CD Input */
2833 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002834 /* Analog Mix output amp */
2835 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002836
2837 { }
2838};
2839
Takashi Iwai498f5b12011-05-02 11:33:15 +02002840static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002841 /* Headphone; unmute as default */
2842 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2843 /* Port-A front headphon path */
2844 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2845 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2846 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2847 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2848 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002849
2850 { }
2851};
2852
Takashi Iwai498f5b12011-05-02 11:33:15 +02002853static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002854 /* mute analog mix */
2855 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2856 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2857 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2858 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2859 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2860 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2861 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2862 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2863 /* select ADCs - front-mic */
2864 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2865 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2866 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002867
2868 { }
2869};
2870
Takashi Iwai498f5b12011-05-02 11:33:15 +02002871static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002872 /* SPDIF out sel */
2873 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2874 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2875 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002876 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002877 /* SPDIF out pin */
2878 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002879
2880 { }
2881};
2882
Takashi Iwai498f5b12011-05-02 11:33:15 +02002883static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002884 /* unmute SPDIF input pin */
2885 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2886 { }
2887};
2888
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002889/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002890static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002891 /* SPDIF-1 out pin */
2892 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002893 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002894 /* SPDIF-2/HDMI out pin */
2895 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2896 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002897 { }
2898};
2899
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002900/*
2901 * verbs for 3stack (+dig)
2902 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002903static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002904 /* set port-C to line-in */
2905 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2906 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2907 /* set port-E to mic-in */
2908 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2909 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2910 { } /* end */
2911};
2912
Takashi Iwai498f5b12011-05-02 11:33:15 +02002913static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002914 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002915 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002916 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002917 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002918 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002919 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002920 { } /* end */
2921};
2922
Takashi Iwai498f5b12011-05-02 11:33:15 +02002923static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002924 { 2, ad1988_3stack_ch2_init },
2925 { 6, ad1988_3stack_ch6_init },
2926};
2927
Takashi Iwai498f5b12011-05-02 11:33:15 +02002928static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002929 /* Front, Surround, CLFE, side DAC; unmute as default */
2930 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2931 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2932 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2933 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002934 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002935 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002936 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2937 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2938 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2939 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2940 /* Port-D line-out path */
2941 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2942 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2943 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2944 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2945 /* Mono out path */
2946 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2947 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2948 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2949 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2950 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2951 /* Port-B front mic-in path */
2952 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2953 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2954 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002955 /* Port-C line-in/surround path - 6ch mode as default */
2956 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2957 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002958 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002959 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002960 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002961 /* Port-E mic-in/CLFE path - 6ch mode as default */
2962 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2963 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002964 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002965 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002966 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2967 /* mute analog mix */
2968 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2969 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2970 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2971 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2972 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2973 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2974 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2975 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2976 /* select ADCs - front-mic */
2977 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2978 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2979 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002980 /* Analog Mix output amp */
2981 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002982 { }
2983};
2984
2985/*
2986 * verbs for laptop mode (+dig)
2987 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002988static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002989 /* unmute port-A and mute port-D */
2990 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2991 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2992 { } /* end */
2993};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002994static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002995 /* mute port-A and unmute port-D */
2996 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2997 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2998 { } /* end */
2999};
3000
3001#define AD1988_HP_EVENT 0x01
3002
Takashi Iwai498f5b12011-05-02 11:33:15 +02003003static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01003004 /* Front, Surround, CLFE, side DAC; unmute as default */
3005 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3006 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3007 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3008 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003009 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08003010 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003011 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3012 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3013 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3014 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3015 /* unsolicited event for pin-sense */
3016 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
3017 /* Port-D line-out path + EAPD */
3018 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3019 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3020 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3021 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3022 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
3023 /* Mono out path */
3024 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
3025 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3026 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3027 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3028 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
3029 /* Port-B mic-in path */
3030 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3031 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3032 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3033 /* Port-C docking station - try to output */
3034 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3035 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3036 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3037 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
3038 /* mute analog mix */
3039 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3040 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3041 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3042 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3043 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3044 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3045 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
3046 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
3047 /* select ADCs - mic */
3048 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
3049 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
3050 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02003051 /* Analog Mix output amp */
3052 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003053 { }
3054};
3055
3056static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
3057{
3058 if ((res >> 26) != AD1988_HP_EVENT)
3059 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003060 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003061 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
3062 else
3063 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
3064}
3065
Takashi Iwai83012a72012-08-24 18:38:08 +02003066#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02003067static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003068 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3069 { 0x20, HDA_INPUT, 1 }, /* Line */
3070 { 0x20, HDA_INPUT, 4 }, /* Mic */
3071 { 0x20, HDA_INPUT, 6 }, /* CD */
3072 { } /* end */
3073};
3074#endif
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003075#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003076
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003077static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
3078 struct snd_ctl_elem_info *uinfo)
3079{
3080 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3081 static const char * const texts[] = {
3082 "PCM", "ADC1", "ADC2", "ADC3",
3083 };
3084 int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
3085 if (num_conns > 4)
3086 num_conns = 4;
3087 return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
3088}
3089
3090static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
3091 struct snd_ctl_elem_value *ucontrol)
3092{
3093 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3094 struct ad198x_spec *spec = codec->spec;
3095
3096 ucontrol->value.enumerated.item[0] = spec->cur_smux;
3097 return 0;
3098}
3099
3100static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
3101 struct snd_ctl_elem_value *ucontrol)
3102{
3103 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3104 struct ad198x_spec *spec = codec->spec;
3105 unsigned int val = ucontrol->value.enumerated.item[0];
3106 struct nid_path *path;
3107 int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
3108
3109 if (val >= num_conns)
3110 return -EINVAL;
3111 if (spec->cur_smux == val)
3112 return 0;
3113
3114 mutex_lock(&codec->control_mutex);
3115 codec->cached_write = 1;
3116 path = snd_hda_get_path_from_idx(codec,
3117 spec->smux_paths[spec->cur_smux]);
3118 if (path)
3119 snd_hda_activate_path(codec, path, false, true);
3120 path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
3121 if (path)
3122 snd_hda_activate_path(codec, path, true, true);
3123 spec->cur_smux = val;
3124 codec->cached_write = 0;
3125 mutex_unlock(&codec->control_mutex);
3126 snd_hda_codec_flush_cache(codec); /* flush the updates */
3127 return 1;
3128}
3129
3130static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
3131 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3132 .name = "IEC958 Playback Source",
3133 .info = ad1988_auto_smux_enum_info,
3134 .get = ad1988_auto_smux_enum_get,
3135 .put = ad1988_auto_smux_enum_put,
3136};
3137
3138static int ad1988_auto_init(struct hda_codec *codec)
3139{
3140 struct ad198x_spec *spec = codec->spec;
3141 int i, err;
3142
3143 err = snd_hda_gen_init(codec);
3144 if (err < 0)
3145 return err;
3146 if (!spec->gen.autocfg.dig_outs)
3147 return 0;
3148
3149 for (i = 0; i < 4; i++) {
3150 struct nid_path *path;
3151 path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
3152 if (path)
3153 snd_hda_activate_path(codec, path, path->active, false);
3154 }
3155
3156 return 0;
3157}
3158
3159static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
3160{
3161 struct ad198x_spec *spec = codec->spec;
3162 int i, num_conns;
3163 /* we create four static faked paths, since AD codecs have odd
3164 * widget connections regarding the SPDIF out source
3165 */
3166 static struct nid_path fake_paths[4] = {
3167 {
3168 .depth = 3,
3169 .path = { 0x02, 0x1d, 0x1b },
3170 .idx = { 0, 0, 0 },
3171 .multi = { 0, 0, 0 },
3172 },
3173 {
3174 .depth = 4,
3175 .path = { 0x08, 0x0b, 0x1d, 0x1b },
3176 .idx = { 0, 0, 1, 0 },
3177 .multi = { 0, 1, 0, 0 },
3178 },
3179 {
3180 .depth = 4,
3181 .path = { 0x09, 0x0b, 0x1d, 0x1b },
3182 .idx = { 0, 1, 1, 0 },
3183 .multi = { 0, 1, 0, 0 },
3184 },
3185 {
3186 .depth = 4,
3187 .path = { 0x0f, 0x0b, 0x1d, 0x1b },
3188 .idx = { 0, 2, 1, 0 },
3189 .multi = { 0, 1, 0, 0 },
3190 },
3191 };
3192
3193 /* SPDIF source mux appears to be present only on AD1988A */
3194 if (!spec->gen.autocfg.dig_outs ||
3195 get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
3196 return 0;
3197
3198 num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
3199 if (num_conns != 3 && num_conns != 4)
3200 return 0;
3201
3202 for (i = 0; i < num_conns; i++) {
3203 struct nid_path *path = snd_array_new(&spec->gen.paths);
3204 if (!path)
3205 return -ENOMEM;
3206 *path = fake_paths[i];
3207 if (!i)
3208 path->active = 1;
3209 spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
3210 }
3211
3212 if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
3213 return -ENOMEM;
3214
3215 codec->patch_ops.init = ad1988_auto_init;
3216
3217 return 0;
3218}
3219
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003220/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01003221 */
3222
Takashi Iwaid32410b12005-11-24 16:06:23 +01003223static int ad1988_parse_auto_config(struct hda_codec *codec)
3224{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003225 struct ad198x_spec *spec;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003226 int err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003227
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003228 err = alloc_ad_spec(codec);
3229 if (err < 0)
3230 return err;
3231 spec = codec->spec;
3232
Takashi Iwaif2f8be42013-01-21 16:40:16 +01003233 spec->gen.mixer_nid = 0x20;
Takashi Iwaie4a395e2013-01-23 17:00:31 +01003234 spec->gen.mixer_merge_nid = 0x21;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01003235 spec->gen.beep_nid = 0x10;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003236 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003237 err = ad198x_parse_auto_config(codec);
3238 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003239 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003240 err = ad1988_add_spdif_mux_ctl(codec);
3241 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003242 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003243 return 0;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003244
3245 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01003246 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003247 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003248}
3249
Takashi Iwaid32410b12005-11-24 16:06:23 +01003250/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003251 */
3252
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003253#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwaiea734962011-01-17 11:29:34 +01003254static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003255 [AD1988_6STACK] = "6stack",
3256 [AD1988_6STACK_DIG] = "6stack-dig",
3257 [AD1988_3STACK] = "3stack",
3258 [AD1988_3STACK_DIG] = "3stack-dig",
3259 [AD1988_LAPTOP] = "laptop",
3260 [AD1988_LAPTOP_DIG] = "laptop-dig",
3261 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003262};
3263
Takashi Iwai498f5b12011-05-02 11:33:15 +02003264static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003265 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003266 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003267 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01003268 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003269 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003270 {}
3271};
3272
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003273static int patch_ad1988(struct hda_codec *codec)
3274{
3275 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003276 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003277
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003278 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003279 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003280 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003281 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3282 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003283 board_config = AD1988_AUTO;
3284 }
3285
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003286 if (board_config == AD1988_AUTO)
3287 return ad1988_parse_auto_config(codec);
3288
3289 err = alloc_ad_spec(codec);
3290 if (err < 0)
3291 return err;
3292 spec = codec->spec;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003293
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003294 if (is_rev2(codec))
3295 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3296
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003297 err = snd_hda_attach_beep_device(codec, 0x10);
3298 if (err < 0) {
3299 ad198x_free(codec);
3300 return err;
3301 }
3302 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3303
Raymond Yau356aab72011-08-31 10:30:59 +08003304 if (!spec->multiout.hp_nid)
Raymond Yau34588702011-09-23 19:03:25 +08003305 spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003306 switch (board_config) {
3307 case AD1988_6STACK:
3308 case AD1988_6STACK_DIG:
3309 spec->multiout.max_channels = 8;
3310 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003311 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003312 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3313 else
3314 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003315 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003316 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003317 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003318 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3319 else
3320 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08003321 spec->mixers[1] = ad1988_6stack_mixers2;
3322 spec->num_init_verbs = 1;
3323 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yau34588702011-09-23 19:03:25 +08003324 if (board_config == AD1988_6STACK_DIG) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003325 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3326 spec->dig_in_nid = AD1988_SPDIF_IN;
3327 }
3328 break;
3329 case AD1988_3STACK:
3330 case AD1988_3STACK_DIG:
3331 spec->multiout.max_channels = 6;
3332 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003333 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003334 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3335 else
3336 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003337 spec->input_mux = &ad1988_6stack_capture_source;
3338 spec->channel_mode = ad1988_3stack_modes;
3339 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003340 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003341 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003342 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3343 else
3344 spec->mixers[0] = ad1988_3stack_mixers1;
3345 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003346 spec->num_init_verbs = 1;
3347 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3348 if (board_config == AD1988_3STACK_DIG)
3349 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3350 break;
3351 case AD1988_LAPTOP:
3352 case AD1988_LAPTOP_DIG:
3353 spec->multiout.max_channels = 2;
3354 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003355 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003356 spec->input_mux = &ad1988_laptop_capture_source;
3357 spec->num_mixers = 1;
3358 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003359 codec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003360 spec->num_init_verbs = 1;
3361 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3362 if (board_config == AD1988_LAPTOP_DIG)
3363 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3364 break;
3365 }
3366
Takashi Iwaid32410b12005-11-24 16:06:23 +01003367 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3368 spec->adc_nids = ad1988_adc_nids;
3369 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003370 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3371 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3372 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003373 if (codec->vendor_id >= 0x11d4989a) {
3374 spec->mixers[spec->num_mixers++] =
3375 ad1989_spdif_out_mixers;
3376 spec->init_verbs[spec->num_init_verbs++] =
3377 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003378 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003379 } else {
3380 spec->mixers[spec->num_mixers++] =
3381 ad1988_spdif_out_mixers;
3382 spec->init_verbs[spec->num_init_verbs++] =
3383 ad1988_spdif_init_verbs;
3384 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003385 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003386 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003387 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003388 spec->init_verbs[spec->num_init_verbs++] =
3389 ad1988_spdif_in_init_verbs;
3390 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003391
3392 codec->patch_ops = ad198x_patch_ops;
3393 switch (board_config) {
3394 case AD1988_LAPTOP:
3395 case AD1988_LAPTOP_DIG:
3396 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3397 break;
3398 }
Takashi Iwai83012a72012-08-24 18:38:08 +02003399#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02003400 spec->loopback.amplist = ad1988_loopbacks;
3401#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003402 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003403
Takashi Iwai729d55b2009-12-25 22:49:01 +01003404 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003405 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003406
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003407 return 0;
3408}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003409#else /* ENABLE_AD_STATIC_QUIRKS */
3410#define patch_ad1988 ad1988_parse_auto_config
3411#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003412
3413
3414/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003415 * AD1884 / AD1984
3416 *
3417 * port-B - front line/mic-in
3418 * port-E - aux in/out
3419 * port-F - aux in/out
3420 * port-C - rear line/mic-in
3421 * port-D - rear line/hp-out
3422 * port-A - front line/hp-out
3423 *
3424 * AD1984 = AD1884 + two digital mic-ins
3425 *
3426 * FIXME:
3427 * For simplicity, we share the single DAC for both HP and line-outs
3428 * right now. The inidividual playbacks could be easily implemented,
3429 * but no build-up framework is given, so far.
3430 */
3431
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003432#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +02003433static const hda_nid_t ad1884_dac_nids[1] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003434 0x04,
3435};
3436
Takashi Iwai498f5b12011-05-02 11:33:15 +02003437static const hda_nid_t ad1884_adc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003438 0x08, 0x09,
3439};
3440
Takashi Iwai498f5b12011-05-02 11:33:15 +02003441static const hda_nid_t ad1884_capsrc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003442 0x0c, 0x0d,
3443};
3444
3445#define AD1884_SPDIF_OUT 0x02
3446
Takashi Iwai498f5b12011-05-02 11:33:15 +02003447static const struct hda_input_mux ad1884_capture_source = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003448 .num_items = 4,
3449 .items = {
3450 { "Front Mic", 0x0 },
3451 { "Mic", 0x1 },
3452 { "CD", 0x2 },
3453 { "Mix", 0x3 },
3454 },
3455};
3456
Takashi Iwai498f5b12011-05-02 11:33:15 +02003457static const struct snd_kcontrol_new ad1884_base_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003458 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3459 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3460 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3461 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3462 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3463 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3464 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3465 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3466 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3467 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3468 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3469 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003470 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3471 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003472 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3473 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3474 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3475 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3476 {
3477 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3478 /* The multiple "Capture Source" controls confuse alsamixer
3479 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003480 */
3481 /* .name = "Capture Source", */
3482 .name = "Input Source",
3483 .count = 2,
3484 .info = ad198x_mux_enum_info,
3485 .get = ad198x_mux_enum_get,
3486 .put = ad198x_mux_enum_put,
3487 },
3488 /* SPDIF controls */
3489 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3490 {
3491 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3492 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3493 /* identical with ad1983 */
3494 .info = ad1983_spdif_route_info,
3495 .get = ad1983_spdif_route_get,
3496 .put = ad1983_spdif_route_put,
3497 },
3498 { } /* end */
3499};
3500
Takashi Iwai498f5b12011-05-02 11:33:15 +02003501static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003502 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3503 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3504 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003505 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003506 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003507 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003508 { } /* end */
3509};
3510
3511/*
3512 * initialization verbs
3513 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003514static const struct hda_verb ad1884_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003515 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003516 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3517 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003518 /* Port-A (HP) mixer */
3519 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3520 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3521 /* Port-A pin */
3522 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3523 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3524 /* HP selector - select DAC2 */
3525 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3526 /* Port-D (Line-out) mixer */
3527 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3528 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3529 /* Port-D pin */
3530 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3531 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3532 /* Mono-out mixer */
3533 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3534 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3535 /* Mono-out pin */
3536 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3537 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3538 /* Mono selector */
3539 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3540 /* Port-B (front mic) pin */
3541 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003542 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003543 /* Port-C (rear mic) pin */
3544 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003545 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003546 /* Analog mixer; mute as default */
3547 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3548 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3549 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3550 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3551 /* Analog Mix output amp */
3552 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3553 /* SPDIF output selector */
3554 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3555 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3556 { } /* end */
3557};
3558
Takashi Iwai83012a72012-08-24 18:38:08 +02003559#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02003560static const struct hda_amp_list ad1884_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003561 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3562 { 0x20, HDA_INPUT, 1 }, /* Mic */
3563 { 0x20, HDA_INPUT, 2 }, /* CD */
3564 { 0x20, HDA_INPUT, 4 }, /* Docking */
3565 { } /* end */
3566};
3567#endif
3568
Takashi Iwaiea734962011-01-17 11:29:34 +01003569static const char * const ad1884_slave_vols[] = {
Takashi Iwai9322ca52012-02-03 14:28:01 +01003570 "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD",
David Henningssonc40bd912012-09-19 12:19:47 +02003571 "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003572 NULL
3573};
3574
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003575enum {
3576 AD1884_AUTO,
3577 AD1884_BASIC,
3578 AD1884_MODELS
3579};
3580
3581static const char * const ad1884_models[AD1884_MODELS] = {
3582 [AD1884_AUTO] = "auto",
3583 [AD1884_BASIC] = "basic",
3584};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003585#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003586
Takashi Iwaia928bd22013-01-22 18:18:42 +01003587
3588/* set the upper-limit for mixer amp to 0dB for avoiding the possible
3589 * damage by overloading
3590 */
3591static void ad1884_fixup_amp_override(struct hda_codec *codec,
3592 const struct hda_fixup *fix, int action)
3593{
3594 if (action == HDA_FIXUP_ACT_PRE_PROBE)
3595 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
3596 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3597 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3598 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3599 (1 << AC_AMPCAP_MUTE_SHIFT));
3600}
3601
Takashi Iwai1a39b5e2013-07-04 14:32:16 +02003602/* toggle GPIO1 according to the mute state */
3603static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
3604{
3605 struct hda_codec *codec = private_data;
3606 struct ad198x_spec *spec = codec->spec;
3607
3608 if (spec->eapd_nid)
3609 ad_vmaster_eapd_hook(private_data, enabled);
3610 snd_hda_codec_update_cache(codec, 0x01, 0,
3611 AC_VERB_SET_GPIO_DATA,
3612 enabled ? 0x00 : 0x02);
3613}
3614
Takashi Iwaia928bd22013-01-22 18:18:42 +01003615static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
3616 const struct hda_fixup *fix, int action)
3617{
3618 struct ad198x_spec *spec = codec->spec;
Takashi Iwai1a39b5e2013-07-04 14:32:16 +02003619 static const struct hda_verb gpio_init_verbs[] = {
3620 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
3621 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
3622 {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
3623 {},
3624 };
Takashi Iwaia928bd22013-01-22 18:18:42 +01003625
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02003626 switch (action) {
3627 case HDA_FIXUP_ACT_PRE_PROBE:
Takashi Iwai1a39b5e2013-07-04 14:32:16 +02003628 spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
3629 snd_hda_sequence_write_cache(codec, gpio_init_verbs);
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02003630 break;
3631 case HDA_FIXUP_ACT_PROBE:
Takashi Iwaia928bd22013-01-22 18:18:42 +01003632 if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
3633 spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
3634 else
3635 spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02003636 break;
Takashi Iwaia928bd22013-01-22 18:18:42 +01003637 }
3638}
3639
Takashi Iwai6a699be2013-07-04 14:45:37 +02003640/* set magic COEFs for dmic */
3641static const struct hda_verb ad1884_dmic_init_verbs[] = {
3642 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
3643 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
3644 {}
3645};
3646
Takashi Iwaia928bd22013-01-22 18:18:42 +01003647enum {
3648 AD1884_FIXUP_AMP_OVERRIDE,
3649 AD1884_FIXUP_HP_EAPD,
Takashi Iwai6a699be2013-07-04 14:45:37 +02003650 AD1884_FIXUP_DMIC_COEF,
Takashi Iwaif4046272013-07-04 15:14:17 +02003651 AD1884_FIXUP_HP_TOUCHSMART,
Takashi Iwaia928bd22013-01-22 18:18:42 +01003652};
3653
3654static const struct hda_fixup ad1884_fixups[] = {
3655 [AD1884_FIXUP_AMP_OVERRIDE] = {
3656 .type = HDA_FIXUP_FUNC,
3657 .v.func = ad1884_fixup_amp_override,
3658 },
3659 [AD1884_FIXUP_HP_EAPD] = {
3660 .type = HDA_FIXUP_FUNC,
3661 .v.func = ad1884_fixup_hp_eapd,
3662 .chained = true,
3663 .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
3664 },
Takashi Iwai6a699be2013-07-04 14:45:37 +02003665 [AD1884_FIXUP_DMIC_COEF] = {
3666 .type = HDA_FIXUP_VERBS,
3667 .v.verbs = ad1884_dmic_init_verbs,
3668 },
Takashi Iwaif4046272013-07-04 15:14:17 +02003669 [AD1884_FIXUP_HP_TOUCHSMART] = {
3670 .type = HDA_FIXUP_VERBS,
3671 .v.verbs = ad1884_dmic_init_verbs,
3672 .chained = true,
3673 .chain_id = AD1884_FIXUP_HP_EAPD,
3674 },
Takashi Iwaia928bd22013-01-22 18:18:42 +01003675};
3676
3677static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
Takashi Iwaif4046272013-07-04 15:14:17 +02003678 SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
Takashi Iwaia928bd22013-01-22 18:18:42 +01003679 SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
Takashi Iwai6a699be2013-07-04 14:45:37 +02003680 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF),
Takashi Iwaia928bd22013-01-22 18:18:42 +01003681 {}
3682};
3683
3684
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003685static int ad1884_parse_auto_config(struct hda_codec *codec)
3686{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003687 struct ad198x_spec *spec;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003688 int err;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003689
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003690 err = alloc_ad_spec(codec);
3691 if (err < 0)
3692 return err;
3693 spec = codec->spec;
3694
Takashi Iwaif2f8be42013-01-21 16:40:16 +01003695 spec->gen.mixer_nid = 0x20;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01003696 spec->gen.beep_nid = 0x10;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003697 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
Takashi Iwaia928bd22013-01-22 18:18:42 +01003698
3699 snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
3700 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
3701
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003702 err = ad198x_parse_auto_config(codec);
3703 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003704 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003705 err = ad1983_add_spdif_mux_ctl(codec);
3706 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003707 goto error;
Takashi Iwaia928bd22013-01-22 18:18:42 +01003708
3709 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
3710
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003711 return 0;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003712
3713 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01003714 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003715 return err;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003716}
3717
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003718#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003719static int patch_ad1884_basic(struct hda_codec *codec)
Takashi Iwai2bac6472007-05-18 18:21:41 +02003720{
3721 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003722 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003723
Takashi Iwai361dab32012-05-09 14:35:27 +02003724 err = alloc_ad_spec(codec);
3725 if (err < 0)
3726 return err;
3727 spec = codec->spec;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003728
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003729 err = snd_hda_attach_beep_device(codec, 0x10);
3730 if (err < 0) {
3731 ad198x_free(codec);
3732 return err;
3733 }
3734 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3735
Takashi Iwai2bac6472007-05-18 18:21:41 +02003736 spec->multiout.max_channels = 2;
3737 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3738 spec->multiout.dac_nids = ad1884_dac_nids;
3739 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3740 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3741 spec->adc_nids = ad1884_adc_nids;
3742 spec->capsrc_nids = ad1884_capsrc_nids;
3743 spec->input_mux = &ad1884_capture_source;
3744 spec->num_mixers = 1;
3745 spec->mixers[0] = ad1884_base_mixers;
3746 spec->num_init_verbs = 1;
3747 spec->init_verbs[0] = ad1884_init_verbs;
3748 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02003749#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02003750 spec->loopback.amplist = ad1884_loopbacks;
3751#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003752 spec->vmaster_nid = 0x04;
3753 /* we need to cover all playback volumes */
3754 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai18478e82012-03-09 17:51:10 +01003755 /* slaves may contain input volumes, so we can't raise to 0dB blindly */
3756 spec->avoid_init_slave_vol = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003757
3758 codec->patch_ops = ad198x_patch_ops;
3759
Takashi Iwai729d55b2009-12-25 22:49:01 +01003760 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003761 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003762
Takashi Iwai2bac6472007-05-18 18:21:41 +02003763 return 0;
3764}
3765
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003766static int patch_ad1884(struct hda_codec *codec)
3767{
3768 int board_config;
3769
3770 board_config = snd_hda_check_board_config(codec, AD1884_MODELS,
3771 ad1884_models, NULL);
Takashi Iwai657e1b92013-01-22 18:42:39 +01003772 if (board_config < 0) {
3773 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3774 codec->chip_name);
3775 board_config = AD1884_AUTO;
3776 }
3777
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003778 if (board_config == AD1884_AUTO)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003779 return ad1884_parse_auto_config(codec);
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003780 else
3781 return patch_ad1884_basic(codec);
3782}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003783#else /* ENABLE_AD_STATIC_QUIRKS */
3784#define patch_ad1884 ad1884_parse_auto_config
3785#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003786
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003787
3788#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai2bac6472007-05-18 18:21:41 +02003789/*
3790 * Lenovo Thinkpad T61/X61
3791 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003792static const struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003793 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003794 .items = {
3795 { "Mic", 0x0 },
3796 { "Internal Mic", 0x1 },
3797 { "Mix", 0x3 },
David Henningssonc40bd912012-09-19 12:19:47 +02003798 { "Dock Mic", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003799 },
3800};
3801
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003802
3803/*
3804 * Dell Precision T3400
3805 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003806static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003807 .num_items = 3,
3808 .items = {
3809 { "Front Mic", 0x0 },
3810 { "Line-In", 0x1 },
3811 { "Mix", 0x3 },
3812 },
3813};
3814
3815
Takashi Iwai498f5b12011-05-02 11:33:15 +02003816static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003817 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3818 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3819 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3820 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3821 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3822 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3823 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3824 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003825 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3826 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
David Henningssonc40bd912012-09-19 12:19:47 +02003827 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3828 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003829 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3830 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3831 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003832 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3833 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3834 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3835 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3836 {
3837 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3838 /* The multiple "Capture Source" controls confuse alsamixer
3839 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003840 */
3841 /* .name = "Capture Source", */
3842 .name = "Input Source",
3843 .count = 2,
3844 .info = ad198x_mux_enum_info,
3845 .get = ad198x_mux_enum_get,
3846 .put = ad198x_mux_enum_put,
3847 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003848 /* SPDIF controls */
3849 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3850 {
3851 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3852 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3853 /* identical with ad1983 */
3854 .info = ad1983_spdif_route_info,
3855 .get = ad1983_spdif_route_get,
3856 .put = ad1983_spdif_route_put,
3857 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003858 { } /* end */
3859};
3860
3861/* additional verbs */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003862static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003863 /* Port-E (docking station mic) pin */
3864 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3865 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3866 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003867 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003868 /* Analog PC Beeper - allow firmware/ACPI beeps */
3869 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003870 /* Analog mixer - docking mic; mute as default */
3871 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003872 /* enable EAPD bit */
3873 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003874 { } /* end */
3875};
3876
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003877/*
3878 * Dell Precision T3400
3879 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003880static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003881 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3882 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3883 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3884 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3885 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3886 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3887 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3888 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3889 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003890 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
3891 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003892 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3893 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3894 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3895 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3896 {
3897 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3898 /* The multiple "Capture Source" controls confuse alsamixer
3899 * So call somewhat different..
3900 */
3901 /* .name = "Capture Source", */
3902 .name = "Input Source",
3903 .count = 2,
3904 .info = ad198x_mux_enum_info,
3905 .get = ad198x_mux_enum_get,
3906 .put = ad198x_mux_enum_put,
3907 },
3908 { } /* end */
3909};
3910
Takashi Iwai2bac6472007-05-18 18:21:41 +02003911/* Digial MIC ADC NID 0x05 + 0x06 */
3912static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3913 struct hda_codec *codec,
3914 unsigned int stream_tag,
3915 unsigned int format,
3916 struct snd_pcm_substream *substream)
3917{
3918 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3919 stream_tag, 0, format);
3920 return 0;
3921}
3922
3923static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3924 struct hda_codec *codec,
3925 struct snd_pcm_substream *substream)
3926{
Takashi Iwai888afa12008-03-18 09:57:50 +01003927 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003928 return 0;
3929}
3930
Takashi Iwai498f5b12011-05-02 11:33:15 +02003931static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003932 .substreams = 2,
3933 .channels_min = 2,
3934 .channels_max = 2,
3935 .nid = 0x05,
3936 .ops = {
3937 .prepare = ad1984_pcm_dmic_prepare,
3938 .cleanup = ad1984_pcm_dmic_cleanup
3939 },
3940};
3941
3942static int ad1984_build_pcms(struct hda_codec *codec)
3943{
3944 struct ad198x_spec *spec = codec->spec;
3945 struct hda_pcm *info;
3946 int err;
3947
3948 err = ad198x_build_pcms(codec);
3949 if (err < 0)
3950 return err;
3951
3952 info = spec->pcm_rec + codec->num_pcms;
3953 codec->num_pcms++;
3954 info->name = "AD1984 Digital Mic";
3955 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3956 return 0;
3957}
3958
3959/* models */
3960enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003961 AD1984_AUTO,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003962 AD1984_BASIC,
3963 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003964 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003965 AD1984_MODELS
3966};
3967
Takashi Iwaiea734962011-01-17 11:29:34 +01003968static const char * const ad1984_models[AD1984_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003969 [AD1984_AUTO] = "auto",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003970 [AD1984_BASIC] = "basic",
3971 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003972 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003973};
3974
Takashi Iwai498f5b12011-05-02 11:33:15 +02003975static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003976 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003977 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003978 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003979 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003980 {}
3981};
3982
3983static int patch_ad1984(struct hda_codec *codec)
3984{
3985 struct ad198x_spec *spec;
3986 int board_config, err;
3987
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003988 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3989 ad1984_models, ad1984_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01003990 if (board_config < 0) {
3991 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3992 codec->chip_name);
3993 board_config = AD1984_AUTO;
3994 }
3995
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003996 if (board_config == AD1984_AUTO)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003997 return ad1884_parse_auto_config(codec);
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003998
3999 err = patch_ad1884_basic(codec);
Takashi Iwai2bac6472007-05-18 18:21:41 +02004000 if (err < 0)
4001 return err;
4002 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004003
Takashi Iwai2bac6472007-05-18 18:21:41 +02004004 switch (board_config) {
4005 case AD1984_BASIC:
4006 /* additional digital mics */
4007 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
4008 codec->patch_ops.build_pcms = ad1984_build_pcms;
4009 break;
4010 case AD1984_THINKPAD:
Jerone Young68c18692010-08-03 01:46:44 -05004011 if (codec->subsystem_id == 0x17aa20fb) {
4012 /* Thinpad X300 does not have the ability to do SPDIF,
4013 or attach to docking station to use SPDIF */
4014 spec->multiout.dig_out_nid = 0;
4015 } else
4016 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02004017 spec->input_mux = &ad1984_thinkpad_capture_source;
4018 spec->mixers[0] = ad1984_thinkpad_mixers;
4019 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01004020 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02004021 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01004022 case AD1984_DELL_DESKTOP:
4023 spec->multiout.dig_out_nid = 0;
4024 spec->input_mux = &ad1984_dell_desktop_capture_source;
4025 spec->mixers[0] = ad1984_dell_desktop_mixers;
4026 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02004027 }
4028 return 0;
4029}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004030#else /* ENABLE_AD_STATIC_QUIRKS */
4031#define patch_ad1984 ad1884_parse_auto_config
4032#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai2bac6472007-05-18 18:21:41 +02004033
4034
4035/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004036 * AD1883 / AD1884A / AD1984A / AD1984B
4037 *
4038 * port-B (0x14) - front mic-in
4039 * port-E (0x1c) - rear mic-in
4040 * port-F (0x16) - CD / ext out
4041 * port-C (0x15) - rear line-in
4042 * port-D (0x12) - rear line-out
4043 * port-A (0x11) - front hp-out
4044 *
4045 * AD1984A = AD1884A + digital-mic
4046 * AD1883 = equivalent with AD1984A
4047 * AD1984B = AD1984A + extra SPDIF-out
4048 *
4049 * FIXME:
4050 * We share the single DAC for both HP and line-outs (see AD1884/1984).
4051 */
4052
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004053#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +02004054static const hda_nid_t ad1884a_dac_nids[1] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004055 0x03,
4056};
4057
4058#define ad1884a_adc_nids ad1884_adc_nids
4059#define ad1884a_capsrc_nids ad1884_capsrc_nids
4060
4061#define AD1884A_SPDIF_OUT 0x02
4062
Takashi Iwai498f5b12011-05-02 11:33:15 +02004063static const struct hda_input_mux ad1884a_capture_source = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004064 .num_items = 5,
4065 .items = {
4066 { "Front Mic", 0x0 },
4067 { "Mic", 0x4 },
4068 { "Line", 0x1 },
4069 { "CD", 0x2 },
4070 { "Mix", 0x3 },
4071 },
4072};
4073
Takashi Iwai498f5b12011-05-02 11:33:15 +02004074static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004075 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4076 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4077 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4078 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4079 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4080 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
4081 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4082 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4083 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4084 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4085 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4086 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4087 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4088 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4089 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
4090 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004091 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4092 HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
4093 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004094 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4095 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4096 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4097 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4098 {
4099 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4100 /* The multiple "Capture Source" controls confuse alsamixer
4101 * So call somewhat different..
4102 */
4103 /* .name = "Capture Source", */
4104 .name = "Input Source",
4105 .count = 2,
4106 .info = ad198x_mux_enum_info,
4107 .get = ad198x_mux_enum_get,
4108 .put = ad198x_mux_enum_put,
4109 },
4110 /* SPDIF controls */
4111 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4112 {
4113 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4114 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4115 /* identical with ad1983 */
4116 .info = ad1983_spdif_route_info,
4117 .get = ad1983_spdif_route_get,
4118 .put = ad1983_spdif_route_put,
4119 },
4120 { } /* end */
4121};
4122
4123/*
4124 * initialization verbs
4125 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004126static const struct hda_verb ad1884a_init_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004127 /* DACs; unmute as default */
4128 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4129 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4130 /* Port-A (HP) mixer - route only from analog mixer */
4131 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4132 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4133 /* Port-A pin */
4134 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4135 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4136 /* Port-D (Line-out) mixer - route only from analog mixer */
4137 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4138 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4139 /* Port-D pin */
4140 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4141 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4142 /* Mono-out mixer - route only from analog mixer */
4143 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4144 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4145 /* Mono-out pin */
4146 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4147 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4148 /* Port-B (front mic) pin */
4149 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004150 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004151 /* Port-C (rear line-in) pin */
4152 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004153 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004154 /* Port-E (rear mic) pin */
4155 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4156 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4157 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
4158 /* Port-F (CD) pin */
4159 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4160 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4161 /* Analog mixer; mute as default */
4162 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4163 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4164 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4165 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4166 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
4167 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4168 /* Analog Mix output amp */
4169 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4170 /* capture sources */
4171 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
4172 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4173 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4174 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4175 /* SPDIF output amp */
4176 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4177 { } /* end */
4178};
4179
Takashi Iwai83012a72012-08-24 18:38:08 +02004180#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02004181static const struct hda_amp_list ad1884a_loopbacks[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004182 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4183 { 0x20, HDA_INPUT, 1 }, /* Mic */
4184 { 0x20, HDA_INPUT, 2 }, /* CD */
4185 { 0x20, HDA_INPUT, 4 }, /* Docking */
4186 { } /* end */
4187};
4188#endif
4189
4190/*
4191 * Laptop model
4192 *
4193 * Port A: Headphone jack
4194 * Port B: MIC jack
4195 * Port C: Internal MIC
4196 * Port D: Dock Line Out (if enabled)
4197 * Port E: Dock Line In (if enabled)
4198 * Port F: Internal speakers
4199 */
4200
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004201static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
4202 struct snd_ctl_elem_value *ucontrol)
4203{
4204 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4205 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
4206 int mute = (!ucontrol->value.integer.value[0] &&
4207 !ucontrol->value.integer.value[1]);
4208 /* toggle GPIO1 according to the mute state */
4209 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
4210 mute ? 0x02 : 0x0);
4211 return ret;
4212}
Takashi Iwaic5059252008-02-16 09:43:56 +01004213
Takashi Iwai498f5b12011-05-02 11:33:15 +02004214static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004215 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004216 {
4217 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4218 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004219 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004220 .info = snd_hda_mixer_amp_switch_info,
4221 .get = snd_hda_mixer_amp_switch_get,
4222 .put = ad1884a_mobile_master_sw_put,
4223 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4224 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004225 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4226 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4227 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4228 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4229 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4230 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4231 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4232 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4233 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004234 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4235 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4236 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004237 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4238 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004239 { } /* end */
4240};
4241
Takashi Iwai498f5b12011-05-02 11:33:15 +02004242static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004243 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02004244 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4245 {
4246 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4247 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004248 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02004249 .info = snd_hda_mixer_amp_switch_info,
4250 .get = snd_hda_mixer_amp_switch_get,
4251 .put = ad1884a_mobile_master_sw_put,
4252 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4253 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004254 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4255 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02004256 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
4257 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004258 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4259 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004260 { } /* end */
4261};
4262
Takashi Iwaic5059252008-02-16 09:43:56 +01004263/* mute internal speaker if HP is plugged */
4264static void ad1884a_hp_automute(struct hda_codec *codec)
4265{
4266 unsigned int present;
4267
Takashi Iwaid56757a2009-11-18 08:00:14 +01004268 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01004269 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4270 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4271 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4272 present ? 0x00 : 0x02);
4273}
4274
Takashi Iwai269ef192008-05-30 15:32:15 +02004275/* switch to external mic if plugged */
4276static void ad1884a_hp_automic(struct hda_codec *codec)
4277{
4278 unsigned int present;
4279
Takashi Iwaid56757a2009-11-18 08:00:14 +01004280 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02004281 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
4282 present ? 0 : 1);
4283}
4284
Takashi Iwaic5059252008-02-16 09:43:56 +01004285#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02004286#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01004287
4288/* unsolicited event for HP jack sensing */
4289static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
4290{
Takashi Iwai269ef192008-05-30 15:32:15 +02004291 switch (res >> 26) {
4292 case AD1884A_HP_EVENT:
4293 ad1884a_hp_automute(codec);
4294 break;
4295 case AD1884A_MIC_EVENT:
4296 ad1884a_hp_automic(codec);
4297 break;
4298 }
Takashi Iwaic5059252008-02-16 09:43:56 +01004299}
4300
4301/* initialize jack-sensing, too */
4302static int ad1884a_hp_init(struct hda_codec *codec)
4303{
4304 ad198x_init(codec);
4305 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02004306 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01004307 return 0;
4308}
4309
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004310/* mute internal speaker if HP or docking HP is plugged */
4311static void ad1884a_laptop_automute(struct hda_codec *codec)
4312{
4313 unsigned int present;
4314
Takashi Iwaid56757a2009-11-18 08:00:14 +01004315 present = snd_hda_jack_detect(codec, 0x11);
4316 if (!present)
4317 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004318 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4319 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4320 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4321 present ? 0x00 : 0x02);
4322}
4323
4324/* switch to external mic if plugged */
4325static void ad1884a_laptop_automic(struct hda_codec *codec)
4326{
4327 unsigned int idx;
4328
Takashi Iwaid56757a2009-11-18 08:00:14 +01004329 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004330 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01004331 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004332 idx = 4;
4333 else
4334 idx = 1;
4335 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
4336}
4337
4338/* unsolicited event for HP jack sensing */
4339static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
4340 unsigned int res)
4341{
4342 switch (res >> 26) {
4343 case AD1884A_HP_EVENT:
4344 ad1884a_laptop_automute(codec);
4345 break;
4346 case AD1884A_MIC_EVENT:
4347 ad1884a_laptop_automic(codec);
4348 break;
4349 }
4350}
4351
4352/* initialize jack-sensing, too */
4353static int ad1884a_laptop_init(struct hda_codec *codec)
4354{
4355 ad198x_init(codec);
4356 ad1884a_laptop_automute(codec);
4357 ad1884a_laptop_automic(codec);
4358 return 0;
4359}
4360
Takashi Iwaic5059252008-02-16 09:43:56 +01004361/* additional verbs for laptop model */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004362static const struct hda_verb ad1884a_laptop_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004363 /* Port-A (HP) pin - always unmuted */
4364 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4365 /* Port-F (int speaker) mixer - route only from analog mixer */
4366 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4367 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004368 /* Port-F (int speaker) pin */
4369 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004370 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004371 /* required for compaq 6530s/6531s speaker output */
4372 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004373 /* Port-C pin - internal mic-in */
4374 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4375 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4376 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004377 /* Port-D (docking line-out) pin - default unmuted */
4378 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004379 /* analog mix */
4380 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4381 /* unsolicited event for pin-sense */
4382 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004383 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004384 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004385 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004386 /* allow to touch GPIO1 (for mute control) */
4387 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4388 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4389 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004390 { } /* end */
4391};
4392
Takashi Iwai498f5b12011-05-02 11:33:15 +02004393static const struct hda_verb ad1884a_mobile_verbs[] = {
Takashi Iwai73156132009-04-23 08:24:48 +02004394 /* DACs; unmute as default */
4395 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4396 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4397 /* Port-A (HP) mixer - route only from analog mixer */
4398 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4399 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4400 /* Port-A pin */
4401 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4402 /* Port-A (HP) pin - always unmuted */
4403 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4404 /* Port-B (mic jack) pin */
4405 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4406 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4407 /* Port-C (int mic) pin */
4408 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4409 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4410 /* Port-F (int speaker) mixer - route only from analog mixer */
4411 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4412 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4413 /* Port-F pin */
4414 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4415 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4416 /* Analog mixer; mute as default */
4417 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4418 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4419 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4420 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4421 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4422 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4423 /* Analog Mix output amp */
4424 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4425 /* capture sources */
4426 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4427 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4428 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4429 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4430 /* unsolicited event for pin-sense */
4431 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4432 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02004433 /* allow to touch GPIO1 (for mute control) */
4434 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4435 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4436 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004437 { } /* end */
4438};
4439
Takashi Iwaic5059252008-02-16 09:43:56 +01004440/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004441 * Thinkpad X300
4442 * 0x11 - HP
4443 * 0x12 - speaker
4444 * 0x14 - mic-in
4445 * 0x17 - built-in mic
4446 */
4447
Takashi Iwai498f5b12011-05-02 11:33:15 +02004448static const struct hda_verb ad1984a_thinkpad_verbs[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004449 /* HP unmute */
4450 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4451 /* analog mix */
4452 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4453 /* turn on EAPD */
4454 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4455 /* unsolicited event for pin-sense */
4456 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4457 /* internal mic - dmic */
4458 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004459 /* set magic COEFs for dmic */
4460 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4461 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004462 { } /* end */
4463};
4464
Takashi Iwai498f5b12011-05-02 11:33:15 +02004465static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004466 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4467 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4468 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4469 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4470 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4471 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004472 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4473 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004474 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4475 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4476 {
4477 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4478 .name = "Capture Source",
4479 .info = ad198x_mux_enum_info,
4480 .get = ad198x_mux_enum_get,
4481 .put = ad198x_mux_enum_put,
4482 },
4483 { } /* end */
4484};
4485
Takashi Iwai498f5b12011-05-02 11:33:15 +02004486static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004487 .num_items = 3,
4488 .items = {
4489 { "Mic", 0x0 },
4490 { "Internal Mic", 0x5 },
4491 { "Mix", 0x3 },
4492 },
4493};
4494
4495/* mute internal speaker if HP is plugged */
4496static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4497{
4498 unsigned int present;
4499
Takashi Iwaid56757a2009-11-18 08:00:14 +01004500 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004501 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4502 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4503}
4504
4505/* unsolicited event for HP jack sensing */
4506static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4507 unsigned int res)
4508{
4509 if ((res >> 26) != AD1884A_HP_EVENT)
4510 return;
4511 ad1984a_thinkpad_automute(codec);
4512}
4513
4514/* initialize jack-sensing, too */
4515static int ad1984a_thinkpad_init(struct hda_codec *codec)
4516{
4517 ad198x_init(codec);
4518 ad1984a_thinkpad_automute(codec);
4519 return 0;
4520}
4521
4522/*
David Henningsson677cd902011-02-07 15:19:34 +01004523 * Precision R5500
4524 * 0x12 - HP/line-out
4525 * 0x13 - speaker (mono)
4526 * 0x15 - mic-in
4527 */
4528
Takashi Iwai498f5b12011-05-02 11:33:15 +02004529static const struct hda_verb ad1984a_precision_verbs[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004530 /* Unmute main output path */
4531 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4532 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
4533 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
4534 /* Analog mixer; mute as default */
4535 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4536 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4537 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4538 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4539 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4540 /* Select mic as input */
4541 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
4542 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
4543 /* Configure as mic */
4544 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4545 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4546 /* HP unmute */
4547 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4548 /* turn on EAPD */
4549 {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4550 /* unsolicited event for pin-sense */
4551 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4552 { } /* end */
4553};
4554
Takashi Iwai498f5b12011-05-02 11:33:15 +02004555static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004556 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4557 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4558 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4559 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4560 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4561 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4562 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4563 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4564 HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
4565 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4566 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4567 { } /* end */
4568};
4569
4570
4571/* mute internal speaker if HP is plugged */
4572static void ad1984a_precision_automute(struct hda_codec *codec)
4573{
4574 unsigned int present;
4575
4576 present = snd_hda_jack_detect(codec, 0x12);
4577 snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
4578 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4579}
4580
4581
4582/* unsolicited event for HP jack sensing */
4583static void ad1984a_precision_unsol_event(struct hda_codec *codec,
4584 unsigned int res)
4585{
4586 if ((res >> 26) != AD1884A_HP_EVENT)
4587 return;
4588 ad1984a_precision_automute(codec);
4589}
4590
4591/* initialize jack-sensing, too */
4592static int ad1984a_precision_init(struct hda_codec *codec)
4593{
4594 ad198x_init(codec);
4595 ad1984a_precision_automute(codec);
4596 return 0;
4597}
4598
4599
4600/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004601 * HP Touchsmart
4602 * port-A (0x11) - front hp-out
4603 * port-B (0x14) - unused
4604 * port-C (0x15) - unused
4605 * port-D (0x12) - rear line out
4606 * port-E (0x1c) - front mic-in
4607 * port-F (0x16) - Internal speakers
4608 * digital-mic (0x17) - Internal mic
4609 */
4610
Takashi Iwai498f5b12011-05-02 11:33:15 +02004611static const struct hda_verb ad1984a_touchsmart_verbs[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004612 /* DACs; unmute as default */
4613 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4614 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4615 /* Port-A (HP) mixer - route only from analog mixer */
4616 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4617 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4618 /* Port-A pin */
4619 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4620 /* Port-A (HP) pin - always unmuted */
4621 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4622 /* Port-E (int speaker) mixer - route only from analog mixer */
4623 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4624 /* Port-E pin */
4625 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4626 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4627 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4628 /* Port-F (int speaker) mixer - route only from analog mixer */
4629 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4630 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4631 /* Port-F pin */
4632 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4633 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4634 /* Analog mixer; mute as default */
4635 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4636 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4637 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4638 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4639 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4640 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4641 /* Analog Mix output amp */
4642 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4643 /* capture sources */
4644 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4645 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4646 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4647 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4648 /* unsolicited event for pin-sense */
4649 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4650 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4651 /* allow to touch GPIO1 (for mute control) */
4652 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4653 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4654 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4655 /* internal mic - dmic */
4656 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4657 /* set magic COEFs for dmic */
4658 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4659 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4660 { } /* end */
4661};
4662
Takashi Iwai498f5b12011-05-02 11:33:15 +02004663static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004664 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4665/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4666 {
4667 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004668 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004669 .name = "Master Playback Switch",
4670 .info = snd_hda_mixer_amp_switch_info,
4671 .get = snd_hda_mixer_amp_switch_get,
4672 .put = ad1884a_mobile_master_sw_put,
4673 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4674 },
4675 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4676 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4677 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4678 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004679 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
4680 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004681 { } /* end */
4682};
4683
4684/* switch to external mic if plugged */
4685static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4686{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004687 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004688 snd_hda_codec_write(codec, 0x0c, 0,
4689 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004690 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004691 snd_hda_codec_write(codec, 0x0c, 0,
4692 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004693}
4694
4695
4696/* unsolicited event for HP jack sensing */
4697static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4698 unsigned int res)
4699{
4700 switch (res >> 26) {
4701 case AD1884A_HP_EVENT:
4702 ad1884a_hp_automute(codec);
4703 break;
4704 case AD1884A_MIC_EVENT:
4705 ad1984a_touchsmart_automic(codec);
4706 break;
4707 }
4708}
4709
4710/* initialize jack-sensing, too */
4711static int ad1984a_touchsmart_init(struct hda_codec *codec)
4712{
4713 ad198x_init(codec);
4714 ad1884a_hp_automute(codec);
4715 ad1984a_touchsmart_automic(codec);
4716 return 0;
4717}
4718
4719
4720/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004721 */
4722
4723enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004724 AD1884A_AUTO,
Takashi Iwaic5059252008-02-16 09:43:56 +01004725 AD1884A_DESKTOP,
4726 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004727 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004728 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004729 AD1984A_TOUCHSMART,
David Henningsson677cd902011-02-07 15:19:34 +01004730 AD1984A_PRECISION,
Takashi Iwaic5059252008-02-16 09:43:56 +01004731 AD1884A_MODELS
4732};
4733
Takashi Iwaiea734962011-01-17 11:29:34 +01004734static const char * const ad1884a_models[AD1884A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004735 [AD1884A_AUTO] = "auto",
Takashi Iwaic5059252008-02-16 09:43:56 +01004736 [AD1884A_DESKTOP] = "desktop",
4737 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004738 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004739 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004740 [AD1984A_TOUCHSMART] = "touchsmart",
David Henningsson677cd902011-02-07 15:19:34 +01004741 [AD1984A_PRECISION] = "precision",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004742};
4743
Takashi Iwai498f5b12011-05-02 11:33:15 +02004744static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004745 SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004746 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004747 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004748 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004749 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004750 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004751 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4752 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004753 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004754 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004755 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004756 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004757};
4758
4759static int patch_ad1884a(struct hda_codec *codec)
4760{
4761 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004762 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004763
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004764 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
4765 ad1884a_models,
4766 ad1884a_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01004767 if (board_config < 0) {
4768 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
4769 codec->chip_name);
4770 board_config = AD1884A_AUTO;
4771 }
4772
4773 if (board_config == AD1884A_AUTO)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004774 return ad1884_parse_auto_config(codec);
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004775
Takashi Iwai361dab32012-05-09 14:35:27 +02004776 err = alloc_ad_spec(codec);
4777 if (err < 0)
4778 return err;
4779 spec = codec->spec;
Takashi Iwaic5059252008-02-16 09:43:56 +01004780
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004781 err = snd_hda_attach_beep_device(codec, 0x10);
4782 if (err < 0) {
4783 ad198x_free(codec);
4784 return err;
4785 }
4786 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4787
Takashi Iwaic5059252008-02-16 09:43:56 +01004788 spec->multiout.max_channels = 2;
4789 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4790 spec->multiout.dac_nids = ad1884a_dac_nids;
4791 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4792 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4793 spec->adc_nids = ad1884a_adc_nids;
4794 spec->capsrc_nids = ad1884a_capsrc_nids;
4795 spec->input_mux = &ad1884a_capture_source;
4796 spec->num_mixers = 1;
4797 spec->mixers[0] = ad1884a_base_mixers;
4798 spec->num_init_verbs = 1;
4799 spec->init_verbs[0] = ad1884a_init_verbs;
4800 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02004801#ifdef CONFIG_PM
Takashi Iwaic5059252008-02-16 09:43:56 +01004802 spec->loopback.amplist = ad1884a_loopbacks;
4803#endif
4804 codec->patch_ops = ad198x_patch_ops;
4805
4806 /* override some parameters */
Takashi Iwaic5059252008-02-16 09:43:56 +01004807 switch (board_config) {
4808 case AD1884A_LAPTOP:
4809 spec->mixers[0] = ad1884a_laptop_mixers;
4810 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4811 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004812 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4813 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004814 /* set the upper-limit for mixer amp to 0dB for avoiding the
4815 * possible damage by overloading
4816 */
4817 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4818 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4819 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4820 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4821 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004822 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004823 case AD1884A_MOBILE:
4824 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004825 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004826 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004827 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4828 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004829 /* set the upper-limit for mixer amp to 0dB for avoiding the
4830 * possible damage by overloading
4831 */
4832 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4833 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4834 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4835 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4836 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004837 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004838 case AD1884A_THINKPAD:
4839 spec->mixers[0] = ad1984a_thinkpad_mixers;
4840 spec->init_verbs[spec->num_init_verbs++] =
4841 ad1984a_thinkpad_verbs;
4842 spec->multiout.dig_out_nid = 0;
4843 spec->input_mux = &ad1984a_thinkpad_capture_source;
4844 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4845 codec->patch_ops.init = ad1984a_thinkpad_init;
4846 break;
David Henningsson677cd902011-02-07 15:19:34 +01004847 case AD1984A_PRECISION:
4848 spec->mixers[0] = ad1984a_precision_mixers;
4849 spec->init_verbs[spec->num_init_verbs++] =
4850 ad1984a_precision_verbs;
4851 spec->multiout.dig_out_nid = 0;
4852 codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
4853 codec->patch_ops.init = ad1984a_precision_init;
4854 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004855 case AD1984A_TOUCHSMART:
4856 spec->mixers[0] = ad1984a_touchsmart_mixers;
4857 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4858 spec->multiout.dig_out_nid = 0;
4859 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4860 codec->patch_ops.init = ad1984a_touchsmart_init;
4861 /* set the upper-limit for mixer amp to 0dB for avoiding the
4862 * possible damage by overloading
4863 */
4864 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4865 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4866 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4867 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4868 (1 << AC_AMPCAP_MUTE_SHIFT));
4869 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004870 }
4871
Takashi Iwai729d55b2009-12-25 22:49:01 +01004872 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004873 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004874
Takashi Iwaic5059252008-02-16 09:43:56 +01004875 return 0;
4876}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004877#else /* ENABLE_AD_STATIC_QUIRKS */
4878#define patch_ad1884a ad1884_parse_auto_config
4879#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaic5059252008-02-16 09:43:56 +01004880
4881
4882/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004883 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004884 *
4885 * port-A - front hp-out
4886 * port-B - front mic-in
4887 * port-C - rear line-in, shared surr-out (3stack)
4888 * port-D - rear line-out
4889 * port-E - rear mic-in, shared clfe-out (3stack)
4890 * port-F - rear surr-out (6stack)
4891 * port-G - rear clfe-out (6stack)
4892 */
4893
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004894#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +02004895static const hda_nid_t ad1882_dac_nids[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004896 0x04, 0x03, 0x05
4897};
4898
Takashi Iwai498f5b12011-05-02 11:33:15 +02004899static const hda_nid_t ad1882_adc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004900 0x08, 0x09,
4901};
4902
Takashi Iwai498f5b12011-05-02 11:33:15 +02004903static const hda_nid_t ad1882_capsrc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004904 0x0c, 0x0d,
4905};
4906
4907#define AD1882_SPDIF_OUT 0x02
4908
4909/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004910static const struct hda_input_mux ad1882_capture_source = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004911 .num_items = 5,
4912 .items = {
4913 { "Front Mic", 0x1 },
4914 { "Mic", 0x4 },
4915 { "Line", 0x2 },
4916 { "CD", 0x3 },
4917 { "Mix", 0x7 },
4918 },
4919};
4920
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004921/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004922static const struct hda_input_mux ad1882a_capture_source = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004923 .num_items = 5,
4924 .items = {
4925 { "Front Mic", 0x1 },
4926 { "Mic", 0x4},
4927 { "Line", 0x2 },
4928 { "Digital Mic", 0x06 },
4929 { "Mix", 0x7 },
4930 },
4931};
4932
Takashi Iwai498f5b12011-05-02 11:33:15 +02004933static const struct snd_kcontrol_new ad1882_base_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004934 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4935 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4936 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4937 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4938 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4939 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4940 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4941 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004942
David Henningsson5f99f862011-01-04 15:24:24 +01004943 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
4944 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
4945 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
Takashi Iwai0ac85512007-06-20 15:46:13 +02004946 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4947 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4948 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4949 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4950 {
4951 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4952 /* The multiple "Capture Source" controls confuse alsamixer
4953 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004954 */
4955 /* .name = "Capture Source", */
4956 .name = "Input Source",
4957 .count = 2,
4958 .info = ad198x_mux_enum_info,
4959 .get = ad198x_mux_enum_get,
4960 .put = ad198x_mux_enum_put,
4961 },
4962 /* SPDIF controls */
4963 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4964 {
4965 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4966 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4967 /* identical with ad1983 */
4968 .info = ad1983_spdif_route_info,
4969 .get = ad1983_spdif_route_get,
4970 .put = ad1983_spdif_route_put,
4971 },
4972 { } /* end */
4973};
4974
Takashi Iwai498f5b12011-05-02 11:33:15 +02004975static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004976 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4977 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4978 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4979 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4980 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4981 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4982 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4983 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004984 { } /* end */
4985};
4986
Takashi Iwai498f5b12011-05-02 11:33:15 +02004987static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004988 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4989 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4990 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4991 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4992 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4993 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4994 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4995 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004996 HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004997 { } /* end */
4998};
4999
Takashi Iwai498f5b12011-05-02 11:33:15 +02005000static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005001 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
5002 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
5003 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
5004 {
5005 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5006 .name = "Channel Mode",
5007 .info = ad198x_ch_mode_info,
5008 .get = ad198x_ch_mode_get,
5009 .put = ad198x_ch_mode_put,
5010 },
5011 { } /* end */
5012};
5013
Takashi Iwai1c868452012-08-13 11:09:35 +02005014/* simple auto-mute control for AD1882 3-stack board */
5015#define AD1882_HP_EVENT 0x01
5016
5017static void ad1882_3stack_automute(struct hda_codec *codec)
5018{
5019 bool mute = snd_hda_jack_detect(codec, 0x11);
5020 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
5021 mute ? 0 : PIN_OUT);
5022}
5023
5024static int ad1882_3stack_automute_init(struct hda_codec *codec)
5025{
5026 ad198x_init(codec);
5027 ad1882_3stack_automute(codec);
5028 return 0;
5029}
5030
5031static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res)
5032{
5033 switch (res >> 26) {
5034 case AD1882_HP_EVENT:
5035 ad1882_3stack_automute(codec);
5036 break;
5037 }
5038}
5039
Takashi Iwai498f5b12011-05-02 11:33:15 +02005040static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005041 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
5042 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
5043 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
5044 { } /* end */
5045};
5046
Takashi Iwai498f5b12011-05-02 11:33:15 +02005047static const struct hda_verb ad1882_ch2_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005048 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
5049 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5050 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5051 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5052 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5053 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5054 { } /* end */
5055};
5056
Takashi Iwai498f5b12011-05-02 11:33:15 +02005057static const struct hda_verb ad1882_ch4_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005058 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5059 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5060 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5061 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5062 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5063 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5064 { } /* end */
5065};
5066
Takashi Iwai498f5b12011-05-02 11:33:15 +02005067static const struct hda_verb ad1882_ch6_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005068 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5069 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5070 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5071 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5072 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5073 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5074 { } /* end */
5075};
5076
Takashi Iwai498f5b12011-05-02 11:33:15 +02005077static const struct hda_channel_mode ad1882_modes[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005078 { 2, ad1882_ch2_init },
5079 { 4, ad1882_ch4_init },
5080 { 6, ad1882_ch6_init },
5081};
5082
5083/*
5084 * initialization verbs
5085 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02005086static const struct hda_verb ad1882_init_verbs[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005087 /* DACs; mute as default */
5088 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
5089 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
5090 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
5091 /* Port-A (HP) mixer */
5092 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5093 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5094 /* Port-A pin */
5095 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
5096 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5097 /* HP selector - select DAC2 */
5098 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
5099 /* Port-D (Line-out) mixer */
5100 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5101 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5102 /* Port-D pin */
5103 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
5104 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5105 /* Mono-out mixer */
5106 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5107 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5108 /* Mono-out pin */
5109 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
5110 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5111 /* Port-B (front mic) pin */
5112 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5113 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5114 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
5115 /* Port-C (line-in) pin */
5116 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
5117 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5118 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
5119 /* Port-C mixer - mute as input */
5120 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5121 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5122 /* Port-E (mic-in) pin */
5123 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5124 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5125 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
5126 /* Port-E mixer - mute as input */
5127 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5128 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5129 /* Port-F (surround) */
5130 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5131 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5132 /* Port-G (CLFE) */
5133 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5134 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5135 /* Analog mixer; mute as default */
5136 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
5137 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5138 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5139 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5140 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5141 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5142 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
5143 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
5144 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
5145 /* Analog Mix output amp */
5146 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
5147 /* SPDIF output selector */
5148 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
5149 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
5150 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
5151 { } /* end */
5152};
5153
Takashi Iwai1c868452012-08-13 11:09:35 +02005154static const struct hda_verb ad1882_3stack_automute_verbs[] = {
5155 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT},
5156 { } /* end */
5157};
5158
Takashi Iwai83012a72012-08-24 18:38:08 +02005159#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02005160static const struct hda_amp_list ad1882_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02005161 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
5162 { 0x20, HDA_INPUT, 1 }, /* Mic */
5163 { 0x20, HDA_INPUT, 4 }, /* Line */
5164 { 0x20, HDA_INPUT, 6 }, /* CD */
5165 { } /* end */
5166};
5167#endif
5168
Takashi Iwai0ac85512007-06-20 15:46:13 +02005169/* models */
5170enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005171 AD1882_AUTO,
Takashi Iwai0ac85512007-06-20 15:46:13 +02005172 AD1882_3STACK,
5173 AD1882_6STACK,
Takashi Iwai1c868452012-08-13 11:09:35 +02005174 AD1882_3STACK_AUTOMUTE,
Takashi Iwai0ac85512007-06-20 15:46:13 +02005175 AD1882_MODELS
5176};
5177
Takashi Iwaiea734962011-01-17 11:29:34 +01005178static const char * const ad1882_models[AD1986A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005179 [AD1882_AUTO] = "auto",
Takashi Iwai0ac85512007-06-20 15:46:13 +02005180 [AD1882_3STACK] = "3stack",
5181 [AD1882_6STACK] = "6stack",
Takashi Iwai1c868452012-08-13 11:09:35 +02005182 [AD1882_3STACK_AUTOMUTE] = "3stack-automute",
Takashi Iwai0ac85512007-06-20 15:46:13 +02005183};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005184#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai0ac85512007-06-20 15:46:13 +02005185
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005186static int ad1882_parse_auto_config(struct hda_codec *codec)
5187{
Takashi Iwai0ac85512007-06-20 15:46:13 +02005188 struct ad198x_spec *spec;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005189 int err;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005190
Takashi Iwai361dab32012-05-09 14:35:27 +02005191 err = alloc_ad_spec(codec);
5192 if (err < 0)
5193 return err;
5194 spec = codec->spec;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005195
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005196 spec->gen.mixer_nid = 0x20;
Takashi Iwaie4a395e2013-01-23 17:00:31 +01005197 spec->gen.mixer_merge_nid = 0x21;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01005198 spec->gen.beep_nid = 0x10;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005199 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
5200 err = ad198x_parse_auto_config(codec);
5201 if (err < 0)
5202 goto error;
5203 err = ad1988_add_spdif_mux_ctl(codec);
5204 if (err < 0)
5205 goto error;
5206 return 0;
5207
5208 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01005209 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005210 return err;
5211}
5212
5213#ifdef ENABLE_AD_STATIC_QUIRKS
5214static int patch_ad1882(struct hda_codec *codec)
5215{
5216 struct ad198x_spec *spec;
5217 int err, board_config;
5218
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005219 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
5220 ad1882_models, NULL);
Takashi Iwai657e1b92013-01-22 18:42:39 +01005221 if (board_config < 0) {
5222 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
5223 codec->chip_name);
5224 board_config = AD1882_AUTO;
5225 }
5226
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005227 if (board_config == AD1882_AUTO)
5228 return ad1882_parse_auto_config(codec);
5229
5230 err = alloc_ad_spec(codec);
5231 if (err < 0)
5232 return err;
5233 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005234
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01005235 err = snd_hda_attach_beep_device(codec, 0x10);
5236 if (err < 0) {
5237 ad198x_free(codec);
5238 return err;
5239 }
5240 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
5241
Takashi Iwai0ac85512007-06-20 15:46:13 +02005242 spec->multiout.max_channels = 6;
5243 spec->multiout.num_dacs = 3;
5244 spec->multiout.dac_nids = ad1882_dac_nids;
5245 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
5246 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
5247 spec->adc_nids = ad1882_adc_nids;
5248 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005249 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005250 spec->input_mux = &ad1882_capture_source;
5251 else
5252 spec->input_mux = &ad1882a_capture_source;
5253 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005254 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005255 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005256 spec->mixers[1] = ad1882_loopback_mixers;
5257 else
5258 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005259 spec->num_init_verbs = 1;
5260 spec->init_verbs[0] = ad1882_init_verbs;
5261 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02005262#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02005263 spec->loopback.amplist = ad1882_loopbacks;
5264#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01005265 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005266
5267 codec->patch_ops = ad198x_patch_ops;
5268
5269 /* override some parameters */
Takashi Iwai0ac85512007-06-20 15:46:13 +02005270 switch (board_config) {
5271 default:
5272 case AD1882_3STACK:
Takashi Iwai1c868452012-08-13 11:09:35 +02005273 case AD1882_3STACK_AUTOMUTE:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005274 spec->num_mixers = 3;
5275 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005276 spec->channel_mode = ad1882_modes;
5277 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
5278 spec->need_dac_fix = 1;
5279 spec->multiout.max_channels = 2;
5280 spec->multiout.num_dacs = 1;
Takashi Iwai1c868452012-08-13 11:09:35 +02005281 if (board_config != AD1882_3STACK) {
5282 spec->init_verbs[spec->num_init_verbs++] =
5283 ad1882_3stack_automute_verbs;
5284 codec->patch_ops.unsol_event = ad1882_3stack_unsol_event;
5285 codec->patch_ops.init = ad1882_3stack_automute_init;
5286 }
Takashi Iwai0ac85512007-06-20 15:46:13 +02005287 break;
5288 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005289 spec->num_mixers = 3;
5290 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005291 break;
5292 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01005293
5294 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02005295 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01005296
Takashi Iwai0ac85512007-06-20 15:46:13 +02005297 return 0;
5298}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005299#else /* ENABLE_AD_STATIC_QUIRKS */
5300#define patch_ad1882 ad1882_parse_auto_config
5301#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai0ac85512007-06-20 15:46:13 +02005302
5303
5304/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07005305 * patch entries
5306 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02005307static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01005308 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02005309 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005310 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005311 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005312 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
5313 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02005314 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
5315 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005316 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005317 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01005318 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02005319 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005320 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02005321 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
5322 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005323 {} /* terminator */
5324};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01005325
5326MODULE_ALIAS("snd-hda-codec-id:11d4*");
5327
5328MODULE_LICENSE("GPL");
5329MODULE_DESCRIPTION("Analog Devices HD-audio codec");
5330
5331static struct hda_codec_preset_list analog_list = {
5332 .preset = snd_hda_preset_analog,
5333 .owner = THIS_MODULE,
5334};
5335
5336static int __init patch_analog_init(void)
5337{
5338 return snd_hda_add_codec_preset(&analog_list);
5339}
5340
5341static void __exit patch_analog_exit(void)
5342{
5343 snd_hda_delete_codec_preset(&analog_list);
5344}
5345
5346module_init(patch_analog_init)
5347module_exit(patch_analog_exit)