blob: 4fedd9dfd85a8ec2967fca31a77776d3841f7f81 [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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai825aa972006-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 Iwai272f3ea2013-01-22 15:31:33 +01001430/*
1431 * SPDIF mux control for AD1983 auto-parser
1432 */
1433static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
1434 struct snd_ctl_elem_info *uinfo)
1435{
1436 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1437 struct ad198x_spec *spec = codec->spec;
1438 static const char * const texts2[] = { "PCM", "ADC" };
1439 static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
1440 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1441 int num_conns = snd_hda_get_num_conns(codec, dig_out);
1442
1443 if (num_conns == 2)
1444 return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
1445 else if (num_conns == 3)
1446 return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
1447 else
1448 return -EINVAL;
1449}
1450
1451static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
1452 struct snd_ctl_elem_value *ucontrol)
1453{
1454 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1455 struct ad198x_spec *spec = codec->spec;
1456
1457 ucontrol->value.enumerated.item[0] = spec->cur_smux;
1458 return 0;
1459}
1460
1461static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
1462 struct snd_ctl_elem_value *ucontrol)
1463{
1464 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1465 struct ad198x_spec *spec = codec->spec;
1466 unsigned int val = ucontrol->value.enumerated.item[0];
1467 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1468 int num_conns = snd_hda_get_num_conns(codec, dig_out);
1469
1470 if (val >= num_conns)
1471 return -EINVAL;
1472 if (spec->cur_smux == val)
1473 return 0;
1474 spec->cur_smux = val;
1475 snd_hda_codec_write_cache(codec, dig_out, 0,
1476 AC_VERB_SET_CONNECT_SEL, val);
1477 return 1;
1478}
1479
1480static struct snd_kcontrol_new ad1983_auto_smux_mixer = {
1481 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1482 .name = "IEC958 Playback Source",
1483 .info = ad1983_auto_smux_enum_info,
1484 .get = ad1983_auto_smux_enum_get,
1485 .put = ad1983_auto_smux_enum_put,
1486};
1487
1488static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
1489{
1490 struct ad198x_spec *spec = codec->spec;
1491 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1492 int num_conns;
1493
1494 if (!dig_out)
1495 return 0;
1496 num_conns = snd_hda_get_num_conns(codec, dig_out);
1497 if (num_conns != 2 && num_conns != 3)
1498 return 0;
1499 if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
1500 return -ENOMEM;
1501 return 0;
1502}
1503
Takashi Iwaibd450dc2013-07-04 15:48:04 +02001504static int patch_ad1983(struct hda_codec *codec)
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001505{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001506 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001507 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001508
Takashi Iwai361dab32012-05-09 14:35:27 +02001509 err = alloc_ad_spec(codec);
1510 if (err < 0)
1511 return err;
1512 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001513
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001514 spec->gen.beep_nid = 0x10;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001515 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1516 err = ad198x_parse_auto_config(codec);
1517 if (err < 0)
1518 goto error;
1519 err = ad1983_add_spdif_mux_ctl(codec);
1520 if (err < 0)
1521 goto error;
1522 return 0;
1523
1524 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001525 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001526 return err;
1527}
1528
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001529
1530/*
1531 * AD1981 HD specific
1532 */
1533
Takashi Iwaia928bd22013-01-22 18:18:42 +01001534/* follow EAPD via vmaster hook */
1535static void ad_vmaster_eapd_hook(void *private_data, int enabled)
1536{
1537 struct hda_codec *codec = private_data;
1538 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02001539
1540 if (!spec->eapd_nid)
1541 return;
Takashi Iwaia928bd22013-01-22 18:18:42 +01001542 snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
1543 AC_VERB_SET_EAPD_BTLENABLE,
1544 enabled ? 0x02 : 0x00);
1545}
1546
1547static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
1548 const struct hda_fixup *fix, int action)
1549{
1550 struct ad198x_spec *spec = codec->spec;
1551
1552 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
1553 spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
1554 spec->eapd_nid = 0x05;
1555 }
1556}
1557
1558/* set the upper-limit for mixer amp to 0dB for avoiding the possible
1559 * damage by overloading
1560 */
1561static void ad1981_fixup_amp_override(struct hda_codec *codec,
1562 const struct hda_fixup *fix, int action)
1563{
1564 if (action == HDA_FIXUP_ACT_PRE_PROBE)
1565 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1566 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1567 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1568 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1569 (1 << AC_AMPCAP_MUTE_SHIFT));
1570}
1571
1572enum {
1573 AD1981_FIXUP_AMP_OVERRIDE,
1574 AD1981_FIXUP_HP_EAPD,
1575};
1576
1577static const struct hda_fixup ad1981_fixups[] = {
1578 [AD1981_FIXUP_AMP_OVERRIDE] = {
1579 .type = HDA_FIXUP_FUNC,
1580 .v.func = ad1981_fixup_amp_override,
1581 },
1582 [AD1981_FIXUP_HP_EAPD] = {
1583 .type = HDA_FIXUP_FUNC,
1584 .v.func = ad1981_fixup_hp_eapd,
1585 .chained = true,
1586 .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
1587 },
1588};
1589
1590static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
1591 SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
1592 SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
1593 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
1594 /* HP nx6320 (reversed SSID, H/W bug) */
1595 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
1596 {}
1597};
1598
Takashi Iwaibd450dc2013-07-04 15:48:04 +02001599static int patch_ad1981(struct hda_codec *codec)
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001600{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001601 struct ad198x_spec *spec;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001602 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001603
Takashi Iwai361dab32012-05-09 14:35:27 +02001604 err = alloc_ad_spec(codec);
1605 if (err < 0)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001606 return -ENOMEM;
Takashi Iwai361dab32012-05-09 14:35:27 +02001607 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001608
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001609 spec->gen.mixer_nid = 0x0e;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001610 spec->gen.beep_nid = 0x10;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001611 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
Takashi Iwaia928bd22013-01-22 18:18:42 +01001612
1613 snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
1614 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
1615
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001616 err = ad198x_parse_auto_config(codec);
1617 if (err < 0)
1618 goto error;
1619 err = ad1983_add_spdif_mux_ctl(codec);
1620 if (err < 0)
1621 goto error;
Takashi Iwaia928bd22013-01-22 18:18:42 +01001622
1623 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
1624
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001625 return 0;
1626
1627 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01001628 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001629 return err;
1630}
1631
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001632
1633/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001634 * AD1988
1635 *
1636 * Output pins and routes
1637 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001638 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001639 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1640 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1641 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1642 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1643 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1644 * port-F 0x16 (mute) <- 0x2a <- 06
1645 * port-G 0x24 (mute) <- 0x27 <- 05
1646 * port-H 0x25 (mute) <- 0x28 <- 0a
1647 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1648 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001649 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1650 * (*) 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 +01001651 *
1652 * Input pins and routes
1653 *
1654 * pin boost mix input # / adc input #
1655 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1656 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1657 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1658 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1659 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1660 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1661 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1662 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
1663 *
1664 *
1665 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01001666 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001667 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001668 *
1669 * Inputs of Analog Mix (0x20)
1670 * 0:Port-B (front mic)
1671 * 1:Port-C/G/H (line-in)
1672 * 2:Port-A
1673 * 3:Port-D (line-in/2)
1674 * 4:Port-E/G/H (mic-in)
1675 * 5:Port-F (mic2-in)
1676 * 6:CD
1677 * 7:Beep
1678 *
1679 * ADC selection
1680 * 0:Port-A
1681 * 1:Port-B (front mic-in)
1682 * 2:Port-C (line-in)
1683 * 3:Port-F (mic2-in)
1684 * 4:Port-E (mic-in)
1685 * 5:CD
1686 * 6:Port-G
1687 * 7:Port-H
1688 * 8:Port-D (line-in/2)
1689 * 9:Mix
1690 *
1691 * Proposed pin assignments by the datasheet
1692 *
1693 * 6-stack
1694 * Port-A front headphone
1695 * B front mic-in
1696 * C rear line-in
1697 * D rear front-out
1698 * E rear mic-in
1699 * F rear surround
1700 * G rear CLFE
1701 * H rear side
1702 *
1703 * 3-stack
1704 * Port-A front headphone
1705 * B front mic
1706 * C rear line-in/surround
1707 * D rear front-out
1708 * E rear mic-in/CLFE
1709 *
1710 * laptop
1711 * Port-A headphone
1712 * B mic-in
1713 * C docking station
1714 * D internal speaker (with EAPD)
1715 * E/F quad mic array
1716 */
1717
1718
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001719#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001720/* models */
1721enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001722 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001723 AD1988_6STACK,
1724 AD1988_6STACK_DIG,
1725 AD1988_3STACK,
1726 AD1988_3STACK_DIG,
1727 AD1988_LAPTOP,
1728 AD1988_LAPTOP_DIG,
1729 AD1988_MODEL_LAST,
1730};
1731
Takashi Iwaid32410b12005-11-24 16:06:23 +01001732/* reivision id to check workarounds */
1733#define AD1988A_REV2 0x100200
1734
Takashi Iwai1a806f42006-07-03 15:58:16 +02001735#define is_rev2(codec) \
1736 ((codec)->vendor_id == 0x11d41988 && \
1737 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001738
1739/*
1740 * mixers
1741 */
1742
Takashi Iwai498f5b12011-05-02 11:33:15 +02001743static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001744 0x04, 0x06, 0x05, 0x0a
1745};
1746
Takashi Iwai498f5b12011-05-02 11:33:15 +02001747static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001748 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01001749};
1750
1751/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001752static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01001753 0x04, 0x05, 0x0a, 0x06
1754};
1755
Takashi Iwai498f5b12011-05-02 11:33:15 +02001756static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01001757 0x03
1758};
1759
Takashi Iwai498f5b12011-05-02 11:33:15 +02001760static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001761 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01001762};
1763
Takashi Iwai498f5b12011-05-02 11:33:15 +02001764static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001765 0x08, 0x09, 0x0f
1766};
1767
Takashi Iwai498f5b12011-05-02 11:33:15 +02001768static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001769 0x0c, 0x0d, 0x0e
1770};
1771
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001772#define AD1988_SPDIF_OUT 0x02
1773#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001774#define AD1988_SPDIF_IN 0x07
1775
Takashi Iwai498f5b12011-05-02 11:33:15 +02001776static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01001777 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07001778};
1779
Takashi Iwai498f5b12011-05-02 11:33:15 +02001780static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001781 .num_items = 5,
1782 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001783 { "Front Mic", 0x1 }, /* port-B */
1784 { "Line", 0x2 }, /* port-C */
1785 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001786 { "CD", 0x5 },
1787 { "Mix", 0x9 },
1788 },
1789};
1790
Takashi Iwai498f5b12011-05-02 11:33:15 +02001791static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001792 .num_items = 3,
1793 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01001794 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001795 { "CD", 0x5 },
1796 { "Mix", 0x9 },
1797 },
1798};
1799
1800/*
1801 */
1802static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
1803 struct snd_ctl_elem_info *uinfo)
1804{
1805 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1806 struct ad198x_spec *spec = codec->spec;
1807 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
1808 spec->num_channel_mode);
1809}
1810
1811static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
1812 struct snd_ctl_elem_value *ucontrol)
1813{
1814 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1815 struct ad198x_spec *spec = codec->spec;
1816 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
1817 spec->num_channel_mode, spec->multiout.max_channels);
1818}
1819
1820static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
1821 struct snd_ctl_elem_value *ucontrol)
1822{
1823 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1824 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001825 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
1826 spec->num_channel_mode,
1827 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02001828 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02001829 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02001830 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001831}
1832
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001833/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001834static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001835 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1836 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1837 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1838 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
1839 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001840 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001841};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001842
Takashi Iwai498f5b12011-05-02 11:33:15 +02001843static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01001844 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1845 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1846 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
1847 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
1848 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001849 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001850};
1851
Takashi Iwai498f5b12011-05-02 11:33:15 +02001852static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08001853 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001854 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
1855 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
1856 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
1857 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
1858 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
1859 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1860 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1861
1862 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1863 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1864 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1865 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1866 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1867 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1868 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1869 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1870
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001871 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001872 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1873
David Henningsson5f99f862011-01-04 15:24:24 +01001874 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
1875 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001876 { } /* end */
1877};
1878
1879/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001880static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001881 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001882 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001883 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
1884 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001885 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001886};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001887
Takashi Iwai498f5b12011-05-02 11:33:15 +02001888static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01001889 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01001890 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
1891 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
1892 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02001893 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01001894};
1895
Takashi Iwai498f5b12011-05-02 11:33:15 +02001896static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08001897 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001898 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01001899 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
1900 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
1901 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001902 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
1903 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1904
1905 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1906 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1907 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1908 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1909 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1910 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1911 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
1912 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
1913
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001914 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001915 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1916
David Henningsson5f99f862011-01-04 15:24:24 +01001917 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
1918 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001919 {
1920 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1921 .name = "Channel Mode",
1922 .info = ad198x_ch_mode_info,
1923 .get = ad198x_ch_mode_get,
1924 .put = ad198x_ch_mode_put,
1925 },
1926
1927 { } /* end */
1928};
1929
1930/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001931static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08001932 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001933 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
1934 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
1935 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
1936
1937 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
1938 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
1939 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
1940 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
1941 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
1942 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
1943
Takashi Iwai2e5b9562005-11-21 16:36:15 +01001944 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001945 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
1946
David Henningsson5f99f862011-01-04 15:24:24 +01001947 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001948
1949 {
1950 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1951 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001952 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001953 .info = ad198x_eapd_info,
1954 .get = ad198x_eapd_get,
1955 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001956 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001957 },
1958
1959 { } /* end */
1960};
1961
1962/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001963static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001964 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
1965 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
1966 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
1967 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
1968 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
1969 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
1970 {
1971 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1972 /* The multiple "Capture Source" controls confuse alsamixer
1973 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001974 */
1975 /* .name = "Capture Source", */
1976 .name = "Input Source",
1977 .count = 3,
1978 .info = ad198x_mux_enum_info,
1979 .get = ad198x_mux_enum_get,
1980 .put = ad198x_mux_enum_put,
1981 },
1982 { } /* end */
1983};
1984
1985static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
1986 struct snd_ctl_elem_info *uinfo)
1987{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001988 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001989 "PCM", "ADC1", "ADC2", "ADC3"
1990 };
1991 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1992 uinfo->count = 1;
1993 uinfo->value.enumerated.items = 4;
1994 if (uinfo->value.enumerated.item >= 4)
1995 uinfo->value.enumerated.item = 3;
1996 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1997 return 0;
1998}
1999
2000static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2001 struct snd_ctl_elem_value *ucontrol)
2002{
2003 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2004 unsigned int sel;
2005
Takashi Iwaibddcf542007-07-24 18:04:05 +02002006 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2007 AC_AMP_GET_INPUT);
2008 if (!(sel & 0x80))
2009 ucontrol->value.enumerated.item[0] = 0;
2010 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002011 sel = snd_hda_codec_read(codec, 0x0b, 0,
2012 AC_VERB_GET_CONNECT_SEL, 0);
2013 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002014 sel++;
2015 else
2016 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002017 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002018 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002019 return 0;
2020}
2021
2022static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2023 struct snd_ctl_elem_value *ucontrol)
2024{
2025 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002026 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002027 int change;
2028
Takashi Iwai35b26722007-05-05 12:17:17 +02002029 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002030 if (val > 3)
2031 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002032 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002033 sel = snd_hda_codec_read(codec, 0x1d, 0,
2034 AC_VERB_GET_AMP_GAIN_MUTE,
2035 AC_AMP_GET_INPUT);
2036 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002037 if (change) {
2038 snd_hda_codec_write_cache(codec, 0x1d, 0,
2039 AC_VERB_SET_AMP_GAIN_MUTE,
2040 AMP_IN_UNMUTE(0));
2041 snd_hda_codec_write_cache(codec, 0x1d, 0,
2042 AC_VERB_SET_AMP_GAIN_MUTE,
2043 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002044 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002045 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002046 sel = snd_hda_codec_read(codec, 0x1d, 0,
2047 AC_VERB_GET_AMP_GAIN_MUTE,
2048 AC_AMP_GET_INPUT | 0x01);
2049 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002050 if (change) {
2051 snd_hda_codec_write_cache(codec, 0x1d, 0,
2052 AC_VERB_SET_AMP_GAIN_MUTE,
2053 AMP_IN_MUTE(0));
2054 snd_hda_codec_write_cache(codec, 0x1d, 0,
2055 AC_VERB_SET_AMP_GAIN_MUTE,
2056 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002057 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002058 sel = snd_hda_codec_read(codec, 0x0b, 0,
2059 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2060 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002061 if (change)
2062 snd_hda_codec_write_cache(codec, 0x0b, 0,
2063 AC_VERB_SET_CONNECT_SEL,
2064 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002065 }
2066 return change;
2067}
2068
Takashi Iwai498f5b12011-05-02 11:33:15 +02002069static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002070 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2071 {
2072 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2073 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002074 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002075 .info = ad1988_spdif_playback_source_info,
2076 .get = ad1988_spdif_playback_source_get,
2077 .put = ad1988_spdif_playback_source_put,
2078 },
2079 { } /* end */
2080};
2081
Takashi Iwai498f5b12011-05-02 11:33:15 +02002082static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002083 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2084 { } /* end */
2085};
2086
Takashi Iwai498f5b12011-05-02 11:33:15 +02002087static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002088 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002089 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002090 { } /* end */
2091};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002092
2093/*
2094 * initialization verbs
2095 */
2096
2097/*
2098 * for 6-stack (+dig)
2099 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002100static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002101 /* Front, Surround, CLFE, side DAC; unmute as default */
2102 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2103 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2104 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2105 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002106 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002107 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002108 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2109 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2110 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2111 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2112 /* Port-D line-out path */
2113 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2114 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2115 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2116 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2117 /* Port-F surround path */
2118 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2119 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2120 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2121 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2122 /* Port-G CLFE path */
2123 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2124 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2125 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2126 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2127 /* Port-H side path */
2128 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2129 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2130 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2131 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2132 /* Mono out path */
2133 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2134 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2135 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2136 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2137 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2138 /* Port-B front mic-in path */
2139 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2140 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2141 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2142 /* Port-C line-in path */
2143 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2144 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2145 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2146 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2147 /* Port-E mic-in path */
2148 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2149 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2150 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2151 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002152 /* Analog CD Input */
2153 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002154 /* Analog Mix output amp */
2155 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002156
2157 { }
2158};
2159
Takashi Iwai498f5b12011-05-02 11:33:15 +02002160static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002161 /* Headphone; unmute as default */
2162 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2163 /* Port-A front headphon path */
2164 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2165 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2166 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2167 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2168 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002169
2170 { }
2171};
2172
Takashi Iwai498f5b12011-05-02 11:33:15 +02002173static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002174 /* mute analog mix */
2175 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2176 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2177 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2178 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2179 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2180 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2181 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2182 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2183 /* select ADCs - front-mic */
2184 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2185 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2186 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002187
2188 { }
2189};
2190
Takashi Iwai498f5b12011-05-02 11:33:15 +02002191static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002192 /* SPDIF out sel */
2193 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2194 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2195 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002196 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002197 /* SPDIF out pin */
2198 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002199
2200 { }
2201};
2202
Takashi Iwai498f5b12011-05-02 11:33:15 +02002203static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002204 /* unmute SPDIF input pin */
2205 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2206 { }
2207};
2208
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002209/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002210static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002211 /* SPDIF-1 out pin */
2212 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002213 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002214 /* SPDIF-2/HDMI out pin */
2215 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2216 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002217 { }
2218};
2219
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002220/*
2221 * verbs for 3stack (+dig)
2222 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002223static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002224 /* set port-C to line-in */
2225 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2226 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2227 /* set port-E to mic-in */
2228 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2229 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2230 { } /* end */
2231};
2232
Takashi Iwai498f5b12011-05-02 11:33:15 +02002233static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002234 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002235 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002236 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002237 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002238 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002239 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002240 { } /* end */
2241};
2242
Takashi Iwai498f5b12011-05-02 11:33:15 +02002243static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002244 { 2, ad1988_3stack_ch2_init },
2245 { 6, ad1988_3stack_ch6_init },
2246};
2247
Takashi Iwai498f5b12011-05-02 11:33:15 +02002248static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002249 /* Front, Surround, CLFE, side DAC; unmute as default */
2250 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2251 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2252 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2253 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002254 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002255 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002256 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2257 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2258 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2259 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2260 /* Port-D line-out path */
2261 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2262 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2263 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2264 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2265 /* Mono out path */
2266 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2267 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2268 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2269 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2270 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2271 /* Port-B front mic-in path */
2272 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2273 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2274 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002275 /* Port-C line-in/surround path - 6ch mode as default */
2276 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2277 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002278 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002279 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002280 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002281 /* Port-E mic-in/CLFE path - 6ch mode as default */
2282 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2283 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002284 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002285 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002286 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2287 /* mute analog mix */
2288 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2289 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2290 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2291 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2292 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2293 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2294 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2295 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2296 /* select ADCs - front-mic */
2297 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2298 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2299 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002300 /* Analog Mix output amp */
2301 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002302 { }
2303};
2304
2305/*
2306 * verbs for laptop mode (+dig)
2307 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002308static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002309 /* unmute port-A and mute port-D */
2310 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2311 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2312 { } /* end */
2313};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002314static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002315 /* mute port-A and unmute port-D */
2316 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2317 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2318 { } /* end */
2319};
2320
2321#define AD1988_HP_EVENT 0x01
2322
Takashi Iwai498f5b12011-05-02 11:33:15 +02002323static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002324 /* Front, Surround, CLFE, side DAC; unmute as default */
2325 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2326 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2327 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2328 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002329 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002330 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002331 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2332 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2333 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2334 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2335 /* unsolicited event for pin-sense */
2336 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2337 /* Port-D line-out path + EAPD */
2338 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2339 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2340 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2341 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2342 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2343 /* Mono out path */
2344 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2345 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2346 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2347 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2348 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2349 /* Port-B mic-in path */
2350 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2351 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2352 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2353 /* Port-C docking station - try to output */
2354 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2355 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2356 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2357 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2358 /* mute analog mix */
2359 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2360 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2361 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2362 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2363 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2364 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2365 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2366 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2367 /* select ADCs - mic */
2368 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2369 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2370 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002371 /* Analog Mix output amp */
2372 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002373 { }
2374};
2375
2376static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2377{
2378 if ((res >> 26) != AD1988_HP_EVENT)
2379 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002380 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002381 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2382 else
2383 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2384}
2385
Takashi Iwai83012a72012-08-24 18:38:08 +02002386#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02002387static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002388 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2389 { 0x20, HDA_INPUT, 1 }, /* Line */
2390 { 0x20, HDA_INPUT, 4 }, /* Mic */
2391 { 0x20, HDA_INPUT, 6 }, /* CD */
2392 { } /* end */
2393};
2394#endif
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002395#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002396
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002397static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
2398 struct snd_ctl_elem_info *uinfo)
2399{
2400 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2401 static const char * const texts[] = {
2402 "PCM", "ADC1", "ADC2", "ADC3",
2403 };
2404 int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
2405 if (num_conns > 4)
2406 num_conns = 4;
2407 return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
2408}
2409
2410static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
2411 struct snd_ctl_elem_value *ucontrol)
2412{
2413 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2414 struct ad198x_spec *spec = codec->spec;
2415
2416 ucontrol->value.enumerated.item[0] = spec->cur_smux;
2417 return 0;
2418}
2419
2420static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
2421 struct snd_ctl_elem_value *ucontrol)
2422{
2423 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2424 struct ad198x_spec *spec = codec->spec;
2425 unsigned int val = ucontrol->value.enumerated.item[0];
2426 struct nid_path *path;
2427 int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
2428
2429 if (val >= num_conns)
2430 return -EINVAL;
2431 if (spec->cur_smux == val)
2432 return 0;
2433
2434 mutex_lock(&codec->control_mutex);
2435 codec->cached_write = 1;
2436 path = snd_hda_get_path_from_idx(codec,
2437 spec->smux_paths[spec->cur_smux]);
2438 if (path)
2439 snd_hda_activate_path(codec, path, false, true);
2440 path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
2441 if (path)
2442 snd_hda_activate_path(codec, path, true, true);
2443 spec->cur_smux = val;
2444 codec->cached_write = 0;
2445 mutex_unlock(&codec->control_mutex);
2446 snd_hda_codec_flush_cache(codec); /* flush the updates */
2447 return 1;
2448}
2449
2450static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
2451 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2452 .name = "IEC958 Playback Source",
2453 .info = ad1988_auto_smux_enum_info,
2454 .get = ad1988_auto_smux_enum_get,
2455 .put = ad1988_auto_smux_enum_put,
2456};
2457
2458static int ad1988_auto_init(struct hda_codec *codec)
2459{
2460 struct ad198x_spec *spec = codec->spec;
2461 int i, err;
2462
2463 err = snd_hda_gen_init(codec);
2464 if (err < 0)
2465 return err;
2466 if (!spec->gen.autocfg.dig_outs)
2467 return 0;
2468
2469 for (i = 0; i < 4; i++) {
2470 struct nid_path *path;
2471 path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
2472 if (path)
2473 snd_hda_activate_path(codec, path, path->active, false);
2474 }
2475
2476 return 0;
2477}
2478
2479static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
2480{
2481 struct ad198x_spec *spec = codec->spec;
2482 int i, num_conns;
2483 /* we create four static faked paths, since AD codecs have odd
2484 * widget connections regarding the SPDIF out source
2485 */
2486 static struct nid_path fake_paths[4] = {
2487 {
2488 .depth = 3,
2489 .path = { 0x02, 0x1d, 0x1b },
2490 .idx = { 0, 0, 0 },
2491 .multi = { 0, 0, 0 },
2492 },
2493 {
2494 .depth = 4,
2495 .path = { 0x08, 0x0b, 0x1d, 0x1b },
2496 .idx = { 0, 0, 1, 0 },
2497 .multi = { 0, 1, 0, 0 },
2498 },
2499 {
2500 .depth = 4,
2501 .path = { 0x09, 0x0b, 0x1d, 0x1b },
2502 .idx = { 0, 1, 1, 0 },
2503 .multi = { 0, 1, 0, 0 },
2504 },
2505 {
2506 .depth = 4,
2507 .path = { 0x0f, 0x0b, 0x1d, 0x1b },
2508 .idx = { 0, 2, 1, 0 },
2509 .multi = { 0, 1, 0, 0 },
2510 },
2511 };
2512
2513 /* SPDIF source mux appears to be present only on AD1988A */
2514 if (!spec->gen.autocfg.dig_outs ||
2515 get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
2516 return 0;
2517
2518 num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
2519 if (num_conns != 3 && num_conns != 4)
2520 return 0;
2521
2522 for (i = 0; i < num_conns; i++) {
2523 struct nid_path *path = snd_array_new(&spec->gen.paths);
2524 if (!path)
2525 return -ENOMEM;
2526 *path = fake_paths[i];
2527 if (!i)
2528 path->active = 1;
2529 spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
2530 }
2531
2532 if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
2533 return -ENOMEM;
2534
2535 codec->patch_ops.init = ad1988_auto_init;
2536
2537 return 0;
2538}
2539
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002540/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002541 */
2542
Takashi Iwaid32410b12005-11-24 16:06:23 +01002543static int ad1988_parse_auto_config(struct hda_codec *codec)
2544{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002545 struct ad198x_spec *spec;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002546 int err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002547
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002548 err = alloc_ad_spec(codec);
2549 if (err < 0)
2550 return err;
2551 spec = codec->spec;
2552
Takashi Iwaif2f8be42013-01-21 16:40:16 +01002553 spec->gen.mixer_nid = 0x20;
Takashi Iwaie4a395e2013-01-23 17:00:31 +01002554 spec->gen.mixer_merge_nid = 0x21;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002555 spec->gen.beep_nid = 0x10;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002556 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002557 err = ad198x_parse_auto_config(codec);
2558 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002559 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002560 err = ad1988_add_spdif_mux_ctl(codec);
2561 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002562 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002563 return 0;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002564
2565 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002566 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002567 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002568}
2569
Takashi Iwaid32410b12005-11-24 16:06:23 +01002570/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002571 */
2572
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002573#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwaiea734962011-01-17 11:29:34 +01002574static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002575 [AD1988_6STACK] = "6stack",
2576 [AD1988_6STACK_DIG] = "6stack-dig",
2577 [AD1988_3STACK] = "3stack",
2578 [AD1988_3STACK_DIG] = "3stack-dig",
2579 [AD1988_LAPTOP] = "laptop",
2580 [AD1988_LAPTOP_DIG] = "laptop-dig",
2581 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002582};
2583
Takashi Iwai498f5b12011-05-02 11:33:15 +02002584static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002585 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002586 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02002587 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01002588 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07002589 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002590 {}
2591};
2592
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002593static int patch_ad1988(struct hda_codec *codec)
2594{
2595 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002596 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002597
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002598 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01002599 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002600 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02002601 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
2602 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002603 board_config = AD1988_AUTO;
2604 }
2605
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002606 if (board_config == AD1988_AUTO)
2607 return ad1988_parse_auto_config(codec);
2608
2609 err = alloc_ad_spec(codec);
2610 if (err < 0)
2611 return err;
2612 spec = codec->spec;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002613
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002614 if (is_rev2(codec))
2615 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
2616
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002617 err = snd_hda_attach_beep_device(codec, 0x10);
2618 if (err < 0) {
2619 ad198x_free(codec);
2620 return err;
2621 }
2622 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
2623
Raymond Yau356aab72011-08-31 10:30:59 +08002624 if (!spec->multiout.hp_nid)
Raymond Yau34588702011-09-23 19:03:25 +08002625 spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002626 switch (board_config) {
2627 case AD1988_6STACK:
2628 case AD1988_6STACK_DIG:
2629 spec->multiout.max_channels = 8;
2630 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002631 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002632 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
2633 else
2634 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002635 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002636 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002637 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002638 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
2639 else
2640 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08002641 spec->mixers[1] = ad1988_6stack_mixers2;
2642 spec->num_init_verbs = 1;
2643 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yau34588702011-09-23 19:03:25 +08002644 if (board_config == AD1988_6STACK_DIG) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002645 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2646 spec->dig_in_nid = AD1988_SPDIF_IN;
2647 }
2648 break;
2649 case AD1988_3STACK:
2650 case AD1988_3STACK_DIG:
2651 spec->multiout.max_channels = 6;
2652 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002653 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002654 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
2655 else
2656 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002657 spec->input_mux = &ad1988_6stack_capture_source;
2658 spec->channel_mode = ad1988_3stack_modes;
2659 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002660 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02002661 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002662 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
2663 else
2664 spec->mixers[0] = ad1988_3stack_mixers1;
2665 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002666 spec->num_init_verbs = 1;
2667 spec->init_verbs[0] = ad1988_3stack_init_verbs;
2668 if (board_config == AD1988_3STACK_DIG)
2669 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2670 break;
2671 case AD1988_LAPTOP:
2672 case AD1988_LAPTOP_DIG:
2673 spec->multiout.max_channels = 2;
2674 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002675 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002676 spec->input_mux = &ad1988_laptop_capture_source;
2677 spec->num_mixers = 1;
2678 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002679 codec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002680 spec->num_init_verbs = 1;
2681 spec->init_verbs[0] = ad1988_laptop_init_verbs;
2682 if (board_config == AD1988_LAPTOP_DIG)
2683 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
2684 break;
2685 }
2686
Takashi Iwaid32410b12005-11-24 16:06:23 +01002687 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
2688 spec->adc_nids = ad1988_adc_nids;
2689 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002690 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
2691 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
2692 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002693 if (codec->vendor_id >= 0x11d4989a) {
2694 spec->mixers[spec->num_mixers++] =
2695 ad1989_spdif_out_mixers;
2696 spec->init_verbs[spec->num_init_verbs++] =
2697 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002698 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002699 } else {
2700 spec->mixers[spec->num_mixers++] =
2701 ad1988_spdif_out_mixers;
2702 spec->init_verbs[spec->num_init_verbs++] =
2703 ad1988_spdif_init_verbs;
2704 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002705 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002706 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002707 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002708 spec->init_verbs[spec->num_init_verbs++] =
2709 ad1988_spdif_in_init_verbs;
2710 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002711
2712 codec->patch_ops = ad198x_patch_ops;
2713 switch (board_config) {
2714 case AD1988_LAPTOP:
2715 case AD1988_LAPTOP_DIG:
2716 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
2717 break;
2718 }
Takashi Iwai83012a72012-08-24 18:38:08 +02002719#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02002720 spec->loopback.amplist = ad1988_loopbacks;
2721#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002722 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002723
Takashi Iwai729d55b2009-12-25 22:49:01 +01002724 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002725 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002726
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002727 return 0;
2728}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002729#else /* ENABLE_AD_STATIC_QUIRKS */
2730#define patch_ad1988 ad1988_parse_auto_config
2731#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002732
2733
2734/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02002735 * AD1884 / AD1984
2736 *
2737 * port-B - front line/mic-in
2738 * port-E - aux in/out
2739 * port-F - aux in/out
2740 * port-C - rear line/mic-in
2741 * port-D - rear line/hp-out
2742 * port-A - front line/hp-out
2743 *
2744 * AD1984 = AD1884 + two digital mic-ins
2745 *
Takashi Iwai5ccc6182013-07-04 15:36:56 +02002746 * AD1883 / AD1884A / AD1984A / AD1984B
2747 *
2748 * port-B (0x14) - front mic-in
2749 * port-E (0x1c) - rear mic-in
2750 * port-F (0x16) - CD / ext out
2751 * port-C (0x15) - rear line-in
2752 * port-D (0x12) - rear line-out
2753 * port-A (0x11) - front hp-out
2754 *
2755 * AD1984A = AD1884A + digital-mic
2756 * AD1883 = equivalent with AD1984A
2757 * AD1984B = AD1984A + extra SPDIF-out
Takashi Iwai2bac6472007-05-18 18:21:41 +02002758 */
2759
Takashi Iwaia928bd22013-01-22 18:18:42 +01002760/* set the upper-limit for mixer amp to 0dB for avoiding the possible
2761 * damage by overloading
2762 */
2763static void ad1884_fixup_amp_override(struct hda_codec *codec,
2764 const struct hda_fixup *fix, int action)
2765{
2766 if (action == HDA_FIXUP_ACT_PRE_PROBE)
2767 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
2768 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2769 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2770 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2771 (1 << AC_AMPCAP_MUTE_SHIFT));
2772}
2773
Takashi Iwai1a39b5e2013-07-04 14:32:16 +02002774/* toggle GPIO1 according to the mute state */
2775static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
2776{
2777 struct hda_codec *codec = private_data;
2778 struct ad198x_spec *spec = codec->spec;
2779
2780 if (spec->eapd_nid)
2781 ad_vmaster_eapd_hook(private_data, enabled);
2782 snd_hda_codec_update_cache(codec, 0x01, 0,
2783 AC_VERB_SET_GPIO_DATA,
2784 enabled ? 0x00 : 0x02);
2785}
2786
Takashi Iwaia928bd22013-01-22 18:18:42 +01002787static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
2788 const struct hda_fixup *fix, int action)
2789{
2790 struct ad198x_spec *spec = codec->spec;
Takashi Iwai1a39b5e2013-07-04 14:32:16 +02002791 static const struct hda_verb gpio_init_verbs[] = {
2792 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
2793 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
2794 {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
2795 {},
2796 };
Takashi Iwaia928bd22013-01-22 18:18:42 +01002797
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02002798 switch (action) {
2799 case HDA_FIXUP_ACT_PRE_PROBE:
Takashi Iwai1a39b5e2013-07-04 14:32:16 +02002800 spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
2801 snd_hda_sequence_write_cache(codec, gpio_init_verbs);
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02002802 break;
2803 case HDA_FIXUP_ACT_PROBE:
Takashi Iwaia928bd22013-01-22 18:18:42 +01002804 if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
2805 spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
2806 else
2807 spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
Takashi Iwai8f0b3b72013-07-04 12:54:22 +02002808 break;
Takashi Iwaia928bd22013-01-22 18:18:42 +01002809 }
2810}
2811
Takashi Iwai6a699be2013-07-04 14:45:37 +02002812/* set magic COEFs for dmic */
2813static const struct hda_verb ad1884_dmic_init_verbs[] = {
2814 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
2815 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
2816 {}
2817};
2818
Takashi Iwaia928bd22013-01-22 18:18:42 +01002819enum {
2820 AD1884_FIXUP_AMP_OVERRIDE,
2821 AD1884_FIXUP_HP_EAPD,
Takashi Iwai6a699be2013-07-04 14:45:37 +02002822 AD1884_FIXUP_DMIC_COEF,
Takashi Iwaif4046272013-07-04 15:14:17 +02002823 AD1884_FIXUP_HP_TOUCHSMART,
Takashi Iwaia928bd22013-01-22 18:18:42 +01002824};
2825
2826static const struct hda_fixup ad1884_fixups[] = {
2827 [AD1884_FIXUP_AMP_OVERRIDE] = {
2828 .type = HDA_FIXUP_FUNC,
2829 .v.func = ad1884_fixup_amp_override,
2830 },
2831 [AD1884_FIXUP_HP_EAPD] = {
2832 .type = HDA_FIXUP_FUNC,
2833 .v.func = ad1884_fixup_hp_eapd,
2834 .chained = true,
2835 .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
2836 },
Takashi Iwai6a699be2013-07-04 14:45:37 +02002837 [AD1884_FIXUP_DMIC_COEF] = {
2838 .type = HDA_FIXUP_VERBS,
2839 .v.verbs = ad1884_dmic_init_verbs,
2840 },
Takashi Iwaif4046272013-07-04 15:14:17 +02002841 [AD1884_FIXUP_HP_TOUCHSMART] = {
2842 .type = HDA_FIXUP_VERBS,
2843 .v.verbs = ad1884_dmic_init_verbs,
2844 .chained = true,
2845 .chain_id = AD1884_FIXUP_HP_EAPD,
2846 },
Takashi Iwaia928bd22013-01-22 18:18:42 +01002847};
2848
2849static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
Takashi Iwaif4046272013-07-04 15:14:17 +02002850 SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
Takashi Iwaia928bd22013-01-22 18:18:42 +01002851 SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
Takashi Iwai6a699be2013-07-04 14:45:37 +02002852 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF),
Takashi Iwaia928bd22013-01-22 18:18:42 +01002853 {}
2854};
2855
2856
Takashi Iwai5ccc6182013-07-04 15:36:56 +02002857static int patch_ad1884(struct hda_codec *codec)
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002858{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002859 struct ad198x_spec *spec;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002860 int err;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002861
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002862 err = alloc_ad_spec(codec);
2863 if (err < 0)
2864 return err;
2865 spec = codec->spec;
2866
Takashi Iwaif2f8be42013-01-21 16:40:16 +01002867 spec->gen.mixer_nid = 0x20;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002868 spec->gen.beep_nid = 0x10;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002869 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
Takashi Iwaia928bd22013-01-22 18:18:42 +01002870
2871 snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
2872 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
2873
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002874 err = ad198x_parse_auto_config(codec);
2875 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002876 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002877 err = ad1983_add_spdif_mux_ctl(codec);
2878 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002879 goto error;
Takashi Iwaia928bd22013-01-22 18:18:42 +01002880
2881 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
2882
Takashi Iwai272f3ea2013-01-22 15:31:33 +01002883 return 0;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002884
2885 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002886 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002887 return err;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002888}
2889
Takashi Iwaic5059252008-02-16 09:43:56 +01002890/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02002891 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02002892 *
2893 * port-A - front hp-out
2894 * port-B - front mic-in
2895 * port-C - rear line-in, shared surr-out (3stack)
2896 * port-D - rear line-out
2897 * port-E - rear mic-in, shared clfe-out (3stack)
2898 * port-F - rear surr-out (6stack)
2899 * port-G - rear clfe-out (6stack)
2900 */
2901
Takashi Iwaiaa95d612013-07-04 15:16:31 +02002902static int patch_ad1882(struct hda_codec *codec)
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002903{
Takashi Iwai0ac85512007-06-20 15:46:13 +02002904 struct ad198x_spec *spec;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002905 int err;
Takashi Iwai0ac85512007-06-20 15:46:13 +02002906
Takashi Iwai361dab32012-05-09 14:35:27 +02002907 err = alloc_ad_spec(codec);
2908 if (err < 0)
2909 return err;
2910 spec = codec->spec;
Takashi Iwai0ac85512007-06-20 15:46:13 +02002911
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002912 spec->gen.mixer_nid = 0x20;
Takashi Iwaie4a395e2013-01-23 17:00:31 +01002913 spec->gen.mixer_merge_nid = 0x21;
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002914 spec->gen.beep_nid = 0x10;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002915 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
2916 err = ad198x_parse_auto_config(codec);
2917 if (err < 0)
2918 goto error;
2919 err = ad1988_add_spdif_mux_ctl(codec);
2920 if (err < 0)
2921 goto error;
2922 return 0;
2923
2924 error:
Takashi Iwai7504b6c2013-03-18 11:25:51 +01002925 snd_hda_gen_free(codec);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002926 return err;
2927}
2928
Takashi Iwai0ac85512007-06-20 15:46:13 +02002929
2930/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002931 * patch entries
2932 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002933static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwai5ccc6182013-07-04 15:36:56 +02002934 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
Takashi Iwai0ac85512007-06-20 15:46:13 +02002935 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwai5ccc6182013-07-04 15:36:56 +02002936 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02002937 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwai5ccc6182013-07-04 15:36:56 +02002938 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
2939 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002940 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
2941 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai5ccc6182013-07-04 15:36:56 +02002942 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002944 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02002945 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02002946 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002947 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
2948 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 {} /* terminator */
2950};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01002951
2952MODULE_ALIAS("snd-hda-codec-id:11d4*");
2953
2954MODULE_LICENSE("GPL");
2955MODULE_DESCRIPTION("Analog Devices HD-audio codec");
2956
2957static struct hda_codec_preset_list analog_list = {
2958 .preset = snd_hda_preset_analog,
2959 .owner = THIS_MODULE,
2960};
2961
2962static int __init patch_analog_init(void)
2963{
2964 return snd_hda_add_codec_preset(&analog_list);
2965}
2966
2967static void __exit patch_analog_exit(void)
2968{
2969 snd_hda_delete_codec_preset(&analog_list);
2970}
2971
2972module_init(patch_analog_init)
2973module_exit(patch_analog_exit)