blob: df8014b2759649cb19c5372eb24afc29d5bde702 [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 Iwai78bb3cb2012-12-21 15:17:06 +010046 hda_nid_t beep_dev_nid;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +010047
48#ifdef ENABLE_AD_STATIC_QUIRKS
49 const struct snd_kcontrol_new *mixers[6];
50 int num_mixers;
Raymond Yau28220842011-02-08 19:58:25 +080051 const struct hda_verb *init_verbs[6]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010052 * don't forget NULL termination!
53 */
54 unsigned int num_init_verbs;
55
56 /* playback */
57 struct hda_multi_out multiout; /* playback set-up
58 * max_channels, dacs must be set
59 * dig_out_nid and hp_nid are optional
60 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010061 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020062 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010063
64 /* capture */
65 unsigned int num_adc_nids;
Takashi Iwai498f5b12011-05-02 11:33:15 +020066 const hda_nid_t *adc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010067 hda_nid_t dig_in_nid; /* digital-in NID; optional */
68
69 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020070 const struct hda_input_mux *input_mux;
Takashi Iwai498f5b12011-05-02 11:33:15 +020071 const hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010072 unsigned int cur_mux[3];
73
74 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010075 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010076 int num_channel_mode;
77
78 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020079 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010080
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020081 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010082
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010083 unsigned int jack_present: 1;
84 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010085 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai18478e82012-03-09 17:51:10 +010086 unsigned int avoid_init_slave_vol:1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +020087
Takashi Iwai83012a72012-08-24 18:38:08 +020088#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +020089 struct hda_loopback_check loopback;
90#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010091 /* for virtual master */
92 hda_nid_t vmaster_nid;
Takashi Iwaiea734962011-01-17 11:29:34 +010093 const char * const *slave_vols;
94 const char * const *slave_sws;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +010095#endif /* ENABLE_AD_STATIC_QUIRKS */
Linus Torvalds1da177e2005-04-16 15:20:36 -070096};
97
Takashi Iwai9ff4bc82013-01-22 16:45:58 +010098#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020099/*
100 * input MUX handling (common part)
101 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100102static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200103{
104 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
105 struct ad198x_spec *spec = codec->spec;
106
107 return snd_hda_input_mux_info(spec->input_mux, uinfo);
108}
109
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100110static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200111{
112 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
113 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100114 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200115
Takashi Iwai985be542005-11-02 18:26:49 +0100116 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200117 return 0;
118}
119
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100120static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200121{
122 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
123 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100124 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200125
126 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100127 spec->capsrc_nids[adc_idx],
128 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200129}
130
131/*
132 * initialization (common callbacks)
133 */
134static int ad198x_init(struct hda_codec *codec)
135{
136 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100137 int i;
138
139 for (i = 0; i < spec->num_init_verbs; i++)
140 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200141 return 0;
142}
143
Takashi Iwai9322ca52012-02-03 14:28:01 +0100144static const char * const ad_slave_pfxs[] = {
145 "Front", "Surround", "Center", "LFE", "Side",
146 "Headphone", "Mono", "Speaker", "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100147 NULL
148};
149
Takashi Iwai9322ca52012-02-03 14:28:01 +0100150static const char * const ad1988_6stack_fp_slave_pfxs[] = {
151 "Front", "Surround", "Center", "LFE", "Side", "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100152 NULL
153};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100154#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai2134ea42008-01-10 16:53:55 +0100155
Takashi Iwai67d634c2009-11-16 15:35:59 +0100156#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100157/* additional beep mixers; the actual parameters are overwritten at build */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200158static const struct snd_kcontrol_new ad_beep_mixer[] = {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100159 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200160 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100161 { } /* end */
162};
163
Takashi Iwai498f5b12011-05-02 11:33:15 +0200164static const struct snd_kcontrol_new ad_beep2_mixer[] = {
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100165 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
166 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
167 { } /* end */
168};
169
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100170#define set_beep_amp(spec, nid, idx, dir) \
171 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100172#else
173#define set_beep_amp(spec, nid, idx, dir) /* NOP */
174#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100175
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100176#ifdef CONFIG_SND_HDA_INPUT_BEEP
177static int create_beep_ctls(struct hda_codec *codec)
178{
179 struct ad198x_spec *spec = codec->spec;
180 const struct snd_kcontrol_new *knew;
181
182 if (!spec->beep_amp)
183 return 0;
184
185 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
186 for ( ; knew->name; knew++) {
187 int err;
188 struct snd_kcontrol *kctl;
189 kctl = snd_ctl_new1(knew, codec);
190 if (!kctl)
191 return -ENOMEM;
192 kctl->private_value = spec->beep_amp;
193 err = snd_hda_ctl_add(codec, 0, kctl);
194 if (err < 0)
195 return err;
196 }
197 return 0;
198}
199#else
200#define create_beep_ctls(codec) 0
201#endif
202
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100203#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200204static int ad198x_build_controls(struct hda_codec *codec)
205{
206 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100207 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100208 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200209 int err;
210
Takashi Iwai985be542005-11-02 18:26:49 +0100211 for (i = 0; i < spec->num_mixers; i++) {
212 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
213 if (err < 0)
214 return err;
215 }
216 if (spec->multiout.dig_out_nid) {
Stephen Warren74b654c2011-06-01 11:14:18 -0600217 err = snd_hda_create_spdif_out_ctls(codec,
218 spec->multiout.dig_out_nid,
219 spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100220 if (err < 0)
221 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100222 err = snd_hda_create_spdif_share_sw(codec,
223 &spec->multiout);
224 if (err < 0)
225 return err;
226 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100227 }
228 if (spec->dig_in_nid) {
229 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
230 if (err < 0)
231 return err;
232 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100233
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100234 /* create beep controls if needed */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100235 err = create_beep_ctls(codec);
236 if (err < 0)
237 return err;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100238
Takashi Iwai2134ea42008-01-10 16:53:55 +0100239 /* if we have no master control, let's create it */
240 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100241 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100242 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100243 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai18478e82012-03-09 17:51:10 +0100244 err = __snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100245 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100246 (spec->slave_vols ?
Takashi Iwai9322ca52012-02-03 14:28:01 +0100247 spec->slave_vols : ad_slave_pfxs),
Takashi Iwai18478e82012-03-09 17:51:10 +0100248 "Playback Volume",
Takashi Iwai29e58532012-03-12 12:25:03 +0100249 !spec->avoid_init_slave_vol, NULL);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100250 if (err < 0)
251 return err;
252 }
253 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
254 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
255 NULL,
256 (spec->slave_sws ?
Takashi Iwai9322ca52012-02-03 14:28:01 +0100257 spec->slave_sws : ad_slave_pfxs),
258 "Playback Switch");
Takashi Iwai2134ea42008-01-10 16:53:55 +0100259 if (err < 0)
260 return err;
261 }
262
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100263 /* assign Capture Source enums to NID */
264 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
265 if (!kctl)
266 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
267 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100268 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100269 if (err < 0)
270 return err;
271 }
272
273 /* assign IEC958 enums to NID */
274 kctl = snd_hda_find_mixer_ctl(codec,
275 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
276 if (kctl) {
277 err = snd_hda_add_nid(codec, kctl, 0,
278 spec->multiout.dig_out_nid);
279 if (err < 0)
280 return err;
281 }
282
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200283 return 0;
284}
285
Takashi Iwai83012a72012-08-24 18:38:08 +0200286#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +0200287static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
288{
289 struct ad198x_spec *spec = codec->spec;
290 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
291}
292#endif
293
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200294/*
295 * Analog playback callbacks
296 */
297static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
298 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100299 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200300{
301 struct ad198x_spec *spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100302 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
Takashi Iwai9a081602008-02-12 18:37:26 +0100303 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200304}
305
306static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
307 struct hda_codec *codec,
308 unsigned int stream_tag,
309 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100310 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200311{
312 struct ad198x_spec *spec = codec->spec;
313 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
314 format, substream);
315}
316
317static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
318 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100319 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200320{
321 struct ad198x_spec *spec = codec->spec;
322 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
323}
324
325/*
326 * Digital out
327 */
328static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
329 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100330 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200331{
332 struct ad198x_spec *spec = codec->spec;
333 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
334}
335
336static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
337 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100338 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200339{
340 struct ad198x_spec *spec = codec->spec;
341 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
342}
343
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200344static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
345 struct hda_codec *codec,
346 unsigned int stream_tag,
347 unsigned int format,
348 struct snd_pcm_substream *substream)
349{
350 struct ad198x_spec *spec = codec->spec;
351 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
352 format, substream);
353}
354
Takashi Iwai9411e212009-02-13 11:32:28 +0100355static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
356 struct hda_codec *codec,
357 struct snd_pcm_substream *substream)
358{
359 struct ad198x_spec *spec = codec->spec;
360 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
361}
362
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200363/*
364 * Analog capture
365 */
366static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
367 struct hda_codec *codec,
368 unsigned int stream_tag,
369 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100370 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200371{
372 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100373 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
374 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200375 return 0;
376}
377
378static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
379 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100380 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200381{
382 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100383 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200384 return 0;
385}
386
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200387/*
388 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200389static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200390 .substreams = 1,
391 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100392 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200393 .nid = 0, /* fill later */
394 .ops = {
395 .open = ad198x_playback_pcm_open,
396 .prepare = ad198x_playback_pcm_prepare,
Raymond Yau34588702011-09-23 19:03:25 +0800397 .cleanup = ad198x_playback_pcm_cleanup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200398 },
399};
400
Takashi Iwai498f5b12011-05-02 11:33:15 +0200401static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100402 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200403 .channels_min = 2,
404 .channels_max = 2,
405 .nid = 0, /* fill later */
406 .ops = {
407 .prepare = ad198x_capture_pcm_prepare,
408 .cleanup = ad198x_capture_pcm_cleanup
409 },
410};
411
Takashi Iwai498f5b12011-05-02 11:33:15 +0200412static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200413 .substreams = 1,
414 .channels_min = 2,
415 .channels_max = 2,
416 .nid = 0, /* fill later */
417 .ops = {
418 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200419 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100420 .prepare = ad198x_dig_playback_pcm_prepare,
421 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200422 },
423};
424
Takashi Iwai498f5b12011-05-02 11:33:15 +0200425static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100426 .substreams = 1,
427 .channels_min = 2,
428 .channels_max = 2,
429 /* NID is set in alc_build_pcms */
430};
431
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200432static int ad198x_build_pcms(struct hda_codec *codec)
433{
434 struct ad198x_spec *spec = codec->spec;
435 struct hda_pcm *info = spec->pcm_rec;
436
437 codec->num_pcms = 1;
438 codec->pcm_info = info;
439
440 info->name = "AD198x Analog";
441 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
442 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
443 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
444 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100445 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
446 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200447
448 if (spec->multiout.dig_out_nid) {
449 info++;
450 codec->num_pcms++;
Takashi Iwaiae24c312012-11-05 12:32:46 +0100451 codec->spdif_status_reset = 1;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200452 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100453 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200454 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
455 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100456 if (spec->dig_in_nid) {
457 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
458 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
459 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200460 }
461
462 return 0;
463}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100464#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200465
Daniel T Chenea52bf22009-12-27 18:48:29 -0500466static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
467 hda_nid_t hp)
468{
Raymond Yaua01ef052011-06-01 15:09:48 +0800469 if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
470 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100471 !codec->inv_eapd ? 0x00 : 0x02);
Raymond Yaua01ef052011-06-01 15:09:48 +0800472 if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
473 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100474 !codec->inv_eapd ? 0x00 : 0x02);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500475}
476
477static void ad198x_power_eapd(struct hda_codec *codec)
478{
479 /* We currently only handle front, HP */
480 switch (codec->vendor_id) {
481 case 0x11d41882:
482 case 0x11d4882a:
483 case 0x11d41884:
484 case 0x11d41984:
485 case 0x11d41883:
486 case 0x11d4184a:
487 case 0x11d4194a:
488 case 0x11d4194b:
Takashi Iwai4dffbe02011-06-03 10:05:02 +0200489 case 0x11d41988:
490 case 0x11d4198b:
491 case 0x11d4989a:
492 case 0x11d4989b:
Daniel T Chenea52bf22009-12-27 18:48:29 -0500493 ad198x_power_eapd_write(codec, 0x12, 0x11);
494 break;
495 case 0x11d41981:
496 case 0x11d41983:
497 ad198x_power_eapd_write(codec, 0x05, 0x06);
498 break;
499 case 0x11d41986:
500 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
501 break;
Daniel T Chenea52bf22009-12-27 18:48:29 -0500502 }
503}
504
Takashi Iwai0da26922011-04-26 15:18:33 +0200505static void ad198x_shutup(struct hda_codec *codec)
506{
507 snd_hda_shutup_pins(codec);
508 ad198x_power_eapd(codec);
509}
510
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200511static void ad198x_free(struct hda_codec *codec)
512{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100513 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100514
Takashi Iwai603c4012008-07-30 15:01:44 +0200515 if (!spec)
516 return;
517
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100518 snd_hda_gen_spec_free(&spec->gen);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100519 kfree(spec);
520 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200521}
522
Takashi Iwai2a439522011-07-26 09:52:50 +0200523#ifdef CONFIG_PM
Takashi Iwai68cb2b52012-07-02 15:20:37 +0200524static int ad198x_suspend(struct hda_codec *codec)
Daniel T Chenea52bf22009-12-27 18:48:29 -0500525{
526 ad198x_shutup(codec);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500527 return 0;
528}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500529#endif
530
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100531#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +0200532static const struct hda_codec_ops ad198x_patch_ops = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200533 .build_controls = ad198x_build_controls,
534 .build_pcms = ad198x_build_pcms,
535 .init = ad198x_init,
536 .free = ad198x_free,
Takashi Iwai2a439522011-07-26 09:52:50 +0200537#ifdef CONFIG_PM
Takashi Iwai83012a72012-08-24 18:38:08 +0200538 .check_power_status = ad198x_check_power_status,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500539 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500540#endif
541 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200542};
543
544
545/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100546 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100547 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100548 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200549#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100550
551static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
552 struct snd_ctl_elem_value *ucontrol)
553{
554 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
555 struct ad198x_spec *spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100556 if (codec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100557 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
558 else
559 ucontrol->value.integer.value[0] = spec->cur_eapd;
560 return 0;
561}
562
563static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
564 struct snd_ctl_elem_value *ucontrol)
565{
566 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
567 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100568 hda_nid_t nid = kcontrol->private_value & 0xff;
569 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100570 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100571 if (codec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100572 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200573 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100574 return 0;
575 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200576 snd_hda_codec_write_cache(codec, nid,
577 0, AC_VERB_SET_EAPD_BTLENABLE,
578 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100579 return 1;
580}
581
Takashi Iwai9230d212006-03-13 13:49:49 +0100582static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
583 struct snd_ctl_elem_info *uinfo);
584static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
585 struct snd_ctl_elem_value *ucontrol);
586static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
587 struct snd_ctl_elem_value *ucontrol);
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100588#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai9230d212006-03-13 13:49:49 +0100589
590
Takashi Iwai18a815d2006-03-01 19:54:39 +0100591/*
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100592 * Automatic parse of I/O pins from the BIOS configuration
593 */
594
595static int ad198x_auto_build_controls(struct hda_codec *codec)
596{
597 int err;
598
599 err = snd_hda_gen_build_controls(codec);
600 if (err < 0)
601 return err;
602 err = create_beep_ctls(codec);
603 if (err < 0)
604 return err;
605 return 0;
606}
607
608static const struct hda_codec_ops ad198x_auto_patch_ops = {
609 .build_controls = ad198x_auto_build_controls,
610 .build_pcms = snd_hda_gen_build_pcms,
611 .init = snd_hda_gen_init,
612 .free = ad198x_free,
Takashi Iwai8a6c21a2013-01-18 07:51:17 +0100613 .unsol_event = snd_hda_jack_unsol_event,
Takashi Iwai78bb3cb2012-12-21 15:17:06 +0100614#ifdef CONFIG_PM
615 .check_power_status = snd_hda_gen_check_power_status,
616 .suspend = ad198x_suspend,
617#endif
618 .reboot_notify = ad198x_shutup,
619};
620
621
622static int ad198x_parse_auto_config(struct hda_codec *codec)
623{
624 struct ad198x_spec *spec = codec->spec;
625 struct auto_pin_cfg *cfg = &spec->gen.autocfg;
626 int err;
627
628 codec->spdif_status_reset = 1;
629 codec->no_trigger_sense = 1;
630 codec->no_sticky_stream = 1;
631
632 spec->gen.indep_hp = 1;
633
634 err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
635 if (err < 0)
636 return err;
637 err = snd_hda_gen_parse_auto_config(codec, cfg);
638 if (err < 0)
639 return err;
640
641 if (spec->beep_dev_nid) {
642 err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid);
643 if (err < 0)
644 return err;
645 }
646
647 codec->patch_ops = ad198x_auto_patch_ops;
648
649 return 0;
650}
651
652/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200653 * AD1986A specific
654 */
655
Takashi Iwai9ff4bc82013-01-22 16:45:58 +0100656#ifdef ENABLE_AD_STATIC_QUIRKS
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657#define AD1986A_SPDIF_OUT 0x02
658#define AD1986A_FRONT_DAC 0x03
659#define AD1986A_SURR_DAC 0x04
660#define AD1986A_CLFE_DAC 0x05
661#define AD1986A_ADC 0x06
662
Takashi Iwai498f5b12011-05-02 11:33:15 +0200663static const hda_nid_t ad1986a_dac_nids[3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
665};
Takashi Iwai498f5b12011-05-02 11:33:15 +0200666static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
667static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Takashi Iwai498f5b12011-05-02 11:33:15 +0200669static const struct hda_input_mux ad1986a_capture_source = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 .num_items = 7,
671 .items = {
672 { "Mic", 0x0 },
673 { "CD", 0x1 },
674 { "Aux", 0x3 },
675 { "Line", 0x4 },
676 { "Mix", 0x5 },
677 { "Mono", 0x6 },
678 { "Phone", 0x7 },
679 },
680};
681
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Takashi Iwai498f5b12011-05-02 11:33:15 +0200683static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200684 .ops = &snd_hda_bind_vol,
685 .values = {
686 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
687 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
688 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
689 0
690 },
691};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Takashi Iwai498f5b12011-05-02 11:33:15 +0200693static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200694 .ops = &snd_hda_bind_sw,
695 .values = {
696 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
697 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
698 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
699 0
700 },
701};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
703/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 * mixers
705 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200706static const struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200707 /*
708 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
709 */
710 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
711 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
713 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
714 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
715 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
716 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
717 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
718 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
719 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
720 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
721 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
722 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
723 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
724 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
725 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
726 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
727 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
728 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
729 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100730 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
732 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
733 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
734 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
735 {
736 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
737 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200738 .info = ad198x_mux_enum_info,
739 .get = ad198x_mux_enum_get,
740 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 },
742 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
743 { } /* end */
744};
745
Takashi Iwai9230d212006-03-13 13:49:49 +0100746/* additional mixers for 3stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200747static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100748 {
749 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
750 .name = "Channel Mode",
751 .info = ad198x_ch_mode_info,
752 .get = ad198x_ch_mode_get,
753 .put = ad198x_ch_mode_put,
754 },
755 { } /* end */
756};
757
758/* laptop model - 2ch only */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200759static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
Takashi Iwai9230d212006-03-13 13:49:49 +0100760
Takashi Iwai20a45e82007-08-15 22:20:45 +0200761/* master controls both pins 0x1a and 0x1b */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200762static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200763 .ops = &snd_hda_bind_vol,
764 .values = {
765 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
766 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
767 0,
768 },
769};
770
Takashi Iwai498f5b12011-05-02 11:33:15 +0200771static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200772 .ops = &snd_hda_bind_sw,
773 .values = {
774 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
775 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
776 0,
777 },
778};
779
Takashi Iwai498f5b12011-05-02 11:33:15 +0200780static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100781 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
782 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200783 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
784 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100785 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
786 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
787 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
788 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
789 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
790 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
791 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
792 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100793 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100794 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100795 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
796 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
797 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
798 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
799 {
800 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
801 .name = "Capture Source",
802 .info = ad198x_mux_enum_info,
803 .get = ad198x_mux_enum_get,
804 .put = ad198x_mux_enum_put,
805 },
806 { } /* end */
807};
808
Takashi Iwai825aa9722006-03-17 10:50:49 +0100809/* laptop-eapd model - 2ch only */
810
Takashi Iwai498f5b12011-05-02 11:33:15 +0200811static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100812 .num_items = 3,
813 .items = {
814 { "Mic", 0x0 },
815 { "Internal Mic", 0x4 },
816 { "Mix", 0x5 },
817 },
818};
819
Takashi Iwai498f5b12011-05-02 11:33:15 +0200820static const struct hda_input_mux ad1986a_automic_capture_source = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100821 .num_items = 2,
822 .items = {
823 { "Mic", 0x0 },
824 { "Mix", 0x5 },
825 },
826};
827
Takashi Iwai498f5b12011-05-02 11:33:15 +0200828static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200829 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
830 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200831 { } /* end */
832};
833
Takashi Iwai498f5b12011-05-02 11:33:15 +0200834static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100835 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
836 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100837 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
838 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100839 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100840 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
841 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
842 {
843 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
844 .name = "Capture Source",
845 .info = ad198x_mux_enum_info,
846 .get = ad198x_mux_enum_get,
847 .put = ad198x_mux_enum_put,
848 },
849 {
850 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
851 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100852 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100853 .info = ad198x_eapd_info,
854 .get = ad198x_eapd_get,
855 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100856 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100857 },
858 { } /* end */
859};
860
Takashi Iwai498f5b12011-05-02 11:33:15 +0200861static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
Takashi Iwai16d11a82009-06-24 14:07:53 +0200862 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
863 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100864 { } /* end */
865};
866
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100867/* re-connect the mic boost input according to the jack sensing */
868static void ad1986a_automic(struct hda_codec *codec)
869{
870 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100871 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100872 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
873 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100874 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100875}
876
877#define AD1986A_MIC_EVENT 0x36
878
879static void ad1986a_automic_unsol_event(struct hda_codec *codec,
880 unsigned int res)
881{
882 if ((res >> 26) != AD1986A_MIC_EVENT)
883 return;
884 ad1986a_automic(codec);
885}
886
887static int ad1986a_automic_init(struct hda_codec *codec)
888{
889 ad198x_init(codec);
890 ad1986a_automic(codec);
891 return 0;
892}
893
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200894/* laptop-automute - 2ch only */
895
896static void ad1986a_update_hp(struct hda_codec *codec)
897{
898 struct ad198x_spec *spec = codec->spec;
899 unsigned int mute;
900
901 if (spec->jack_present)
902 mute = HDA_AMP_MUTE; /* mute internal speaker */
903 else
904 /* unmute internal speaker if necessary */
905 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
906 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
907 HDA_AMP_MUTE, mute);
908}
909
910static void ad1986a_hp_automute(struct hda_codec *codec)
911{
912 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200913
Takashi Iwaid56757a2009-11-18 08:00:14 +0100914 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200915 if (spec->inv_jack_detect)
916 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200917 ad1986a_update_hp(codec);
918}
919
920#define AD1986A_HP_EVENT 0x37
921
922static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
923{
924 if ((res >> 26) != AD1986A_HP_EVENT)
925 return;
926 ad1986a_hp_automute(codec);
927}
928
929static int ad1986a_hp_init(struct hda_codec *codec)
930{
931 ad198x_init(codec);
932 ad1986a_hp_automute(codec);
933 return 0;
934}
935
936/* bind hp and internal speaker mute (with plug check) */
937static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
938 struct snd_ctl_elem_value *ucontrol)
939{
940 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai8092e602012-12-13 17:03:30 +0100941 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200942 if (change)
943 ad1986a_update_hp(codec);
944 return change;
945}
946
Takashi Iwai498f5b12011-05-02 11:33:15 +0200947static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200948 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
949 {
950 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
951 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100952 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200953 .info = snd_hda_mixer_amp_switch_info,
954 .get = snd_hda_mixer_amp_switch_get,
955 .put = ad1986a_hp_master_sw_put,
956 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
957 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200958 { } /* end */
959};
960
Takashi Iwai16d11a82009-06-24 14:07:53 +0200961
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962/*
963 * initialization verbs
964 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200965static const struct hda_verb ad1986a_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 /* Front, Surround, CLFE DAC; mute as default */
967 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
968 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
969 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
970 /* Downmix - off */
971 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
972 /* HP, Line-Out, Surround, CLFE selectors */
973 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
974 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
975 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
976 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
977 /* Mono selector */
978 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
979 /* Mic selector: Mic 1/2 pin */
980 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
981 /* Line-in selector: Line-in */
982 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
983 /* Mic 1/2 swap */
984 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
985 /* Record selector: mic */
986 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
987 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
988 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
989 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
990 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
991 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
992 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
993 /* PC beep */
994 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
995 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
996 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
997 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
998 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
999 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1000 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001001 /* HP Pin */
1002 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1003 /* Front, Surround, CLFE Pins */
1004 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1005 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1006 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1007 /* Mono Pin */
1008 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1009 /* Mic Pin */
1010 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1011 /* Line, Aux, CD, Beep-In Pin */
1012 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1013 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1014 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1015 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1016 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 { } /* end */
1018};
1019
Takashi Iwai498f5b12011-05-02 11:33:15 +02001020static const struct hda_verb ad1986a_ch2_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001021 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001022 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1023 /* Line-in selectors */
1024 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001025 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001026 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1027 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
1028 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001029 { } /* end */
1030};
1031
Takashi Iwai498f5b12011-05-02 11:33:15 +02001032static const struct hda_verb ad1986a_ch4_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001033 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001034 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1035 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001036 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001037 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1038 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001039 { } /* end */
1040};
1041
Takashi Iwai498f5b12011-05-02 11:33:15 +02001042static const struct hda_verb ad1986a_ch6_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001043 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001044 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1045 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001046 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001047 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1048 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001049 { } /* end */
1050};
1051
Takashi Iwai498f5b12011-05-02 11:33:15 +02001052static const struct hda_channel_mode ad1986a_modes[3] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001053 { 2, ad1986a_ch2_init },
1054 { 4, ad1986a_ch4_init },
1055 { 6, ad1986a_ch6_init },
1056};
1057
Takashi Iwai825aa9722006-03-17 10:50:49 +01001058/* eapd initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001059static const struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001060 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +01001061 {}
1062};
1063
Takashi Iwai498f5b12011-05-02 11:33:15 +02001064static const struct hda_verb ad1986a_automic_verbs[] = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001065 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1066 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1067 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1068 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1069 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1070 {}
1071};
1072
Tobin Davisf36090f2007-01-08 11:07:12 +01001073/* Ultra initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001074static const struct hda_verb ad1986a_ultra_init[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001075 /* eapd initialization */
1076 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1077 /* CLFE -> Mic in */
1078 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1079 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1080 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1081 { } /* end */
1082};
1083
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001084/* pin sensing on HP jack */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001085static const struct hda_verb ad1986a_hp_init_verbs[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001086 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1087 {}
1088};
1089
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001090static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1091 unsigned int res)
1092{
1093 switch (res >> 26) {
1094 case AD1986A_HP_EVENT:
1095 ad1986a_hp_automute(codec);
1096 break;
1097 case AD1986A_MIC_EVENT:
1098 ad1986a_automic(codec);
1099 break;
1100 }
1101}
1102
1103static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1104{
1105 ad198x_init(codec);
1106 ad1986a_hp_automute(codec);
1107 ad1986a_automic(codec);
1108 return 0;
1109}
1110
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001111
Takashi Iwai9230d212006-03-13 13:49:49 +01001112/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001113enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001114 AD1986A_AUTO,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001115 AD1986A_6STACK,
1116 AD1986A_3STACK,
1117 AD1986A_LAPTOP,
1118 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001119 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001120 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001121 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001122 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001123 AD1986A_MODELS
1124};
Takashi Iwai9230d212006-03-13 13:49:49 +01001125
Takashi Iwaiea734962011-01-17 11:29:34 +01001126static const char * const ad1986a_models[AD1986A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001127 [AD1986A_AUTO] = "auto",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001128 [AD1986A_6STACK] = "6stack",
1129 [AD1986A_3STACK] = "3stack",
1130 [AD1986A_LAPTOP] = "laptop",
1131 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001132 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001133 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001134 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001135 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001136};
1137
Takashi Iwai498f5b12011-05-02 11:33:15 +02001138static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001139 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001140 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001141 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001142 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001143 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1144 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1145 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1146 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001147 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001148 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001149 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1150 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1151 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1152 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1153 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001154 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001155 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001156 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001157 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001158 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001159 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001160 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001161 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001162 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001163 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001164 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001165 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001166 {}
1167};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
Takashi Iwai83012a72012-08-24 18:38:08 +02001169#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001170static const struct hda_amp_list ad1986a_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001171 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1172 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1173 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1174 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1175 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1176 { } /* end */
1177};
1178#endif
1179
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001180static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1181{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001182 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001183 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1184}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001185#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001186
Takashi Iwai361dab32012-05-09 14:35:27 +02001187static int alloc_ad_spec(struct hda_codec *codec)
1188{
1189 struct ad198x_spec *spec;
1190
1191 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1192 if (!spec)
1193 return -ENOMEM;
1194 codec->spec = spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001195 snd_hda_gen_spec_init(&spec->gen);
Takashi Iwai361dab32012-05-09 14:35:27 +02001196 return 0;
1197}
1198
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001199/*
Takashi Iwaia928bd22013-01-22 18:18:42 +01001200 * AD1986A fixup codes
1201 */
1202
1203/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
1204static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
1205 const struct hda_fixup *fix, int action)
1206{
1207 if (action == HDA_FIXUP_ACT_PRE_PROBE)
1208 codec->inv_jack_detect = 1;
1209}
1210
1211enum {
1212 AD1986A_FIXUP_INV_JACK_DETECT,
1213};
1214
1215static const struct hda_fixup ad1986a_fixups[] = {
1216 [AD1986A_FIXUP_INV_JACK_DETECT] = {
1217 .type = HDA_FIXUP_FUNC,
1218 .v.func = ad_fixup_inv_jack_detect,
1219 },
1220};
1221
1222static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
1223 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
1224 {}
1225};
1226
1227/*
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001228 */
1229static int ad1986a_parse_auto_config(struct hda_codec *codec)
1230{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001231 int err;
1232 struct ad198x_spec *spec;
1233
1234 err = alloc_ad_spec(codec);
1235 if (err < 0)
1236 return err;
1237 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001238
1239 /* AD1986A has the inverted EAPD implementation */
1240 codec->inv_eapd = 1;
1241
Takashi Iwaif2f8be42013-01-21 16:40:16 +01001242 spec->gen.mixer_nid = 0x07;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001243 spec->beep_dev_nid = 0x19;
1244 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1245
1246 /* AD1986A has a hardware problem that it can't share a stream
1247 * with multiple output pins. The copy of front to surrounds
1248 * causes noisy or silent outputs at a certain timing, e.g.
1249 * changing the volume.
1250 * So, let's disable the shared stream.
1251 */
1252 spec->gen.multiout.no_share_stream = 1;
1253
Takashi Iwaia928bd22013-01-22 18:18:42 +01001254 snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups);
1255 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
1256
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001257 err = ad198x_parse_auto_config(codec);
1258 if (err < 0) {
1259 ad198x_free(codec);
1260 return err;
1261 }
1262
Takashi Iwaia928bd22013-01-22 18:18:42 +01001263 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
1264
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001265 return 0;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001266}
1267
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001268#ifdef ENABLE_AD_STATIC_QUIRKS
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269static int patch_ad1986a(struct hda_codec *codec)
1270{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001271 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001272 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001274 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1275 ad1986a_models,
1276 ad1986a_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01001277 if (board_config < 0) {
1278 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
1279 codec->chip_name);
1280 board_config = AD1986A_AUTO;
1281 }
1282
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001283 if (board_config == AD1986A_AUTO)
1284 return ad1986a_parse_auto_config(codec);
1285
Takashi Iwai361dab32012-05-09 14:35:27 +02001286 err = alloc_ad_spec(codec);
1287 if (err < 0)
1288 return err;
1289 spec = codec->spec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001291 err = snd_hda_attach_beep_device(codec, 0x19);
1292 if (err < 0) {
1293 ad198x_free(codec);
1294 return err;
1295 }
1296 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1297
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 spec->multiout.max_channels = 6;
1299 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1300 spec->multiout.dac_nids = ad1986a_dac_nids;
1301 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001302 spec->num_adc_nids = 1;
1303 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001304 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001305 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001306 spec->num_mixers = 1;
1307 spec->mixers[0] = ad1986a_mixers;
1308 spec->num_init_verbs = 1;
1309 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwai83012a72012-08-24 18:38:08 +02001310#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02001311 spec->loopback.amplist = ad1986a_loopbacks;
1312#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001313 spec->vmaster_nid = 0x1b;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001314 codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001316 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317
Takashi Iwai9230d212006-03-13 13:49:49 +01001318 /* override some parameters */
Takashi Iwai9230d212006-03-13 13:49:49 +01001319 switch (board_config) {
1320 case AD1986A_3STACK:
1321 spec->num_mixers = 2;
1322 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001323 spec->num_init_verbs = 2;
1324 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001325 spec->channel_mode = ad1986a_modes;
1326 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001327 spec->need_dac_fix = 1;
1328 spec->multiout.max_channels = 2;
1329 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001330 break;
1331 case AD1986A_LAPTOP:
1332 spec->mixers[0] = ad1986a_laptop_mixers;
1333 spec->multiout.max_channels = 2;
1334 spec->multiout.num_dacs = 1;
1335 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1336 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001337 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001338 spec->num_mixers = 3;
1339 spec->mixers[0] = ad1986a_laptop_master_mixers;
1340 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1341 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001342 spec->num_init_verbs = 2;
1343 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1344 spec->multiout.max_channels = 2;
1345 spec->multiout.num_dacs = 1;
1346 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1347 if (!is_jack_available(codec, 0x25))
1348 spec->multiout.dig_out_nid = 0;
1349 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1350 break;
1351 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001352 spec->num_mixers = 2;
1353 spec->mixers[0] = ad1986a_laptop_master_mixers;
1354 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001355 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001356 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001357 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001358 spec->multiout.max_channels = 2;
1359 spec->multiout.num_dacs = 1;
1360 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001361 if (!is_jack_available(codec, 0x25))
1362 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001363 spec->input_mux = &ad1986a_automic_capture_source;
1364 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1365 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001366 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001367 case AD1986A_SAMSUNG_P50:
1368 spec->num_mixers = 2;
1369 spec->mixers[0] = ad1986a_automute_master_mixers;
1370 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1371 spec->num_init_verbs = 4;
1372 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1373 spec->init_verbs[2] = ad1986a_automic_verbs;
1374 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1375 spec->multiout.max_channels = 2;
1376 spec->multiout.num_dacs = 1;
1377 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1378 if (!is_jack_available(codec, 0x25))
1379 spec->multiout.dig_out_nid = 0;
1380 spec->input_mux = &ad1986a_automic_capture_source;
1381 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1382 codec->patch_ops.init = ad1986a_samsung_p50_init;
1383 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001384 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001385 spec->num_mixers = 3;
1386 spec->mixers[0] = ad1986a_automute_master_mixers;
1387 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1388 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001389 spec->num_init_verbs = 3;
1390 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1391 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1392 spec->multiout.max_channels = 2;
1393 spec->multiout.num_dacs = 1;
1394 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001395 if (!is_jack_available(codec, 0x25))
1396 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001397 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1398 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1399 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001400 /* Lenovo N100 seems to report the reversed bit
1401 * for HP jack-sensing
1402 */
1403 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001404 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001405 case AD1986A_ULTRA:
1406 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1407 spec->num_init_verbs = 2;
1408 spec->init_verbs[1] = ad1986a_ultra_init;
1409 spec->multiout.max_channels = 2;
1410 spec->multiout.num_dacs = 1;
1411 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1412 spec->multiout.dig_out_nid = 0;
1413 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001414 }
1415
Takashi Iwaid29240c2007-10-26 12:35:56 +02001416 /* AD1986A has a hardware problem that it can't share a stream
1417 * with multiple output pins. The copy of front to surrounds
1418 * causes noisy or silent outputs at a certain timing, e.g.
1419 * changing the volume.
1420 * So, let's disable the shared stream.
1421 */
1422 spec->multiout.no_share_stream = 1;
1423
Takashi Iwai729d55b2009-12-25 22:49:01 +01001424 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001425 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001426
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 return 0;
1428}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001429#else /* ENABLE_AD_STATIC_QUIRKS */
1430#define patch_ad1986a ad1986a_parse_auto_config
1431#endif /* ENABLE_AD_STATIC_QUIRKS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
1433/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001434 * AD1983 specific
1435 */
1436
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001437#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001438#define AD1983_SPDIF_OUT 0x02
1439#define AD1983_DAC 0x03
1440#define AD1983_ADC 0x04
1441
Takashi Iwai498f5b12011-05-02 11:33:15 +02001442static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
1443static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
1444static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001445
Takashi Iwai498f5b12011-05-02 11:33:15 +02001446static const struct hda_input_mux ad1983_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001447 .num_items = 4,
1448 .items = {
1449 { "Mic", 0x0 },
1450 { "Line", 0x1 },
1451 { "Mix", 0x2 },
1452 { "Mix Mono", 0x3 },
1453 },
1454};
1455
1456/*
1457 * SPDIF playback route
1458 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001459static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001460{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001461 static const char * const texts[] = { "PCM", "ADC" };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001462
1463 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1464 uinfo->count = 1;
1465 uinfo->value.enumerated.items = 2;
1466 if (uinfo->value.enumerated.item > 1)
1467 uinfo->value.enumerated.item = 1;
1468 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1469 return 0;
1470}
1471
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001472static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001473{
1474 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1475 struct ad198x_spec *spec = codec->spec;
1476
1477 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1478 return 0;
1479}
1480
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001481static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001482{
1483 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1484 struct ad198x_spec *spec = codec->spec;
1485
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001486 if (ucontrol->value.enumerated.item[0] > 1)
1487 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001488 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1489 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001490 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1491 AC_VERB_SET_CONNECT_SEL,
1492 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001493 return 1;
1494 }
1495 return 0;
1496}
1497
Takashi Iwai498f5b12011-05-02 11:33:15 +02001498static const struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001499 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1508 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1509 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1510 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001511 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001512 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1513 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1514 {
1515 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1516 .name = "Capture Source",
1517 .info = ad198x_mux_enum_info,
1518 .get = ad198x_mux_enum_get,
1519 .put = ad198x_mux_enum_put,
1520 },
1521 {
1522 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001523 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001524 .info = ad1983_spdif_route_info,
1525 .get = ad1983_spdif_route_get,
1526 .put = ad1983_spdif_route_put,
1527 },
1528 { } /* end */
1529};
1530
Takashi Iwai498f5b12011-05-02 11:33:15 +02001531static const struct hda_verb ad1983_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001532 /* Front, HP, Mono; mute as default */
1533 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1534 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1535 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1536 /* Beep, PCM, Mic, Line-In: mute */
1537 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1538 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1539 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1540 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1541 /* Front, HP selectors; from Mix */
1542 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1543 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1544 /* Mono selector; from Mix */
1545 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1546 /* Mic selector; Mic */
1547 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1548 /* Line-in selector: Line-in */
1549 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1550 /* Mic boost: 0dB */
1551 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1552 /* Record selector: mic */
1553 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1554 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1555 /* SPDIF route: PCM */
1556 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1557 /* Front Pin */
1558 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1559 /* HP Pin */
1560 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1561 /* Mono Pin */
1562 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1563 /* Mic Pin */
1564 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1565 /* Line Pin */
1566 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1567 { } /* end */
1568};
1569
Takashi Iwai83012a72012-08-24 18:38:08 +02001570#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001571static const struct hda_amp_list ad1983_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001572 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1573 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1574 { } /* end */
1575};
1576#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001577
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001578/* models */
1579enum {
1580 AD1983_AUTO,
1581 AD1983_BASIC,
1582 AD1983_MODELS
1583};
1584
1585static const char * const ad1983_models[AD1983_MODELS] = {
1586 [AD1983_AUTO] = "auto",
1587 [AD1983_BASIC] = "basic",
1588};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001589#endif /* ENABLE_AD_STATIC_QUIRKS */
1590
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001591
Takashi Iwai272f3ea2013-01-22 15:31:33 +01001592/*
1593 * SPDIF mux control for AD1983 auto-parser
1594 */
1595static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
1596 struct snd_ctl_elem_info *uinfo)
1597{
1598 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1599 struct ad198x_spec *spec = codec->spec;
1600 static const char * const texts2[] = { "PCM", "ADC" };
1601 static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
1602 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1603 int num_conns = snd_hda_get_num_conns(codec, dig_out);
1604
1605 if (num_conns == 2)
1606 return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
1607 else if (num_conns == 3)
1608 return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
1609 else
1610 return -EINVAL;
1611}
1612
1613static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
1614 struct snd_ctl_elem_value *ucontrol)
1615{
1616 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1617 struct ad198x_spec *spec = codec->spec;
1618
1619 ucontrol->value.enumerated.item[0] = spec->cur_smux;
1620 return 0;
1621}
1622
1623static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
1624 struct snd_ctl_elem_value *ucontrol)
1625{
1626 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1627 struct ad198x_spec *spec = codec->spec;
1628 unsigned int val = ucontrol->value.enumerated.item[0];
1629 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1630 int num_conns = snd_hda_get_num_conns(codec, dig_out);
1631
1632 if (val >= num_conns)
1633 return -EINVAL;
1634 if (spec->cur_smux == val)
1635 return 0;
1636 spec->cur_smux = val;
1637 snd_hda_codec_write_cache(codec, dig_out, 0,
1638 AC_VERB_SET_CONNECT_SEL, val);
1639 return 1;
1640}
1641
1642static struct snd_kcontrol_new ad1983_auto_smux_mixer = {
1643 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1644 .name = "IEC958 Playback Source",
1645 .info = ad1983_auto_smux_enum_info,
1646 .get = ad1983_auto_smux_enum_get,
1647 .put = ad1983_auto_smux_enum_put,
1648};
1649
1650static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
1651{
1652 struct ad198x_spec *spec = codec->spec;
1653 hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
1654 int num_conns;
1655
1656 if (!dig_out)
1657 return 0;
1658 num_conns = snd_hda_get_num_conns(codec, dig_out);
1659 if (num_conns != 2 && num_conns != 3)
1660 return 0;
1661 if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
1662 return -ENOMEM;
1663 return 0;
1664}
1665
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001666static int ad1983_parse_auto_config(struct hda_codec *codec)
1667{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001668 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001669 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001670
Takashi Iwai361dab32012-05-09 14:35:27 +02001671 err = alloc_ad_spec(codec);
1672 if (err < 0)
1673 return err;
1674 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001675
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001676 spec->beep_dev_nid = 0x10;
1677 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1678 err = ad198x_parse_auto_config(codec);
1679 if (err < 0)
1680 goto error;
1681 err = ad1983_add_spdif_mux_ctl(codec);
1682 if (err < 0)
1683 goto error;
1684 return 0;
1685
1686 error:
1687 ad198x_free(codec);
1688 return err;
1689}
1690
1691#ifdef ENABLE_AD_STATIC_QUIRKS
1692static int patch_ad1983(struct hda_codec *codec)
1693{
1694 struct ad198x_spec *spec;
1695 int board_config;
1696 int err;
1697
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001698 board_config = snd_hda_check_board_config(codec, AD1983_MODELS,
1699 ad1983_models, NULL);
Takashi Iwai657e1b92013-01-22 18:42:39 +01001700 if (board_config < 0) {
1701 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
1702 codec->chip_name);
1703 board_config = AD1983_AUTO;
1704 }
1705
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001706 if (board_config == AD1983_AUTO)
1707 return ad1983_parse_auto_config(codec);
1708
1709 err = alloc_ad_spec(codec);
1710 if (err < 0)
1711 return err;
1712 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01001713
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001714 err = snd_hda_attach_beep_device(codec, 0x10);
1715 if (err < 0) {
1716 ad198x_free(codec);
1717 return err;
1718 }
1719 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1720
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001721 spec->multiout.max_channels = 2;
1722 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1723 spec->multiout.dac_nids = ad1983_dac_nids;
1724 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001725 spec->num_adc_nids = 1;
1726 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001727 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001728 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001729 spec->num_mixers = 1;
1730 spec->mixers[0] = ad1983_mixers;
1731 spec->num_init_verbs = 1;
1732 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001733 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02001734#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02001735 spec->loopback.amplist = ad1983_loopbacks;
1736#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001737 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001738
1739 codec->patch_ops = ad198x_patch_ops;
1740
Takashi Iwai729d55b2009-12-25 22:49:01 +01001741 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001742 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001743
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001744 return 0;
1745}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001746#else /* ENABLE_AD_STATIC_QUIRKS */
1747#define patch_ad1983 ad1983_parse_auto_config
1748#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001749
1750
1751/*
1752 * AD1981 HD specific
1753 */
1754
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01001755#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001756#define AD1981_SPDIF_OUT 0x02
1757#define AD1981_DAC 0x03
1758#define AD1981_ADC 0x04
1759
Takashi Iwai498f5b12011-05-02 11:33:15 +02001760static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
1761static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
1762static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001763
1764/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001765static const struct hda_input_mux ad1981_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001766 .num_items = 7,
1767 .items = {
1768 { "Front Mic", 0x0 },
1769 { "Line", 0x1 },
1770 { "Mix", 0x2 },
1771 { "Mix Mono", 0x3 },
1772 { "CD", 0x4 },
1773 { "Mic", 0x6 },
1774 { "Aux", 0x7 },
1775 },
1776};
1777
Takashi Iwai498f5b12011-05-02 11:33:15 +02001778static const struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001779 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1780 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1781 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1782 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1783 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1784 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1785 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1786 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1787 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1788 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1789 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1790 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1791 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1792 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1793 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1794 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1795 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1796 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001797 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1798 HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001799 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1800 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1801 {
1802 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1803 .name = "Capture Source",
1804 .info = ad198x_mux_enum_info,
1805 .get = ad198x_mux_enum_get,
1806 .put = ad198x_mux_enum_put,
1807 },
1808 /* identical with AD1983 */
1809 {
1810 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001811 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001812 .info = ad1983_spdif_route_info,
1813 .get = ad1983_spdif_route_get,
1814 .put = ad1983_spdif_route_put,
1815 },
1816 { } /* end */
1817};
1818
Takashi Iwai498f5b12011-05-02 11:33:15 +02001819static const struct hda_verb ad1981_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001820 /* Front, HP, Mono; mute as default */
1821 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1822 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1823 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1824 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1825 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1826 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1827 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1828 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1829 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1830 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1831 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1832 /* Front, HP selectors; from Mix */
1833 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1834 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1835 /* Mono selector; from Mix */
1836 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1837 /* Mic Mixer; select Front Mic */
1838 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1839 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1840 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001841 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1842 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001843 /* Record selector: Front mic */
1844 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1845 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1846 /* SPDIF route: PCM */
1847 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1848 /* Front Pin */
1849 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1850 /* HP Pin */
1851 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1852 /* Mono Pin */
1853 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1854 /* Front & Rear Mic Pins */
1855 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1856 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1857 /* Line Pin */
1858 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1859 /* Digital Beep */
1860 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1861 /* Line-Out as Input: disabled */
1862 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1863 { } /* end */
1864};
1865
Takashi Iwai83012a72012-08-24 18:38:08 +02001866#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02001867static const struct hda_amp_list ad1981_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001868 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1869 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1870 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1871 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1872 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1873 { } /* end */
1874};
1875#endif
1876
Takashi Iwai18a815d2006-03-01 19:54:39 +01001877/*
1878 * Patch for HP nx6320
1879 *
Tobin Davis18768992007-03-12 22:20:51 +01001880 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001881 * speaker output enabled _and_ mute-LED off.
1882 */
1883
1884#define AD1981_HP_EVENT 0x37
1885#define AD1981_MIC_EVENT 0x38
1886
Takashi Iwai498f5b12011-05-02 11:33:15 +02001887static const struct hda_verb ad1981_hp_init_verbs[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001888 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1889 /* pin sensing on HP and Mic jacks */
1890 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1891 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1892 {}
1893};
1894
1895/* turn on/off EAPD (+ mute HP) as a master switch */
1896static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1897 struct snd_ctl_elem_value *ucontrol)
1898{
1899 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1900 struct ad198x_spec *spec = codec->spec;
1901
1902 if (! ad198x_eapd_put(kcontrol, ucontrol))
1903 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001904 /* change speaker pin appropriately */
Takashi Iwaicdd03ce2012-04-20 12:34:50 +02001905 snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001906 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001907 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1908 HDA_AMP_MUTE,
1909 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001910 return 1;
1911}
1912
1913/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001914static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001915 .ops = &snd_hda_bind_vol,
1916 .values = {
1917 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1918 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1919 0
1920 },
1921};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001922
1923/* mute internal speaker if HP is plugged */
1924static void ad1981_hp_automute(struct hda_codec *codec)
1925{
1926 unsigned int present;
1927
Takashi Iwaid56757a2009-11-18 08:00:14 +01001928 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001929 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1930 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001931}
1932
1933/* toggle input of built-in and mic jack appropriately */
1934static void ad1981_hp_automic(struct hda_codec *codec)
1935{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001936 static const struct hda_verb mic_jack_on[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001937 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1938 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1939 {}
1940 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02001941 static const struct hda_verb mic_jack_off[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001942 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1943 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1944 {}
1945 };
1946 unsigned int present;
1947
Takashi Iwaid56757a2009-11-18 08:00:14 +01001948 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001949 if (present)
1950 snd_hda_sequence_write(codec, mic_jack_on);
1951 else
1952 snd_hda_sequence_write(codec, mic_jack_off);
1953}
1954
1955/* unsolicited event for HP jack sensing */
1956static void ad1981_hp_unsol_event(struct hda_codec *codec,
1957 unsigned int res)
1958{
1959 res >>= 26;
1960 switch (res) {
1961 case AD1981_HP_EVENT:
1962 ad1981_hp_automute(codec);
1963 break;
1964 case AD1981_MIC_EVENT:
1965 ad1981_hp_automic(codec);
1966 break;
1967 }
1968}
1969
Takashi Iwai498f5b12011-05-02 11:33:15 +02001970static const struct hda_input_mux ad1981_hp_capture_source = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001971 .num_items = 3,
1972 .items = {
1973 { "Mic", 0x0 },
David Henningssonc40bd912012-09-19 12:19:47 +02001974 { "Dock Mic", 0x1 },
Takashi Iwai18a815d2006-03-01 19:54:39 +01001975 { "Mix", 0x2 },
1976 },
1977};
1978
Takashi Iwai498f5b12011-05-02 11:33:15 +02001979static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001980 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001981 {
1982 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001983 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001984 .name = "Master Playback Switch",
1985 .info = ad198x_eapd_info,
1986 .get = ad198x_eapd_get,
1987 .put = ad1981_hp_master_sw_put,
1988 .private_value = 0x05,
1989 },
1990 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1991 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1992#if 0
1993 /* FIXME: analog mic/line loopback doesn't work with my tests...
1994 * (although recording is OK)
1995 */
1996 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1997 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
David Henningssonc40bd912012-09-19 12:19:47 +02001998 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1999 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01002000 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
2001 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
2002 /* FIXME: does this laptop have analog CD connection? */
2003 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
2004 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
2005#endif
David Henningsson5f99f862011-01-04 15:24:24 +01002006 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
2007 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01002008 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
2009 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
2010 {
2011 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2012 .name = "Capture Source",
2013 .info = ad198x_mux_enum_info,
2014 .get = ad198x_mux_enum_get,
2015 .put = ad198x_mux_enum_put,
2016 },
2017 { } /* end */
2018};
2019
2020/* initialize jack-sensing, too */
2021static int ad1981_hp_init(struct hda_codec *codec)
2022{
2023 ad198x_init(codec);
2024 ad1981_hp_automute(codec);
2025 ad1981_hp_automic(codec);
2026 return 0;
2027}
2028
Tobin Davis18768992007-03-12 22:20:51 +01002029/* configuration for Toshiba Laptops */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002030static const struct hda_verb ad1981_toshiba_init_verbs[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002031 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
2032 /* pin sensing on HP and Mic jacks */
2033 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
2034 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
2035 {}
2036};
2037
Takashi Iwai498f5b12011-05-02 11:33:15 +02002038static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
Tobin Davis18768992007-03-12 22:20:51 +01002039 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
2040 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
2041 { }
2042};
2043
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002044/* configuration for Lenovo Thinkpad T60 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002045static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002046 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2047 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
2048 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
2049 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
2050 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
2051 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
2052 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
2053 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01002054 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002055 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
2056 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
2057 {
2058 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2059 .name = "Capture Source",
2060 .info = ad198x_mux_enum_info,
2061 .get = ad198x_mux_enum_get,
2062 .put = ad198x_mux_enum_put,
2063 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02002064 /* identical with AD1983 */
2065 {
2066 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2067 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
2068 .info = ad1983_spdif_route_info,
2069 .get = ad1983_spdif_route_get,
2070 .put = ad1983_spdif_route_put,
2071 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002072 { } /* end */
2073};
2074
Takashi Iwai498f5b12011-05-02 11:33:15 +02002075static const struct hda_input_mux ad1981_thinkpad_capture_source = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002076 .num_items = 3,
2077 .items = {
2078 { "Mic", 0x0 },
2079 { "Mix", 0x2 },
2080 { "CD", 0x4 },
2081 },
2082};
2083
Takashi Iwai18a815d2006-03-01 19:54:39 +01002084/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002085enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002086 AD1981_AUTO,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002087 AD1981_BASIC,
2088 AD1981_HP,
2089 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01002090 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002091 AD1981_MODELS
2092};
Takashi Iwai18a815d2006-03-01 19:54:39 +01002093
Takashi Iwaiea734962011-01-17 11:29:34 +01002094static const char * const ad1981_models[AD1981_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002095 [AD1981_AUTO] = "auto",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002096 [AD1981_HP] = "hp",
2097 [AD1981_THINKPAD] = "thinkpad",
2098 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01002099 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002100};
2101
Takashi Iwai498f5b12011-05-02 11:33:15 +02002102static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002103 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02002104 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02002105 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01002106 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002107 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002108 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01002109 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01002110 /* HP nx6320 (reversed SSID, H/W bug) */
2111 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01002112 {}
2113};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002114#endif /* ENABLE_AD_STATIC_QUIRKS */
2115
Takashi Iwai18a815d2006-03-01 19:54:39 +01002116
Takashi Iwaia928bd22013-01-22 18:18:42 +01002117/* follow EAPD via vmaster hook */
2118static void ad_vmaster_eapd_hook(void *private_data, int enabled)
2119{
2120 struct hda_codec *codec = private_data;
2121 struct ad198x_spec *spec = codec->spec;
2122 snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
2123 AC_VERB_SET_EAPD_BTLENABLE,
2124 enabled ? 0x02 : 0x00);
2125}
2126
2127static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
2128 const struct hda_fixup *fix, int action)
2129{
2130 struct ad198x_spec *spec = codec->spec;
2131
2132 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
2133 spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
2134 spec->eapd_nid = 0x05;
2135 }
2136}
2137
2138/* set the upper-limit for mixer amp to 0dB for avoiding the possible
2139 * damage by overloading
2140 */
2141static void ad1981_fixup_amp_override(struct hda_codec *codec,
2142 const struct hda_fixup *fix, int action)
2143{
2144 if (action == HDA_FIXUP_ACT_PRE_PROBE)
2145 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2146 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2147 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2148 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2149 (1 << AC_AMPCAP_MUTE_SHIFT));
2150}
2151
2152enum {
2153 AD1981_FIXUP_AMP_OVERRIDE,
2154 AD1981_FIXUP_HP_EAPD,
2155};
2156
2157static const struct hda_fixup ad1981_fixups[] = {
2158 [AD1981_FIXUP_AMP_OVERRIDE] = {
2159 .type = HDA_FIXUP_FUNC,
2160 .v.func = ad1981_fixup_amp_override,
2161 },
2162 [AD1981_FIXUP_HP_EAPD] = {
2163 .type = HDA_FIXUP_FUNC,
2164 .v.func = ad1981_fixup_hp_eapd,
2165 .chained = true,
2166 .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
2167 },
2168};
2169
2170static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
2171 SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
2172 SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
2173 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
2174 /* HP nx6320 (reversed SSID, H/W bug) */
2175 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
2176 {}
2177};
2178
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002179static int ad1981_parse_auto_config(struct hda_codec *codec)
2180{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002181 struct ad198x_spec *spec;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002182 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002183
Takashi Iwai361dab32012-05-09 14:35:27 +02002184 err = alloc_ad_spec(codec);
2185 if (err < 0)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002186 return -ENOMEM;
Takashi Iwai361dab32012-05-09 14:35:27 +02002187 spec = codec->spec;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002188
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002189 spec->gen.mixer_nid = 0x0e;
2190 spec->beep_dev_nid = 0x10;
2191 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
Takashi Iwaia928bd22013-01-22 18:18:42 +01002192
2193 snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
2194 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
2195
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002196 err = ad198x_parse_auto_config(codec);
2197 if (err < 0)
2198 goto error;
2199 err = ad1983_add_spdif_mux_ctl(codec);
2200 if (err < 0)
2201 goto error;
Takashi Iwaia928bd22013-01-22 18:18:42 +01002202
2203 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
2204
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002205 return 0;
2206
2207 error:
2208 ad198x_free(codec);
2209 return err;
2210}
2211
2212#ifdef ENABLE_AD_STATIC_QUIRKS
2213static int patch_ad1981(struct hda_codec *codec)
2214{
2215 struct ad198x_spec *spec;
2216 int err, board_config;
2217
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002218 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
2219 ad1981_models,
2220 ad1981_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01002221 if (board_config < 0) {
2222 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
2223 codec->chip_name);
2224 board_config = AD1981_AUTO;
2225 }
2226
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002227 if (board_config == AD1981_AUTO)
2228 return ad1981_parse_auto_config(codec);
2229
2230 err = alloc_ad_spec(codec);
2231 if (err < 0)
2232 return -ENOMEM;
2233 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002234
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01002235 err = snd_hda_attach_beep_device(codec, 0x10);
2236 if (err < 0) {
2237 ad198x_free(codec);
2238 return err;
2239 }
2240 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
2241
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002242 spec->multiout.max_channels = 2;
2243 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
2244 spec->multiout.dac_nids = ad1981_dac_nids;
2245 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01002246 spec->num_adc_nids = 1;
2247 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002248 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002249 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01002250 spec->num_mixers = 1;
2251 spec->mixers[0] = ad1981_mixers;
2252 spec->num_init_verbs = 1;
2253 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002254 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02002255#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02002256 spec->loopback.amplist = ad1981_loopbacks;
2257#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002258 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002259
2260 codec->patch_ops = ad198x_patch_ops;
2261
Takashi Iwai18a815d2006-03-01 19:54:39 +01002262 /* override some parameters */
Takashi Iwai18a815d2006-03-01 19:54:39 +01002263 switch (board_config) {
2264 case AD1981_HP:
2265 spec->mixers[0] = ad1981_hp_mixers;
2266 spec->num_init_verbs = 2;
2267 spec->init_verbs[1] = ad1981_hp_init_verbs;
Takashi Iwai695cd4a2011-06-10 14:37:04 +02002268 if (!is_jack_available(codec, 0x0a))
2269 spec->multiout.dig_out_nid = 0;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002270 spec->input_mux = &ad1981_hp_capture_source;
2271
2272 codec->patch_ops.init = ad1981_hp_init;
2273 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05002274 /* set the upper-limit for mixer amp to 0dB for avoiding the
2275 * possible damage by overloading
2276 */
2277 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2278 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2279 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2280 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2281 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01002282 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002283 case AD1981_THINKPAD:
2284 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002285 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04002286 /* set the upper-limit for mixer amp to 0dB for avoiding the
2287 * possible damage by overloading
2288 */
2289 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2290 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2291 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2292 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2293 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002294 break;
Tobin Davis18768992007-03-12 22:20:51 +01002295 case AD1981_TOSHIBA:
2296 spec->mixers[0] = ad1981_hp_mixers;
2297 spec->mixers[1] = ad1981_toshiba_mixers;
2298 spec->num_init_verbs = 2;
2299 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
2300 spec->multiout.dig_out_nid = 0;
2301 spec->input_mux = &ad1981_hp_capture_source;
2302 codec->patch_ops.init = ad1981_hp_init;
2303 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
2304 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002305 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01002306
2307 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002308 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002309
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002310 return 0;
2311}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002312#else /* ENABLE_AD_STATIC_QUIRKS */
2313#define patch_ad1981 ad1981_parse_auto_config
2314#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002315
2316
2317/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002318 * AD1988
2319 *
2320 * Output pins and routes
2321 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002322 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002323 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
2324 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
2325 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
2326 * port-D 0x12 (mute/hp) <- 0x29 <- 04
2327 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
2328 * port-F 0x16 (mute) <- 0x2a <- 06
2329 * port-G 0x24 (mute) <- 0x27 <- 05
2330 * port-H 0x25 (mute) <- 0x28 <- 0a
2331 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
2332 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002333 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
2334 * (*) 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 +01002335 *
2336 * Input pins and routes
2337 *
2338 * pin boost mix input # / adc input #
2339 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
2340 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
2341 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
2342 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
2343 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
2344 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
2345 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
2346 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
2347 *
2348 *
2349 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01002350 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002351 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002352 *
2353 * Inputs of Analog Mix (0x20)
2354 * 0:Port-B (front mic)
2355 * 1:Port-C/G/H (line-in)
2356 * 2:Port-A
2357 * 3:Port-D (line-in/2)
2358 * 4:Port-E/G/H (mic-in)
2359 * 5:Port-F (mic2-in)
2360 * 6:CD
2361 * 7:Beep
2362 *
2363 * ADC selection
2364 * 0:Port-A
2365 * 1:Port-B (front mic-in)
2366 * 2:Port-C (line-in)
2367 * 3:Port-F (mic2-in)
2368 * 4:Port-E (mic-in)
2369 * 5:CD
2370 * 6:Port-G
2371 * 7:Port-H
2372 * 8:Port-D (line-in/2)
2373 * 9:Mix
2374 *
2375 * Proposed pin assignments by the datasheet
2376 *
2377 * 6-stack
2378 * Port-A front headphone
2379 * B front mic-in
2380 * C rear line-in
2381 * D rear front-out
2382 * E rear mic-in
2383 * F rear surround
2384 * G rear CLFE
2385 * H rear side
2386 *
2387 * 3-stack
2388 * Port-A front headphone
2389 * B front mic
2390 * C rear line-in/surround
2391 * D rear front-out
2392 * E rear mic-in/CLFE
2393 *
2394 * laptop
2395 * Port-A headphone
2396 * B mic-in
2397 * C docking station
2398 * D internal speaker (with EAPD)
2399 * E/F quad mic array
2400 */
2401
2402
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01002403#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002404/* models */
2405enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01002406 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002407 AD1988_6STACK,
2408 AD1988_6STACK_DIG,
2409 AD1988_3STACK,
2410 AD1988_3STACK_DIG,
2411 AD1988_LAPTOP,
2412 AD1988_LAPTOP_DIG,
2413 AD1988_MODEL_LAST,
2414};
2415
Takashi Iwaid32410b12005-11-24 16:06:23 +01002416/* reivision id to check workarounds */
2417#define AD1988A_REV2 0x100200
2418
Takashi Iwai1a806f42006-07-03 15:58:16 +02002419#define is_rev2(codec) \
2420 ((codec)->vendor_id == 0x11d41988 && \
2421 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002422
2423/*
2424 * mixers
2425 */
2426
Takashi Iwai498f5b12011-05-02 11:33:15 +02002427static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002428 0x04, 0x06, 0x05, 0x0a
2429};
2430
Takashi Iwai498f5b12011-05-02 11:33:15 +02002431static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002432 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002433};
2434
2435/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002436static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002437 0x04, 0x05, 0x0a, 0x06
2438};
2439
Takashi Iwai498f5b12011-05-02 11:33:15 +02002440static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002441 0x03
2442};
2443
Takashi Iwai498f5b12011-05-02 11:33:15 +02002444static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002445 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002446};
2447
Takashi Iwai498f5b12011-05-02 11:33:15 +02002448static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002449 0x08, 0x09, 0x0f
2450};
2451
Takashi Iwai498f5b12011-05-02 11:33:15 +02002452static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002453 0x0c, 0x0d, 0x0e
2454};
2455
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002456#define AD1988_SPDIF_OUT 0x02
2457#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002458#define AD1988_SPDIF_IN 0x07
2459
Takashi Iwai498f5b12011-05-02 11:33:15 +02002460static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01002461 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002462};
2463
Takashi Iwai498f5b12011-05-02 11:33:15 +02002464static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002465 .num_items = 5,
2466 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002467 { "Front Mic", 0x1 }, /* port-B */
2468 { "Line", 0x2 }, /* port-C */
2469 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002470 { "CD", 0x5 },
2471 { "Mix", 0x9 },
2472 },
2473};
2474
Takashi Iwai498f5b12011-05-02 11:33:15 +02002475static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002476 .num_items = 3,
2477 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002478 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002479 { "CD", 0x5 },
2480 { "Mix", 0x9 },
2481 },
2482};
2483
2484/*
2485 */
2486static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2487 struct snd_ctl_elem_info *uinfo)
2488{
2489 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2490 struct ad198x_spec *spec = codec->spec;
2491 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2492 spec->num_channel_mode);
2493}
2494
2495static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2496 struct snd_ctl_elem_value *ucontrol)
2497{
2498 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2499 struct ad198x_spec *spec = codec->spec;
2500 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2501 spec->num_channel_mode, spec->multiout.max_channels);
2502}
2503
2504static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2505 struct snd_ctl_elem_value *ucontrol)
2506{
2507 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2508 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002509 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2510 spec->num_channel_mode,
2511 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002512 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002513 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002514 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002515}
2516
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002517/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002518static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002519 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2520 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2521 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2522 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2523 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002524 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002525};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002526
Takashi Iwai498f5b12011-05-02 11:33:15 +02002527static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002528 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2529 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2530 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2531 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2532 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002533 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002534};
2535
Takashi Iwai498f5b12011-05-02 11:33:15 +02002536static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002537 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002538 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2539 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2540 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2541 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2542 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2543 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2544 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2545
2546 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2547 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2548 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2549 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2550 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2551 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2552 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2553 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2554
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002555 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002556 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2557
David Henningsson5f99f862011-01-04 15:24:24 +01002558 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2559 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002560 { } /* end */
2561};
2562
2563/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002564static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002565 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002566 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002567 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2568 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002569 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002570};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002571
Takashi Iwai498f5b12011-05-02 11:33:15 +02002572static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002573 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002574 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2575 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2576 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002577 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002578};
2579
Takashi Iwai498f5b12011-05-02 11:33:15 +02002580static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002581 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002582 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002583 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2584 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2585 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002586 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2587 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2588
2589 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2590 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2591 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2592 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2593 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2594 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2595 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2596 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2597
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002598 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002599 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2600
David Henningsson5f99f862011-01-04 15:24:24 +01002601 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2602 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002603 {
2604 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2605 .name = "Channel Mode",
2606 .info = ad198x_ch_mode_info,
2607 .get = ad198x_ch_mode_get,
2608 .put = ad198x_ch_mode_put,
2609 },
2610
2611 { } /* end */
2612};
2613
2614/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002615static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002616 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002617 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2618 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2619 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2620
2621 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2622 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2623 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2624 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2625 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2626 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2627
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002628 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002629 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2630
David Henningsson5f99f862011-01-04 15:24:24 +01002631 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002632
2633 {
2634 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2635 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002636 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002637 .info = ad198x_eapd_info,
2638 .get = ad198x_eapd_get,
2639 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002640 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002641 },
2642
2643 { } /* end */
2644};
2645
2646/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002647static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002648 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2649 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2650 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2651 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2652 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2653 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2654 {
2655 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2656 /* The multiple "Capture Source" controls confuse alsamixer
2657 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002658 */
2659 /* .name = "Capture Source", */
2660 .name = "Input Source",
2661 .count = 3,
2662 .info = ad198x_mux_enum_info,
2663 .get = ad198x_mux_enum_get,
2664 .put = ad198x_mux_enum_put,
2665 },
2666 { } /* end */
2667};
2668
2669static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2670 struct snd_ctl_elem_info *uinfo)
2671{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002672 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002673 "PCM", "ADC1", "ADC2", "ADC3"
2674 };
2675 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2676 uinfo->count = 1;
2677 uinfo->value.enumerated.items = 4;
2678 if (uinfo->value.enumerated.item >= 4)
2679 uinfo->value.enumerated.item = 3;
2680 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2681 return 0;
2682}
2683
2684static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2685 struct snd_ctl_elem_value *ucontrol)
2686{
2687 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2688 unsigned int sel;
2689
Takashi Iwaibddcf542007-07-24 18:04:05 +02002690 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2691 AC_AMP_GET_INPUT);
2692 if (!(sel & 0x80))
2693 ucontrol->value.enumerated.item[0] = 0;
2694 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002695 sel = snd_hda_codec_read(codec, 0x0b, 0,
2696 AC_VERB_GET_CONNECT_SEL, 0);
2697 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002698 sel++;
2699 else
2700 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002701 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002702 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002703 return 0;
2704}
2705
2706static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2707 struct snd_ctl_elem_value *ucontrol)
2708{
2709 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002710 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002711 int change;
2712
Takashi Iwai35b26722007-05-05 12:17:17 +02002713 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002714 if (val > 3)
2715 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002716 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002717 sel = snd_hda_codec_read(codec, 0x1d, 0,
2718 AC_VERB_GET_AMP_GAIN_MUTE,
2719 AC_AMP_GET_INPUT);
2720 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002721 if (change) {
2722 snd_hda_codec_write_cache(codec, 0x1d, 0,
2723 AC_VERB_SET_AMP_GAIN_MUTE,
2724 AMP_IN_UNMUTE(0));
2725 snd_hda_codec_write_cache(codec, 0x1d, 0,
2726 AC_VERB_SET_AMP_GAIN_MUTE,
2727 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002728 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002729 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002730 sel = snd_hda_codec_read(codec, 0x1d, 0,
2731 AC_VERB_GET_AMP_GAIN_MUTE,
2732 AC_AMP_GET_INPUT | 0x01);
2733 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002734 if (change) {
2735 snd_hda_codec_write_cache(codec, 0x1d, 0,
2736 AC_VERB_SET_AMP_GAIN_MUTE,
2737 AMP_IN_MUTE(0));
2738 snd_hda_codec_write_cache(codec, 0x1d, 0,
2739 AC_VERB_SET_AMP_GAIN_MUTE,
2740 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002741 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002742 sel = snd_hda_codec_read(codec, 0x0b, 0,
2743 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2744 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002745 if (change)
2746 snd_hda_codec_write_cache(codec, 0x0b, 0,
2747 AC_VERB_SET_CONNECT_SEL,
2748 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002749 }
2750 return change;
2751}
2752
Takashi Iwai498f5b12011-05-02 11:33:15 +02002753static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002754 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2755 {
2756 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2757 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002758 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002759 .info = ad1988_spdif_playback_source_info,
2760 .get = ad1988_spdif_playback_source_get,
2761 .put = ad1988_spdif_playback_source_put,
2762 },
2763 { } /* end */
2764};
2765
Takashi Iwai498f5b12011-05-02 11:33:15 +02002766static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002767 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2768 { } /* end */
2769};
2770
Takashi Iwai498f5b12011-05-02 11:33:15 +02002771static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002772 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002773 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002774 { } /* end */
2775};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002776
2777/*
2778 * initialization verbs
2779 */
2780
2781/*
2782 * for 6-stack (+dig)
2783 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002784static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002785 /* Front, Surround, CLFE, side DAC; unmute as default */
2786 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2787 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2788 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2789 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002790 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002791 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002792 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2793 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2794 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2795 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2796 /* Port-D line-out path */
2797 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2798 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2799 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2800 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2801 /* Port-F surround path */
2802 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2803 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2804 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2805 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2806 /* Port-G CLFE path */
2807 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2808 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2809 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2810 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2811 /* Port-H side path */
2812 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2813 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2814 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2815 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2816 /* Mono out path */
2817 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2818 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2819 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2820 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2821 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2822 /* Port-B front mic-in path */
2823 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2824 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2825 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2826 /* Port-C line-in path */
2827 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2828 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2829 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2830 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2831 /* Port-E mic-in path */
2832 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2833 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2834 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2835 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002836 /* Analog CD Input */
2837 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002838 /* Analog Mix output amp */
2839 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002840
2841 { }
2842};
2843
Takashi Iwai498f5b12011-05-02 11:33:15 +02002844static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002845 /* Headphone; unmute as default */
2846 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2847 /* Port-A front headphon path */
2848 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2849 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2850 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2851 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2852 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002853
2854 { }
2855};
2856
Takashi Iwai498f5b12011-05-02 11:33:15 +02002857static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002858 /* mute analog mix */
2859 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2860 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2861 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2862 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2863 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2864 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2865 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2866 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2867 /* select ADCs - front-mic */
2868 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2869 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2870 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002871
2872 { }
2873};
2874
Takashi Iwai498f5b12011-05-02 11:33:15 +02002875static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002876 /* SPDIF out sel */
2877 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2878 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2879 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002880 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002881 /* SPDIF out pin */
2882 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002883
2884 { }
2885};
2886
Takashi Iwai498f5b12011-05-02 11:33:15 +02002887static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002888 /* unmute SPDIF input pin */
2889 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2890 { }
2891};
2892
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002893/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002894static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002895 /* SPDIF-1 out pin */
2896 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002897 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002898 /* SPDIF-2/HDMI out pin */
2899 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2900 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002901 { }
2902};
2903
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002904/*
2905 * verbs for 3stack (+dig)
2906 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002907static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002908 /* set port-C to line-in */
2909 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2910 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2911 /* set port-E to mic-in */
2912 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2913 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2914 { } /* end */
2915};
2916
Takashi Iwai498f5b12011-05-02 11:33:15 +02002917static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002918 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002919 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002920 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002921 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002922 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002923 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002924 { } /* end */
2925};
2926
Takashi Iwai498f5b12011-05-02 11:33:15 +02002927static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002928 { 2, ad1988_3stack_ch2_init },
2929 { 6, ad1988_3stack_ch6_init },
2930};
2931
Takashi Iwai498f5b12011-05-02 11:33:15 +02002932static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002933 /* Front, Surround, CLFE, side DAC; unmute as default */
2934 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2935 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2936 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2937 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002938 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002939 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002940 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2941 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2942 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2943 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2944 /* Port-D line-out path */
2945 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2946 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2947 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2948 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2949 /* Mono out path */
2950 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2951 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2952 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2953 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2954 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2955 /* Port-B front mic-in path */
2956 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2957 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2958 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002959 /* Port-C line-in/surround path - 6ch mode as default */
2960 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2961 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002962 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002963 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002964 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002965 /* Port-E mic-in/CLFE path - 6ch mode as default */
2966 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2967 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002968 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002969 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002970 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2971 /* mute analog mix */
2972 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2973 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2974 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2975 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2976 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2977 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2978 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2979 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2980 /* select ADCs - front-mic */
2981 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2982 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2983 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002984 /* Analog Mix output amp */
2985 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002986 { }
2987};
2988
2989/*
2990 * verbs for laptop mode (+dig)
2991 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002992static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002993 /* unmute port-A and mute port-D */
2994 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2995 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2996 { } /* end */
2997};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002998static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002999 /* mute port-A and unmute port-D */
3000 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
3001 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
3002 { } /* end */
3003};
3004
3005#define AD1988_HP_EVENT 0x01
3006
Takashi Iwai498f5b12011-05-02 11:33:15 +02003007static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01003008 /* Front, Surround, CLFE, side DAC; unmute as default */
3009 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3010 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3011 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3012 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003013 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08003014 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003015 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3016 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3017 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3018 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3019 /* unsolicited event for pin-sense */
3020 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
3021 /* Port-D line-out path + EAPD */
3022 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3023 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3024 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3025 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3026 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
3027 /* Mono out path */
3028 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
3029 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3030 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3031 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3032 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
3033 /* Port-B mic-in path */
3034 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3035 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3036 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3037 /* Port-C docking station - try to output */
3038 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3039 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
3040 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3041 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
3042 /* mute analog mix */
3043 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3044 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3045 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3046 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3047 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3048 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3049 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
3050 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
3051 /* select ADCs - mic */
3052 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
3053 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
3054 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02003055 /* Analog Mix output amp */
3056 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003057 { }
3058};
3059
3060static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
3061{
3062 if ((res >> 26) != AD1988_HP_EVENT)
3063 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01003064 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003065 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
3066 else
3067 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
3068}
3069
Takashi Iwai83012a72012-08-24 18:38:08 +02003070#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02003071static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003072 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3073 { 0x20, HDA_INPUT, 1 }, /* Line */
3074 { 0x20, HDA_INPUT, 4 }, /* Mic */
3075 { 0x20, HDA_INPUT, 6 }, /* CD */
3076 { } /* end */
3077};
3078#endif
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003079#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003080
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003081static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
3082 struct snd_ctl_elem_info *uinfo)
3083{
3084 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3085 static const char * const texts[] = {
3086 "PCM", "ADC1", "ADC2", "ADC3",
3087 };
3088 int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
3089 if (num_conns > 4)
3090 num_conns = 4;
3091 return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
3092}
3093
3094static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
3095 struct snd_ctl_elem_value *ucontrol)
3096{
3097 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3098 struct ad198x_spec *spec = codec->spec;
3099
3100 ucontrol->value.enumerated.item[0] = spec->cur_smux;
3101 return 0;
3102}
3103
3104static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
3105 struct snd_ctl_elem_value *ucontrol)
3106{
3107 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3108 struct ad198x_spec *spec = codec->spec;
3109 unsigned int val = ucontrol->value.enumerated.item[0];
3110 struct nid_path *path;
3111 int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
3112
3113 if (val >= num_conns)
3114 return -EINVAL;
3115 if (spec->cur_smux == val)
3116 return 0;
3117
3118 mutex_lock(&codec->control_mutex);
3119 codec->cached_write = 1;
3120 path = snd_hda_get_path_from_idx(codec,
3121 spec->smux_paths[spec->cur_smux]);
3122 if (path)
3123 snd_hda_activate_path(codec, path, false, true);
3124 path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
3125 if (path)
3126 snd_hda_activate_path(codec, path, true, true);
3127 spec->cur_smux = val;
3128 codec->cached_write = 0;
3129 mutex_unlock(&codec->control_mutex);
3130 snd_hda_codec_flush_cache(codec); /* flush the updates */
3131 return 1;
3132}
3133
3134static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
3135 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3136 .name = "IEC958 Playback Source",
3137 .info = ad1988_auto_smux_enum_info,
3138 .get = ad1988_auto_smux_enum_get,
3139 .put = ad1988_auto_smux_enum_put,
3140};
3141
3142static int ad1988_auto_init(struct hda_codec *codec)
3143{
3144 struct ad198x_spec *spec = codec->spec;
3145 int i, err;
3146
3147 err = snd_hda_gen_init(codec);
3148 if (err < 0)
3149 return err;
3150 if (!spec->gen.autocfg.dig_outs)
3151 return 0;
3152
3153 for (i = 0; i < 4; i++) {
3154 struct nid_path *path;
3155 path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
3156 if (path)
3157 snd_hda_activate_path(codec, path, path->active, false);
3158 }
3159
3160 return 0;
3161}
3162
3163static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
3164{
3165 struct ad198x_spec *spec = codec->spec;
3166 int i, num_conns;
3167 /* we create four static faked paths, since AD codecs have odd
3168 * widget connections regarding the SPDIF out source
3169 */
3170 static struct nid_path fake_paths[4] = {
3171 {
3172 .depth = 3,
3173 .path = { 0x02, 0x1d, 0x1b },
3174 .idx = { 0, 0, 0 },
3175 .multi = { 0, 0, 0 },
3176 },
3177 {
3178 .depth = 4,
3179 .path = { 0x08, 0x0b, 0x1d, 0x1b },
3180 .idx = { 0, 0, 1, 0 },
3181 .multi = { 0, 1, 0, 0 },
3182 },
3183 {
3184 .depth = 4,
3185 .path = { 0x09, 0x0b, 0x1d, 0x1b },
3186 .idx = { 0, 1, 1, 0 },
3187 .multi = { 0, 1, 0, 0 },
3188 },
3189 {
3190 .depth = 4,
3191 .path = { 0x0f, 0x0b, 0x1d, 0x1b },
3192 .idx = { 0, 2, 1, 0 },
3193 .multi = { 0, 1, 0, 0 },
3194 },
3195 };
3196
3197 /* SPDIF source mux appears to be present only on AD1988A */
3198 if (!spec->gen.autocfg.dig_outs ||
3199 get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
3200 return 0;
3201
3202 num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
3203 if (num_conns != 3 && num_conns != 4)
3204 return 0;
3205
3206 for (i = 0; i < num_conns; i++) {
3207 struct nid_path *path = snd_array_new(&spec->gen.paths);
3208 if (!path)
3209 return -ENOMEM;
3210 *path = fake_paths[i];
3211 if (!i)
3212 path->active = 1;
3213 spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
3214 }
3215
3216 if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
3217 return -ENOMEM;
3218
3219 codec->patch_ops.init = ad1988_auto_init;
3220
3221 return 0;
3222}
3223
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003224/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01003225 */
3226
Takashi Iwaid32410b12005-11-24 16:06:23 +01003227static int ad1988_parse_auto_config(struct hda_codec *codec)
3228{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003229 struct ad198x_spec *spec;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003230 int err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003231
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003232 err = alloc_ad_spec(codec);
3233 if (err < 0)
3234 return err;
3235 spec = codec->spec;
3236
Takashi Iwaif2f8be42013-01-21 16:40:16 +01003237 spec->gen.mixer_nid = 0x20;
Takashi Iwaie4a395e2013-01-23 17:00:31 +01003238 spec->gen.mixer_merge_nid = 0x21;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003239 spec->beep_dev_nid = 0x10;
3240 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003241 err = ad198x_parse_auto_config(codec);
3242 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003243 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003244 err = ad1988_add_spdif_mux_ctl(codec);
3245 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003246 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003247 return 0;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003248
3249 error:
3250 ad198x_free(codec);
3251 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003252}
3253
Takashi Iwaid32410b12005-11-24 16:06:23 +01003254/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003255 */
3256
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003257#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwaiea734962011-01-17 11:29:34 +01003258static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003259 [AD1988_6STACK] = "6stack",
3260 [AD1988_6STACK_DIG] = "6stack-dig",
3261 [AD1988_3STACK] = "3stack",
3262 [AD1988_3STACK_DIG] = "3stack-dig",
3263 [AD1988_LAPTOP] = "laptop",
3264 [AD1988_LAPTOP_DIG] = "laptop-dig",
3265 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003266};
3267
Takashi Iwai498f5b12011-05-02 11:33:15 +02003268static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003269 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003270 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003271 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01003272 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003273 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003274 {}
3275};
3276
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003277static int patch_ad1988(struct hda_codec *codec)
3278{
3279 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003280 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003281
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003282 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003283 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003284 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003285 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3286 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003287 board_config = AD1988_AUTO;
3288 }
3289
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003290 if (board_config == AD1988_AUTO)
3291 return ad1988_parse_auto_config(codec);
3292
3293 err = alloc_ad_spec(codec);
3294 if (err < 0)
3295 return err;
3296 spec = codec->spec;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003297
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003298 if (is_rev2(codec))
3299 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3300
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003301 err = snd_hda_attach_beep_device(codec, 0x10);
3302 if (err < 0) {
3303 ad198x_free(codec);
3304 return err;
3305 }
3306 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3307
Raymond Yau356aab72011-08-31 10:30:59 +08003308 if (!spec->multiout.hp_nid)
Raymond Yau34588702011-09-23 19:03:25 +08003309 spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003310 switch (board_config) {
3311 case AD1988_6STACK:
3312 case AD1988_6STACK_DIG:
3313 spec->multiout.max_channels = 8;
3314 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003315 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003316 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3317 else
3318 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003319 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003320 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003321 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003322 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3323 else
3324 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08003325 spec->mixers[1] = ad1988_6stack_mixers2;
3326 spec->num_init_verbs = 1;
3327 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yau34588702011-09-23 19:03:25 +08003328 if (board_config == AD1988_6STACK_DIG) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003329 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3330 spec->dig_in_nid = AD1988_SPDIF_IN;
3331 }
3332 break;
3333 case AD1988_3STACK:
3334 case AD1988_3STACK_DIG:
3335 spec->multiout.max_channels = 6;
3336 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003337 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003338 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3339 else
3340 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003341 spec->input_mux = &ad1988_6stack_capture_source;
3342 spec->channel_mode = ad1988_3stack_modes;
3343 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003344 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003345 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003346 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3347 else
3348 spec->mixers[0] = ad1988_3stack_mixers1;
3349 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003350 spec->num_init_verbs = 1;
3351 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3352 if (board_config == AD1988_3STACK_DIG)
3353 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3354 break;
3355 case AD1988_LAPTOP:
3356 case AD1988_LAPTOP_DIG:
3357 spec->multiout.max_channels = 2;
3358 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003359 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003360 spec->input_mux = &ad1988_laptop_capture_source;
3361 spec->num_mixers = 1;
3362 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003363 codec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003364 spec->num_init_verbs = 1;
3365 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3366 if (board_config == AD1988_LAPTOP_DIG)
3367 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3368 break;
3369 }
3370
Takashi Iwaid32410b12005-11-24 16:06:23 +01003371 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3372 spec->adc_nids = ad1988_adc_nids;
3373 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003374 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3375 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3376 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003377 if (codec->vendor_id >= 0x11d4989a) {
3378 spec->mixers[spec->num_mixers++] =
3379 ad1989_spdif_out_mixers;
3380 spec->init_verbs[spec->num_init_verbs++] =
3381 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003382 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003383 } else {
3384 spec->mixers[spec->num_mixers++] =
3385 ad1988_spdif_out_mixers;
3386 spec->init_verbs[spec->num_init_verbs++] =
3387 ad1988_spdif_init_verbs;
3388 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003389 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003390 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003391 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003392 spec->init_verbs[spec->num_init_verbs++] =
3393 ad1988_spdif_in_init_verbs;
3394 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003395
3396 codec->patch_ops = ad198x_patch_ops;
3397 switch (board_config) {
3398 case AD1988_LAPTOP:
3399 case AD1988_LAPTOP_DIG:
3400 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3401 break;
3402 }
Takashi Iwai83012a72012-08-24 18:38:08 +02003403#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02003404 spec->loopback.amplist = ad1988_loopbacks;
3405#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003406 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003407
Takashi Iwai729d55b2009-12-25 22:49:01 +01003408 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003409 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003410
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003411 return 0;
3412}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003413#else /* ENABLE_AD_STATIC_QUIRKS */
3414#define patch_ad1988 ad1988_parse_auto_config
3415#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003416
3417
3418/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003419 * AD1884 / AD1984
3420 *
3421 * port-B - front line/mic-in
3422 * port-E - aux in/out
3423 * port-F - aux in/out
3424 * port-C - rear line/mic-in
3425 * port-D - rear line/hp-out
3426 * port-A - front line/hp-out
3427 *
3428 * AD1984 = AD1884 + two digital mic-ins
3429 *
3430 * FIXME:
3431 * For simplicity, we share the single DAC for both HP and line-outs
3432 * right now. The inidividual playbacks could be easily implemented,
3433 * but no build-up framework is given, so far.
3434 */
3435
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003436#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +02003437static const hda_nid_t ad1884_dac_nids[1] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003438 0x04,
3439};
3440
Takashi Iwai498f5b12011-05-02 11:33:15 +02003441static const hda_nid_t ad1884_adc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003442 0x08, 0x09,
3443};
3444
Takashi Iwai498f5b12011-05-02 11:33:15 +02003445static const hda_nid_t ad1884_capsrc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003446 0x0c, 0x0d,
3447};
3448
3449#define AD1884_SPDIF_OUT 0x02
3450
Takashi Iwai498f5b12011-05-02 11:33:15 +02003451static const struct hda_input_mux ad1884_capture_source = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003452 .num_items = 4,
3453 .items = {
3454 { "Front Mic", 0x0 },
3455 { "Mic", 0x1 },
3456 { "CD", 0x2 },
3457 { "Mix", 0x3 },
3458 },
3459};
3460
Takashi Iwai498f5b12011-05-02 11:33:15 +02003461static const struct snd_kcontrol_new ad1884_base_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003462 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3463 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3464 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3465 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3466 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3467 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3468 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3469 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3470 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3471 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3472 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3473 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003474 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3475 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003476 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3477 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3478 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3479 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3480 {
3481 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3482 /* The multiple "Capture Source" controls confuse alsamixer
3483 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003484 */
3485 /* .name = "Capture Source", */
3486 .name = "Input Source",
3487 .count = 2,
3488 .info = ad198x_mux_enum_info,
3489 .get = ad198x_mux_enum_get,
3490 .put = ad198x_mux_enum_put,
3491 },
3492 /* SPDIF controls */
3493 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3494 {
3495 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3496 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3497 /* identical with ad1983 */
3498 .info = ad1983_spdif_route_info,
3499 .get = ad1983_spdif_route_get,
3500 .put = ad1983_spdif_route_put,
3501 },
3502 { } /* end */
3503};
3504
Takashi Iwai498f5b12011-05-02 11:33:15 +02003505static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003506 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3507 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3508 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003509 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003510 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003511 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003512 { } /* end */
3513};
3514
3515/*
3516 * initialization verbs
3517 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003518static const struct hda_verb ad1884_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003519 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003520 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3521 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003522 /* Port-A (HP) mixer */
3523 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3524 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3525 /* Port-A pin */
3526 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3527 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3528 /* HP selector - select DAC2 */
3529 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3530 /* Port-D (Line-out) mixer */
3531 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3532 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3533 /* Port-D pin */
3534 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3535 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3536 /* Mono-out mixer */
3537 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3538 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3539 /* Mono-out pin */
3540 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3541 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3542 /* Mono selector */
3543 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3544 /* Port-B (front mic) pin */
3545 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003546 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003547 /* Port-C (rear mic) pin */
3548 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003549 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003550 /* Analog mixer; mute as default */
3551 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3552 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3553 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3554 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3555 /* Analog Mix output amp */
3556 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3557 /* SPDIF output selector */
3558 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3559 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3560 { } /* end */
3561};
3562
Takashi Iwai83012a72012-08-24 18:38:08 +02003563#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02003564static const struct hda_amp_list ad1884_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003565 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3566 { 0x20, HDA_INPUT, 1 }, /* Mic */
3567 { 0x20, HDA_INPUT, 2 }, /* CD */
3568 { 0x20, HDA_INPUT, 4 }, /* Docking */
3569 { } /* end */
3570};
3571#endif
3572
Takashi Iwaiea734962011-01-17 11:29:34 +01003573static const char * const ad1884_slave_vols[] = {
Takashi Iwai9322ca52012-02-03 14:28:01 +01003574 "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD",
David Henningssonc40bd912012-09-19 12:19:47 +02003575 "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003576 NULL
3577};
3578
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003579enum {
3580 AD1884_AUTO,
3581 AD1884_BASIC,
3582 AD1884_MODELS
3583};
3584
3585static const char * const ad1884_models[AD1884_MODELS] = {
3586 [AD1884_AUTO] = "auto",
3587 [AD1884_BASIC] = "basic",
3588};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003589#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003590
Takashi Iwaia928bd22013-01-22 18:18:42 +01003591
3592/* set the upper-limit for mixer amp to 0dB for avoiding the possible
3593 * damage by overloading
3594 */
3595static void ad1884_fixup_amp_override(struct hda_codec *codec,
3596 const struct hda_fixup *fix, int action)
3597{
3598 if (action == HDA_FIXUP_ACT_PRE_PROBE)
3599 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
3600 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3601 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3602 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3603 (1 << AC_AMPCAP_MUTE_SHIFT));
3604}
3605
3606static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
3607 const struct hda_fixup *fix, int action)
3608{
3609 struct ad198x_spec *spec = codec->spec;
3610
3611 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
3612 if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
3613 spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
3614 else
3615 spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
3616 if (spec->eapd_nid)
3617 spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
3618 }
3619}
3620
3621enum {
3622 AD1884_FIXUP_AMP_OVERRIDE,
3623 AD1884_FIXUP_HP_EAPD,
3624};
3625
3626static const struct hda_fixup ad1884_fixups[] = {
3627 [AD1884_FIXUP_AMP_OVERRIDE] = {
3628 .type = HDA_FIXUP_FUNC,
3629 .v.func = ad1884_fixup_amp_override,
3630 },
3631 [AD1884_FIXUP_HP_EAPD] = {
3632 .type = HDA_FIXUP_FUNC,
3633 .v.func = ad1884_fixup_hp_eapd,
3634 .chained = true,
3635 .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
3636 },
3637};
3638
3639static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
3640 SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
3641 {}
3642};
3643
3644
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003645static int ad1884_parse_auto_config(struct hda_codec *codec)
3646{
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003647 struct ad198x_spec *spec;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003648 int err;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003649
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003650 err = alloc_ad_spec(codec);
3651 if (err < 0)
3652 return err;
3653 spec = codec->spec;
3654
Takashi Iwaif2f8be42013-01-21 16:40:16 +01003655 spec->gen.mixer_nid = 0x20;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003656 spec->beep_dev_nid = 0x10;
3657 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
Takashi Iwaia928bd22013-01-22 18:18:42 +01003658
3659 snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
3660 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
3661
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003662 err = ad198x_parse_auto_config(codec);
3663 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003664 goto error;
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003665 err = ad1983_add_spdif_mux_ctl(codec);
3666 if (err < 0)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003667 goto error;
Takashi Iwaia928bd22013-01-22 18:18:42 +01003668
3669 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
3670
Takashi Iwai272f3ea2013-01-22 15:31:33 +01003671 return 0;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003672
3673 error:
3674 ad198x_free(codec);
3675 return err;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003676}
3677
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003678#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003679static int patch_ad1884_basic(struct hda_codec *codec)
Takashi Iwai2bac6472007-05-18 18:21:41 +02003680{
3681 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003682 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003683
Takashi Iwai361dab32012-05-09 14:35:27 +02003684 err = alloc_ad_spec(codec);
3685 if (err < 0)
3686 return err;
3687 spec = codec->spec;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003688
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003689 err = snd_hda_attach_beep_device(codec, 0x10);
3690 if (err < 0) {
3691 ad198x_free(codec);
3692 return err;
3693 }
3694 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3695
Takashi Iwai2bac6472007-05-18 18:21:41 +02003696 spec->multiout.max_channels = 2;
3697 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3698 spec->multiout.dac_nids = ad1884_dac_nids;
3699 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3700 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3701 spec->adc_nids = ad1884_adc_nids;
3702 spec->capsrc_nids = ad1884_capsrc_nids;
3703 spec->input_mux = &ad1884_capture_source;
3704 spec->num_mixers = 1;
3705 spec->mixers[0] = ad1884_base_mixers;
3706 spec->num_init_verbs = 1;
3707 spec->init_verbs[0] = ad1884_init_verbs;
3708 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02003709#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02003710 spec->loopback.amplist = ad1884_loopbacks;
3711#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003712 spec->vmaster_nid = 0x04;
3713 /* we need to cover all playback volumes */
3714 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai18478e82012-03-09 17:51:10 +01003715 /* slaves may contain input volumes, so we can't raise to 0dB blindly */
3716 spec->avoid_init_slave_vol = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003717
3718 codec->patch_ops = ad198x_patch_ops;
3719
Takashi Iwai729d55b2009-12-25 22:49:01 +01003720 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003721 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003722
Takashi Iwai2bac6472007-05-18 18:21:41 +02003723 return 0;
3724}
3725
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003726static int patch_ad1884(struct hda_codec *codec)
3727{
3728 int board_config;
3729
3730 board_config = snd_hda_check_board_config(codec, AD1884_MODELS,
3731 ad1884_models, NULL);
Takashi Iwai657e1b92013-01-22 18:42:39 +01003732 if (board_config < 0) {
3733 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3734 codec->chip_name);
3735 board_config = AD1884_AUTO;
3736 }
3737
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003738 if (board_config == AD1884_AUTO)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003739 return ad1884_parse_auto_config(codec);
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003740 else
3741 return patch_ad1884_basic(codec);
3742}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003743#else /* ENABLE_AD_STATIC_QUIRKS */
3744#define patch_ad1884 ad1884_parse_auto_config
3745#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003746
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003747
3748#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai2bac6472007-05-18 18:21:41 +02003749/*
3750 * Lenovo Thinkpad T61/X61
3751 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003752static const struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003753 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003754 .items = {
3755 { "Mic", 0x0 },
3756 { "Internal Mic", 0x1 },
3757 { "Mix", 0x3 },
David Henningssonc40bd912012-09-19 12:19:47 +02003758 { "Dock Mic", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003759 },
3760};
3761
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003762
3763/*
3764 * Dell Precision T3400
3765 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003766static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003767 .num_items = 3,
3768 .items = {
3769 { "Front Mic", 0x0 },
3770 { "Line-In", 0x1 },
3771 { "Mix", 0x3 },
3772 },
3773};
3774
3775
Takashi Iwai498f5b12011-05-02 11:33:15 +02003776static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003777 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3778 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3779 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3780 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3781 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3782 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3783 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3784 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003785 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3786 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
David Henningssonc40bd912012-09-19 12:19:47 +02003787 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3788 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003789 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3790 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3791 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003792 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3793 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3794 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3795 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3796 {
3797 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3798 /* The multiple "Capture Source" controls confuse alsamixer
3799 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003800 */
3801 /* .name = "Capture Source", */
3802 .name = "Input Source",
3803 .count = 2,
3804 .info = ad198x_mux_enum_info,
3805 .get = ad198x_mux_enum_get,
3806 .put = ad198x_mux_enum_put,
3807 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003808 /* SPDIF controls */
3809 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3810 {
3811 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3812 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3813 /* identical with ad1983 */
3814 .info = ad1983_spdif_route_info,
3815 .get = ad1983_spdif_route_get,
3816 .put = ad1983_spdif_route_put,
3817 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003818 { } /* end */
3819};
3820
3821/* additional verbs */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003822static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003823 /* Port-E (docking station mic) pin */
3824 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3825 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3826 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003827 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003828 /* Analog PC Beeper - allow firmware/ACPI beeps */
3829 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003830 /* Analog mixer - docking mic; mute as default */
3831 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003832 /* enable EAPD bit */
3833 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003834 { } /* end */
3835};
3836
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003837/*
3838 * Dell Precision T3400
3839 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003840static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003841 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3842 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3843 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3844 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3845 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3846 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3847 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3848 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3849 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003850 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
3851 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003852 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3853 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3854 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3855 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3856 {
3857 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3858 /* The multiple "Capture Source" controls confuse alsamixer
3859 * So call somewhat different..
3860 */
3861 /* .name = "Capture Source", */
3862 .name = "Input Source",
3863 .count = 2,
3864 .info = ad198x_mux_enum_info,
3865 .get = ad198x_mux_enum_get,
3866 .put = ad198x_mux_enum_put,
3867 },
3868 { } /* end */
3869};
3870
Takashi Iwai2bac6472007-05-18 18:21:41 +02003871/* Digial MIC ADC NID 0x05 + 0x06 */
3872static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3873 struct hda_codec *codec,
3874 unsigned int stream_tag,
3875 unsigned int format,
3876 struct snd_pcm_substream *substream)
3877{
3878 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3879 stream_tag, 0, format);
3880 return 0;
3881}
3882
3883static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3884 struct hda_codec *codec,
3885 struct snd_pcm_substream *substream)
3886{
Takashi Iwai888afa12008-03-18 09:57:50 +01003887 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003888 return 0;
3889}
3890
Takashi Iwai498f5b12011-05-02 11:33:15 +02003891static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003892 .substreams = 2,
3893 .channels_min = 2,
3894 .channels_max = 2,
3895 .nid = 0x05,
3896 .ops = {
3897 .prepare = ad1984_pcm_dmic_prepare,
3898 .cleanup = ad1984_pcm_dmic_cleanup
3899 },
3900};
3901
3902static int ad1984_build_pcms(struct hda_codec *codec)
3903{
3904 struct ad198x_spec *spec = codec->spec;
3905 struct hda_pcm *info;
3906 int err;
3907
3908 err = ad198x_build_pcms(codec);
3909 if (err < 0)
3910 return err;
3911
3912 info = spec->pcm_rec + codec->num_pcms;
3913 codec->num_pcms++;
3914 info->name = "AD1984 Digital Mic";
3915 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3916 return 0;
3917}
3918
3919/* models */
3920enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003921 AD1984_AUTO,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003922 AD1984_BASIC,
3923 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003924 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003925 AD1984_MODELS
3926};
3927
Takashi Iwaiea734962011-01-17 11:29:34 +01003928static const char * const ad1984_models[AD1984_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003929 [AD1984_AUTO] = "auto",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003930 [AD1984_BASIC] = "basic",
3931 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003932 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003933};
3934
Takashi Iwai498f5b12011-05-02 11:33:15 +02003935static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003936 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003937 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003938 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003939 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003940 {}
3941};
3942
3943static int patch_ad1984(struct hda_codec *codec)
3944{
3945 struct ad198x_spec *spec;
3946 int board_config, err;
3947
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003948 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3949 ad1984_models, ad1984_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01003950 if (board_config < 0) {
3951 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3952 codec->chip_name);
3953 board_config = AD1984_AUTO;
3954 }
3955
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003956 if (board_config == AD1984_AUTO)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003957 return ad1884_parse_auto_config(codec);
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003958
3959 err = patch_ad1884_basic(codec);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003960 if (err < 0)
3961 return err;
3962 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01003963
Takashi Iwai2bac6472007-05-18 18:21:41 +02003964 switch (board_config) {
3965 case AD1984_BASIC:
3966 /* additional digital mics */
3967 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3968 codec->patch_ops.build_pcms = ad1984_build_pcms;
3969 break;
3970 case AD1984_THINKPAD:
Jerone Young68c18692010-08-03 01:46:44 -05003971 if (codec->subsystem_id == 0x17aa20fb) {
3972 /* Thinpad X300 does not have the ability to do SPDIF,
3973 or attach to docking station to use SPDIF */
3974 spec->multiout.dig_out_nid = 0;
3975 } else
3976 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003977 spec->input_mux = &ad1984_thinkpad_capture_source;
3978 spec->mixers[0] = ad1984_thinkpad_mixers;
3979 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003980 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003981 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003982 case AD1984_DELL_DESKTOP:
3983 spec->multiout.dig_out_nid = 0;
3984 spec->input_mux = &ad1984_dell_desktop_capture_source;
3985 spec->mixers[0] = ad1984_dell_desktop_mixers;
3986 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003987 }
3988 return 0;
3989}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01003990#else /* ENABLE_AD_STATIC_QUIRKS */
3991#define patch_ad1984 ad1884_parse_auto_config
3992#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai2bac6472007-05-18 18:21:41 +02003993
3994
3995/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003996 * AD1883 / AD1884A / AD1984A / AD1984B
3997 *
3998 * port-B (0x14) - front mic-in
3999 * port-E (0x1c) - rear mic-in
4000 * port-F (0x16) - CD / ext out
4001 * port-C (0x15) - rear line-in
4002 * port-D (0x12) - rear line-out
4003 * port-A (0x11) - front hp-out
4004 *
4005 * AD1984A = AD1884A + digital-mic
4006 * AD1883 = equivalent with AD1984A
4007 * AD1984B = AD1984A + extra SPDIF-out
4008 *
4009 * FIXME:
4010 * We share the single DAC for both HP and line-outs (see AD1884/1984).
4011 */
4012
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004013#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +02004014static const hda_nid_t ad1884a_dac_nids[1] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004015 0x03,
4016};
4017
4018#define ad1884a_adc_nids ad1884_adc_nids
4019#define ad1884a_capsrc_nids ad1884_capsrc_nids
4020
4021#define AD1884A_SPDIF_OUT 0x02
4022
Takashi Iwai498f5b12011-05-02 11:33:15 +02004023static const struct hda_input_mux ad1884a_capture_source = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004024 .num_items = 5,
4025 .items = {
4026 { "Front Mic", 0x0 },
4027 { "Mic", 0x4 },
4028 { "Line", 0x1 },
4029 { "CD", 0x2 },
4030 { "Mix", 0x3 },
4031 },
4032};
4033
Takashi Iwai498f5b12011-05-02 11:33:15 +02004034static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004035 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4036 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4037 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4038 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4039 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4040 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
4041 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4042 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4043 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4044 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4045 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4046 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4047 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4048 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4049 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
4050 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004051 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4052 HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
4053 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004054 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4055 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4056 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4057 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4058 {
4059 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4060 /* The multiple "Capture Source" controls confuse alsamixer
4061 * So call somewhat different..
4062 */
4063 /* .name = "Capture Source", */
4064 .name = "Input Source",
4065 .count = 2,
4066 .info = ad198x_mux_enum_info,
4067 .get = ad198x_mux_enum_get,
4068 .put = ad198x_mux_enum_put,
4069 },
4070 /* SPDIF controls */
4071 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4072 {
4073 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4074 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4075 /* identical with ad1983 */
4076 .info = ad1983_spdif_route_info,
4077 .get = ad1983_spdif_route_get,
4078 .put = ad1983_spdif_route_put,
4079 },
4080 { } /* end */
4081};
4082
4083/*
4084 * initialization verbs
4085 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004086static const struct hda_verb ad1884a_init_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004087 /* DACs; unmute as default */
4088 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4089 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4090 /* Port-A (HP) mixer - route only from analog mixer */
4091 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4092 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4093 /* Port-A pin */
4094 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4095 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4096 /* Port-D (Line-out) mixer - route only from analog mixer */
4097 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4098 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4099 /* Port-D pin */
4100 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4101 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4102 /* Mono-out mixer - route only from analog mixer */
4103 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4104 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4105 /* Mono-out pin */
4106 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4107 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4108 /* Port-B (front mic) pin */
4109 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004110 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004111 /* Port-C (rear line-in) pin */
4112 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004113 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004114 /* Port-E (rear mic) pin */
4115 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4116 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4117 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
4118 /* Port-F (CD) pin */
4119 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4120 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4121 /* Analog mixer; mute as default */
4122 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4123 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4124 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4125 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4126 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
4127 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4128 /* Analog Mix output amp */
4129 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4130 /* capture sources */
4131 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
4132 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4133 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4134 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4135 /* SPDIF output amp */
4136 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4137 { } /* end */
4138};
4139
Takashi Iwai83012a72012-08-24 18:38:08 +02004140#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02004141static const struct hda_amp_list ad1884a_loopbacks[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004142 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4143 { 0x20, HDA_INPUT, 1 }, /* Mic */
4144 { 0x20, HDA_INPUT, 2 }, /* CD */
4145 { 0x20, HDA_INPUT, 4 }, /* Docking */
4146 { } /* end */
4147};
4148#endif
4149
4150/*
4151 * Laptop model
4152 *
4153 * Port A: Headphone jack
4154 * Port B: MIC jack
4155 * Port C: Internal MIC
4156 * Port D: Dock Line Out (if enabled)
4157 * Port E: Dock Line In (if enabled)
4158 * Port F: Internal speakers
4159 */
4160
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004161static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
4162 struct snd_ctl_elem_value *ucontrol)
4163{
4164 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4165 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
4166 int mute = (!ucontrol->value.integer.value[0] &&
4167 !ucontrol->value.integer.value[1]);
4168 /* toggle GPIO1 according to the mute state */
4169 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
4170 mute ? 0x02 : 0x0);
4171 return ret;
4172}
Takashi Iwaic5059252008-02-16 09:43:56 +01004173
Takashi Iwai498f5b12011-05-02 11:33:15 +02004174static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004175 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004176 {
4177 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4178 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004179 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004180 .info = snd_hda_mixer_amp_switch_info,
4181 .get = snd_hda_mixer_amp_switch_get,
4182 .put = ad1884a_mobile_master_sw_put,
4183 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4184 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004185 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4186 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4187 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4188 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4189 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4190 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4191 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4192 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4193 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004194 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4195 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4196 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004197 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4198 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004199 { } /* end */
4200};
4201
Takashi Iwai498f5b12011-05-02 11:33:15 +02004202static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004203 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02004204 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4205 {
4206 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4207 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004208 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02004209 .info = snd_hda_mixer_amp_switch_info,
4210 .get = snd_hda_mixer_amp_switch_get,
4211 .put = ad1884a_mobile_master_sw_put,
4212 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4213 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004214 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4215 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02004216 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
4217 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004218 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4219 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004220 { } /* end */
4221};
4222
Takashi Iwaic5059252008-02-16 09:43:56 +01004223/* mute internal speaker if HP is plugged */
4224static void ad1884a_hp_automute(struct hda_codec *codec)
4225{
4226 unsigned int present;
4227
Takashi Iwaid56757a2009-11-18 08:00:14 +01004228 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01004229 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4230 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4231 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4232 present ? 0x00 : 0x02);
4233}
4234
Takashi Iwai269ef192008-05-30 15:32:15 +02004235/* switch to external mic if plugged */
4236static void ad1884a_hp_automic(struct hda_codec *codec)
4237{
4238 unsigned int present;
4239
Takashi Iwaid56757a2009-11-18 08:00:14 +01004240 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02004241 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
4242 present ? 0 : 1);
4243}
4244
Takashi Iwaic5059252008-02-16 09:43:56 +01004245#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02004246#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01004247
4248/* unsolicited event for HP jack sensing */
4249static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
4250{
Takashi Iwai269ef192008-05-30 15:32:15 +02004251 switch (res >> 26) {
4252 case AD1884A_HP_EVENT:
4253 ad1884a_hp_automute(codec);
4254 break;
4255 case AD1884A_MIC_EVENT:
4256 ad1884a_hp_automic(codec);
4257 break;
4258 }
Takashi Iwaic5059252008-02-16 09:43:56 +01004259}
4260
4261/* initialize jack-sensing, too */
4262static int ad1884a_hp_init(struct hda_codec *codec)
4263{
4264 ad198x_init(codec);
4265 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02004266 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01004267 return 0;
4268}
4269
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004270/* mute internal speaker if HP or docking HP is plugged */
4271static void ad1884a_laptop_automute(struct hda_codec *codec)
4272{
4273 unsigned int present;
4274
Takashi Iwaid56757a2009-11-18 08:00:14 +01004275 present = snd_hda_jack_detect(codec, 0x11);
4276 if (!present)
4277 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004278 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4279 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4280 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4281 present ? 0x00 : 0x02);
4282}
4283
4284/* switch to external mic if plugged */
4285static void ad1884a_laptop_automic(struct hda_codec *codec)
4286{
4287 unsigned int idx;
4288
Takashi Iwaid56757a2009-11-18 08:00:14 +01004289 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004290 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01004291 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004292 idx = 4;
4293 else
4294 idx = 1;
4295 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
4296}
4297
4298/* unsolicited event for HP jack sensing */
4299static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
4300 unsigned int res)
4301{
4302 switch (res >> 26) {
4303 case AD1884A_HP_EVENT:
4304 ad1884a_laptop_automute(codec);
4305 break;
4306 case AD1884A_MIC_EVENT:
4307 ad1884a_laptop_automic(codec);
4308 break;
4309 }
4310}
4311
4312/* initialize jack-sensing, too */
4313static int ad1884a_laptop_init(struct hda_codec *codec)
4314{
4315 ad198x_init(codec);
4316 ad1884a_laptop_automute(codec);
4317 ad1884a_laptop_automic(codec);
4318 return 0;
4319}
4320
Takashi Iwaic5059252008-02-16 09:43:56 +01004321/* additional verbs for laptop model */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004322static const struct hda_verb ad1884a_laptop_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004323 /* Port-A (HP) pin - always unmuted */
4324 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4325 /* Port-F (int speaker) mixer - route only from analog mixer */
4326 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4327 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004328 /* Port-F (int speaker) pin */
4329 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004330 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004331 /* required for compaq 6530s/6531s speaker output */
4332 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004333 /* Port-C pin - internal mic-in */
4334 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4335 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4336 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004337 /* Port-D (docking line-out) pin - default unmuted */
4338 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004339 /* analog mix */
4340 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4341 /* unsolicited event for pin-sense */
4342 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004343 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004344 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004345 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004346 /* allow to touch GPIO1 (for mute control) */
4347 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4348 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4349 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004350 { } /* end */
4351};
4352
Takashi Iwai498f5b12011-05-02 11:33:15 +02004353static const struct hda_verb ad1884a_mobile_verbs[] = {
Takashi Iwai73156132009-04-23 08:24:48 +02004354 /* DACs; unmute as default */
4355 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4356 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4357 /* Port-A (HP) mixer - route only from analog mixer */
4358 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4359 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4360 /* Port-A pin */
4361 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4362 /* Port-A (HP) pin - always unmuted */
4363 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4364 /* Port-B (mic jack) pin */
4365 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4366 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4367 /* Port-C (int mic) pin */
4368 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4369 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4370 /* Port-F (int speaker) mixer - route only from analog mixer */
4371 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4372 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4373 /* Port-F pin */
4374 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4375 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4376 /* Analog mixer; mute as default */
4377 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4378 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4379 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4380 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4381 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4382 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4383 /* Analog Mix output amp */
4384 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4385 /* capture sources */
4386 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4387 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4388 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4389 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4390 /* unsolicited event for pin-sense */
4391 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4392 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02004393 /* allow to touch GPIO1 (for mute control) */
4394 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4395 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4396 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004397 { } /* end */
4398};
4399
Takashi Iwaic5059252008-02-16 09:43:56 +01004400/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004401 * Thinkpad X300
4402 * 0x11 - HP
4403 * 0x12 - speaker
4404 * 0x14 - mic-in
4405 * 0x17 - built-in mic
4406 */
4407
Takashi Iwai498f5b12011-05-02 11:33:15 +02004408static const struct hda_verb ad1984a_thinkpad_verbs[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004409 /* HP unmute */
4410 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4411 /* analog mix */
4412 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4413 /* turn on EAPD */
4414 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4415 /* unsolicited event for pin-sense */
4416 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4417 /* internal mic - dmic */
4418 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004419 /* set magic COEFs for dmic */
4420 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4421 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004422 { } /* end */
4423};
4424
Takashi Iwai498f5b12011-05-02 11:33:15 +02004425static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004426 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4427 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4428 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4429 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4430 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4431 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004432 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4433 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004434 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4435 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4436 {
4437 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4438 .name = "Capture Source",
4439 .info = ad198x_mux_enum_info,
4440 .get = ad198x_mux_enum_get,
4441 .put = ad198x_mux_enum_put,
4442 },
4443 { } /* end */
4444};
4445
Takashi Iwai498f5b12011-05-02 11:33:15 +02004446static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004447 .num_items = 3,
4448 .items = {
4449 { "Mic", 0x0 },
4450 { "Internal Mic", 0x5 },
4451 { "Mix", 0x3 },
4452 },
4453};
4454
4455/* mute internal speaker if HP is plugged */
4456static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4457{
4458 unsigned int present;
4459
Takashi Iwaid56757a2009-11-18 08:00:14 +01004460 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004461 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4462 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4463}
4464
4465/* unsolicited event for HP jack sensing */
4466static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4467 unsigned int res)
4468{
4469 if ((res >> 26) != AD1884A_HP_EVENT)
4470 return;
4471 ad1984a_thinkpad_automute(codec);
4472}
4473
4474/* initialize jack-sensing, too */
4475static int ad1984a_thinkpad_init(struct hda_codec *codec)
4476{
4477 ad198x_init(codec);
4478 ad1984a_thinkpad_automute(codec);
4479 return 0;
4480}
4481
4482/*
David Henningsson677cd902011-02-07 15:19:34 +01004483 * Precision R5500
4484 * 0x12 - HP/line-out
4485 * 0x13 - speaker (mono)
4486 * 0x15 - mic-in
4487 */
4488
Takashi Iwai498f5b12011-05-02 11:33:15 +02004489static const struct hda_verb ad1984a_precision_verbs[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004490 /* Unmute main output path */
4491 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4492 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
4493 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
4494 /* Analog mixer; mute as default */
4495 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4496 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4497 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4498 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4499 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4500 /* Select mic as input */
4501 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
4502 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
4503 /* Configure as mic */
4504 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4505 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4506 /* HP unmute */
4507 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4508 /* turn on EAPD */
4509 {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4510 /* unsolicited event for pin-sense */
4511 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4512 { } /* end */
4513};
4514
Takashi Iwai498f5b12011-05-02 11:33:15 +02004515static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004516 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4517 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4518 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4519 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4520 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4521 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4522 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4523 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4524 HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
4525 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4526 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4527 { } /* end */
4528};
4529
4530
4531/* mute internal speaker if HP is plugged */
4532static void ad1984a_precision_automute(struct hda_codec *codec)
4533{
4534 unsigned int present;
4535
4536 present = snd_hda_jack_detect(codec, 0x12);
4537 snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
4538 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4539}
4540
4541
4542/* unsolicited event for HP jack sensing */
4543static void ad1984a_precision_unsol_event(struct hda_codec *codec,
4544 unsigned int res)
4545{
4546 if ((res >> 26) != AD1884A_HP_EVENT)
4547 return;
4548 ad1984a_precision_automute(codec);
4549}
4550
4551/* initialize jack-sensing, too */
4552static int ad1984a_precision_init(struct hda_codec *codec)
4553{
4554 ad198x_init(codec);
4555 ad1984a_precision_automute(codec);
4556 return 0;
4557}
4558
4559
4560/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004561 * HP Touchsmart
4562 * port-A (0x11) - front hp-out
4563 * port-B (0x14) - unused
4564 * port-C (0x15) - unused
4565 * port-D (0x12) - rear line out
4566 * port-E (0x1c) - front mic-in
4567 * port-F (0x16) - Internal speakers
4568 * digital-mic (0x17) - Internal mic
4569 */
4570
Takashi Iwai498f5b12011-05-02 11:33:15 +02004571static const struct hda_verb ad1984a_touchsmart_verbs[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004572 /* DACs; unmute as default */
4573 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4574 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4575 /* Port-A (HP) mixer - route only from analog mixer */
4576 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4577 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4578 /* Port-A pin */
4579 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4580 /* Port-A (HP) pin - always unmuted */
4581 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4582 /* Port-E (int speaker) mixer - route only from analog mixer */
4583 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4584 /* Port-E pin */
4585 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4586 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4587 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4588 /* Port-F (int speaker) mixer - route only from analog mixer */
4589 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4590 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4591 /* Port-F pin */
4592 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4593 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4594 /* Analog mixer; mute as default */
4595 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4596 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4597 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4598 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4599 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4600 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4601 /* Analog Mix output amp */
4602 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4603 /* capture sources */
4604 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4605 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4606 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4607 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4608 /* unsolicited event for pin-sense */
4609 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4610 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4611 /* allow to touch GPIO1 (for mute control) */
4612 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4613 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4614 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4615 /* internal mic - dmic */
4616 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4617 /* set magic COEFs for dmic */
4618 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4619 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4620 { } /* end */
4621};
4622
Takashi Iwai498f5b12011-05-02 11:33:15 +02004623static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004624 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4625/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4626 {
4627 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004628 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004629 .name = "Master Playback Switch",
4630 .info = snd_hda_mixer_amp_switch_info,
4631 .get = snd_hda_mixer_amp_switch_get,
4632 .put = ad1884a_mobile_master_sw_put,
4633 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4634 },
4635 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4636 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4637 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4638 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004639 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
4640 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004641 { } /* end */
4642};
4643
4644/* switch to external mic if plugged */
4645static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4646{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004647 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004648 snd_hda_codec_write(codec, 0x0c, 0,
4649 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004650 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004651 snd_hda_codec_write(codec, 0x0c, 0,
4652 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004653}
4654
4655
4656/* unsolicited event for HP jack sensing */
4657static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4658 unsigned int res)
4659{
4660 switch (res >> 26) {
4661 case AD1884A_HP_EVENT:
4662 ad1884a_hp_automute(codec);
4663 break;
4664 case AD1884A_MIC_EVENT:
4665 ad1984a_touchsmart_automic(codec);
4666 break;
4667 }
4668}
4669
4670/* initialize jack-sensing, too */
4671static int ad1984a_touchsmart_init(struct hda_codec *codec)
4672{
4673 ad198x_init(codec);
4674 ad1884a_hp_automute(codec);
4675 ad1984a_touchsmart_automic(codec);
4676 return 0;
4677}
4678
4679
4680/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004681 */
4682
4683enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004684 AD1884A_AUTO,
Takashi Iwaic5059252008-02-16 09:43:56 +01004685 AD1884A_DESKTOP,
4686 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004687 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004688 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004689 AD1984A_TOUCHSMART,
David Henningsson677cd902011-02-07 15:19:34 +01004690 AD1984A_PRECISION,
Takashi Iwaic5059252008-02-16 09:43:56 +01004691 AD1884A_MODELS
4692};
4693
Takashi Iwaiea734962011-01-17 11:29:34 +01004694static const char * const ad1884a_models[AD1884A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004695 [AD1884A_AUTO] = "auto",
Takashi Iwaic5059252008-02-16 09:43:56 +01004696 [AD1884A_DESKTOP] = "desktop",
4697 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004698 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004699 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004700 [AD1984A_TOUCHSMART] = "touchsmart",
David Henningsson677cd902011-02-07 15:19:34 +01004701 [AD1984A_PRECISION] = "precision",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004702};
4703
Takashi Iwai498f5b12011-05-02 11:33:15 +02004704static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004705 SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004706 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004707 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004708 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004709 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004710 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004711 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4712 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004713 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004714 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004715 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004716 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004717};
4718
4719static int patch_ad1884a(struct hda_codec *codec)
4720{
4721 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004722 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004723
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004724 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
4725 ad1884a_models,
4726 ad1884a_cfg_tbl);
Takashi Iwai657e1b92013-01-22 18:42:39 +01004727 if (board_config < 0) {
4728 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
4729 codec->chip_name);
4730 board_config = AD1884A_AUTO;
4731 }
4732
4733 if (board_config == AD1884A_AUTO)
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004734 return ad1884_parse_auto_config(codec);
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01004735
Takashi Iwai361dab32012-05-09 14:35:27 +02004736 err = alloc_ad_spec(codec);
4737 if (err < 0)
4738 return err;
4739 spec = codec->spec;
Takashi Iwaic5059252008-02-16 09:43:56 +01004740
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004741 err = snd_hda_attach_beep_device(codec, 0x10);
4742 if (err < 0) {
4743 ad198x_free(codec);
4744 return err;
4745 }
4746 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4747
Takashi Iwaic5059252008-02-16 09:43:56 +01004748 spec->multiout.max_channels = 2;
4749 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4750 spec->multiout.dac_nids = ad1884a_dac_nids;
4751 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4752 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4753 spec->adc_nids = ad1884a_adc_nids;
4754 spec->capsrc_nids = ad1884a_capsrc_nids;
4755 spec->input_mux = &ad1884a_capture_source;
4756 spec->num_mixers = 1;
4757 spec->mixers[0] = ad1884a_base_mixers;
4758 spec->num_init_verbs = 1;
4759 spec->init_verbs[0] = ad1884a_init_verbs;
4760 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02004761#ifdef CONFIG_PM
Takashi Iwaic5059252008-02-16 09:43:56 +01004762 spec->loopback.amplist = ad1884a_loopbacks;
4763#endif
4764 codec->patch_ops = ad198x_patch_ops;
4765
4766 /* override some parameters */
Takashi Iwaic5059252008-02-16 09:43:56 +01004767 switch (board_config) {
4768 case AD1884A_LAPTOP:
4769 spec->mixers[0] = ad1884a_laptop_mixers;
4770 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4771 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004772 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4773 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004774 /* set the upper-limit for mixer amp to 0dB for avoiding the
4775 * possible damage by overloading
4776 */
4777 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4778 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4779 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4780 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4781 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004782 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004783 case AD1884A_MOBILE:
4784 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004785 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004786 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004787 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4788 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004789 /* set the upper-limit for mixer amp to 0dB for avoiding the
4790 * possible damage by overloading
4791 */
4792 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4793 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4794 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4795 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4796 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004797 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004798 case AD1884A_THINKPAD:
4799 spec->mixers[0] = ad1984a_thinkpad_mixers;
4800 spec->init_verbs[spec->num_init_verbs++] =
4801 ad1984a_thinkpad_verbs;
4802 spec->multiout.dig_out_nid = 0;
4803 spec->input_mux = &ad1984a_thinkpad_capture_source;
4804 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4805 codec->patch_ops.init = ad1984a_thinkpad_init;
4806 break;
David Henningsson677cd902011-02-07 15:19:34 +01004807 case AD1984A_PRECISION:
4808 spec->mixers[0] = ad1984a_precision_mixers;
4809 spec->init_verbs[spec->num_init_verbs++] =
4810 ad1984a_precision_verbs;
4811 spec->multiout.dig_out_nid = 0;
4812 codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
4813 codec->patch_ops.init = ad1984a_precision_init;
4814 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004815 case AD1984A_TOUCHSMART:
4816 spec->mixers[0] = ad1984a_touchsmart_mixers;
4817 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4818 spec->multiout.dig_out_nid = 0;
4819 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4820 codec->patch_ops.init = ad1984a_touchsmart_init;
4821 /* set the upper-limit for mixer amp to 0dB for avoiding the
4822 * possible damage by overloading
4823 */
4824 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4825 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4826 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4827 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4828 (1 << AC_AMPCAP_MUTE_SHIFT));
4829 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004830 }
4831
Takashi Iwai729d55b2009-12-25 22:49:01 +01004832 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004833 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004834
Takashi Iwaic5059252008-02-16 09:43:56 +01004835 return 0;
4836}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004837#else /* ENABLE_AD_STATIC_QUIRKS */
4838#define patch_ad1884a ad1884_parse_auto_config
4839#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwaic5059252008-02-16 09:43:56 +01004840
4841
4842/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004843 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004844 *
4845 * port-A - front hp-out
4846 * port-B - front mic-in
4847 * port-C - rear line-in, shared surr-out (3stack)
4848 * port-D - rear line-out
4849 * port-E - rear mic-in, shared clfe-out (3stack)
4850 * port-F - rear surr-out (6stack)
4851 * port-G - rear clfe-out (6stack)
4852 */
4853
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01004854#ifdef ENABLE_AD_STATIC_QUIRKS
Takashi Iwai498f5b12011-05-02 11:33:15 +02004855static const hda_nid_t ad1882_dac_nids[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004856 0x04, 0x03, 0x05
4857};
4858
Takashi Iwai498f5b12011-05-02 11:33:15 +02004859static const hda_nid_t ad1882_adc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004860 0x08, 0x09,
4861};
4862
Takashi Iwai498f5b12011-05-02 11:33:15 +02004863static const hda_nid_t ad1882_capsrc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004864 0x0c, 0x0d,
4865};
4866
4867#define AD1882_SPDIF_OUT 0x02
4868
4869/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004870static const struct hda_input_mux ad1882_capture_source = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004871 .num_items = 5,
4872 .items = {
4873 { "Front Mic", 0x1 },
4874 { "Mic", 0x4 },
4875 { "Line", 0x2 },
4876 { "CD", 0x3 },
4877 { "Mix", 0x7 },
4878 },
4879};
4880
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004881/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004882static const struct hda_input_mux ad1882a_capture_source = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004883 .num_items = 5,
4884 .items = {
4885 { "Front Mic", 0x1 },
4886 { "Mic", 0x4},
4887 { "Line", 0x2 },
4888 { "Digital Mic", 0x06 },
4889 { "Mix", 0x7 },
4890 },
4891};
4892
Takashi Iwai498f5b12011-05-02 11:33:15 +02004893static const struct snd_kcontrol_new ad1882_base_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004894 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4895 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4896 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4897 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4898 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4899 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4900 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4901 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004902
David Henningsson5f99f862011-01-04 15:24:24 +01004903 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
4904 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
4905 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
Takashi Iwai0ac85512007-06-20 15:46:13 +02004906 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4907 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4908 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4909 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4910 {
4911 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4912 /* The multiple "Capture Source" controls confuse alsamixer
4913 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004914 */
4915 /* .name = "Capture Source", */
4916 .name = "Input Source",
4917 .count = 2,
4918 .info = ad198x_mux_enum_info,
4919 .get = ad198x_mux_enum_get,
4920 .put = ad198x_mux_enum_put,
4921 },
4922 /* SPDIF controls */
4923 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4924 {
4925 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4926 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4927 /* identical with ad1983 */
4928 .info = ad1983_spdif_route_info,
4929 .get = ad1983_spdif_route_get,
4930 .put = ad1983_spdif_route_put,
4931 },
4932 { } /* end */
4933};
4934
Takashi Iwai498f5b12011-05-02 11:33:15 +02004935static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004936 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4937 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4938 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4939 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4940 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4941 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4942 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4943 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004944 { } /* end */
4945};
4946
Takashi Iwai498f5b12011-05-02 11:33:15 +02004947static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004948 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4949 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4950 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4951 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4952 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4953 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4954 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4955 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004956 HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004957 { } /* end */
4958};
4959
Takashi Iwai498f5b12011-05-02 11:33:15 +02004960static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004961 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4962 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4963 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4964 {
4965 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4966 .name = "Channel Mode",
4967 .info = ad198x_ch_mode_info,
4968 .get = ad198x_ch_mode_get,
4969 .put = ad198x_ch_mode_put,
4970 },
4971 { } /* end */
4972};
4973
Takashi Iwai1c8684522012-08-13 11:09:35 +02004974/* simple auto-mute control for AD1882 3-stack board */
4975#define AD1882_HP_EVENT 0x01
4976
4977static void ad1882_3stack_automute(struct hda_codec *codec)
4978{
4979 bool mute = snd_hda_jack_detect(codec, 0x11);
4980 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
4981 mute ? 0 : PIN_OUT);
4982}
4983
4984static int ad1882_3stack_automute_init(struct hda_codec *codec)
4985{
4986 ad198x_init(codec);
4987 ad1882_3stack_automute(codec);
4988 return 0;
4989}
4990
4991static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res)
4992{
4993 switch (res >> 26) {
4994 case AD1882_HP_EVENT:
4995 ad1882_3stack_automute(codec);
4996 break;
4997 }
4998}
4999
Takashi Iwai498f5b12011-05-02 11:33:15 +02005000static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005001 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
5002 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
5003 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
5004 { } /* end */
5005};
5006
Takashi Iwai498f5b12011-05-02 11:33:15 +02005007static const struct hda_verb ad1882_ch2_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005008 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
5009 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5010 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5011 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5012 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5013 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5014 { } /* end */
5015};
5016
Takashi Iwai498f5b12011-05-02 11:33:15 +02005017static const struct hda_verb ad1882_ch4_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005018 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5019 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5020 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5021 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5022 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5023 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5024 { } /* end */
5025};
5026
Takashi Iwai498f5b12011-05-02 11:33:15 +02005027static const struct hda_verb ad1882_ch6_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005028 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5029 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5030 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5031 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5032 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5033 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5034 { } /* end */
5035};
5036
Takashi Iwai498f5b12011-05-02 11:33:15 +02005037static const struct hda_channel_mode ad1882_modes[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005038 { 2, ad1882_ch2_init },
5039 { 4, ad1882_ch4_init },
5040 { 6, ad1882_ch6_init },
5041};
5042
5043/*
5044 * initialization verbs
5045 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02005046static const struct hda_verb ad1882_init_verbs[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02005047 /* DACs; mute as default */
5048 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
5049 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
5050 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
5051 /* Port-A (HP) mixer */
5052 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5053 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5054 /* Port-A pin */
5055 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
5056 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5057 /* HP selector - select DAC2 */
5058 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
5059 /* Port-D (Line-out) mixer */
5060 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5061 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5062 /* Port-D pin */
5063 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
5064 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5065 /* Mono-out mixer */
5066 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5067 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5068 /* Mono-out pin */
5069 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
5070 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5071 /* Port-B (front mic) pin */
5072 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5073 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5074 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
5075 /* Port-C (line-in) pin */
5076 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
5077 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5078 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
5079 /* Port-C mixer - mute as input */
5080 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5081 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5082 /* Port-E (mic-in) pin */
5083 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
5084 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5085 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
5086 /* Port-E mixer - mute as input */
5087 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5088 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5089 /* Port-F (surround) */
5090 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5091 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5092 /* Port-G (CLFE) */
5093 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
5094 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
5095 /* Analog mixer; mute as default */
5096 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
5097 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5098 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5099 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5100 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5101 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5102 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
5103 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
5104 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
5105 /* Analog Mix output amp */
5106 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
5107 /* SPDIF output selector */
5108 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
5109 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
5110 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
5111 { } /* end */
5112};
5113
Takashi Iwai1c8684522012-08-13 11:09:35 +02005114static const struct hda_verb ad1882_3stack_automute_verbs[] = {
5115 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT},
5116 { } /* end */
5117};
5118
Takashi Iwai83012a72012-08-24 18:38:08 +02005119#ifdef CONFIG_PM
Takashi Iwai498f5b12011-05-02 11:33:15 +02005120static const struct hda_amp_list ad1882_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02005121 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
5122 { 0x20, HDA_INPUT, 1 }, /* Mic */
5123 { 0x20, HDA_INPUT, 4 }, /* Line */
5124 { 0x20, HDA_INPUT, 6 }, /* CD */
5125 { } /* end */
5126};
5127#endif
5128
Takashi Iwai0ac85512007-06-20 15:46:13 +02005129/* models */
5130enum {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005131 AD1882_AUTO,
Takashi Iwai0ac85512007-06-20 15:46:13 +02005132 AD1882_3STACK,
5133 AD1882_6STACK,
Takashi Iwai1c8684522012-08-13 11:09:35 +02005134 AD1882_3STACK_AUTOMUTE,
Takashi Iwai0ac85512007-06-20 15:46:13 +02005135 AD1882_MODELS
5136};
5137
Takashi Iwaiea734962011-01-17 11:29:34 +01005138static const char * const ad1882_models[AD1986A_MODELS] = {
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005139 [AD1882_AUTO] = "auto",
Takashi Iwai0ac85512007-06-20 15:46:13 +02005140 [AD1882_3STACK] = "3stack",
5141 [AD1882_6STACK] = "6stack",
Takashi Iwai1c8684522012-08-13 11:09:35 +02005142 [AD1882_3STACK_AUTOMUTE] = "3stack-automute",
Takashi Iwai0ac85512007-06-20 15:46:13 +02005143};
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005144#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai0ac85512007-06-20 15:46:13 +02005145
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005146static int ad1882_parse_auto_config(struct hda_codec *codec)
5147{
Takashi Iwai0ac85512007-06-20 15:46:13 +02005148 struct ad198x_spec *spec;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005149 int err;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005150
Takashi Iwai361dab32012-05-09 14:35:27 +02005151 err = alloc_ad_spec(codec);
5152 if (err < 0)
5153 return err;
5154 spec = codec->spec;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005155
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005156 spec->gen.mixer_nid = 0x20;
Takashi Iwaie4a395e2013-01-23 17:00:31 +01005157 spec->gen.mixer_merge_nid = 0x21;
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005158 spec->beep_dev_nid = 0x10;
5159 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
5160 err = ad198x_parse_auto_config(codec);
5161 if (err < 0)
5162 goto error;
5163 err = ad1988_add_spdif_mux_ctl(codec);
5164 if (err < 0)
5165 goto error;
5166 return 0;
5167
5168 error:
5169 ad198x_free(codec);
5170 return err;
5171}
5172
5173#ifdef ENABLE_AD_STATIC_QUIRKS
5174static int patch_ad1882(struct hda_codec *codec)
5175{
5176 struct ad198x_spec *spec;
5177 int err, board_config;
5178
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005179 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
5180 ad1882_models, NULL);
Takashi Iwai657e1b92013-01-22 18:42:39 +01005181 if (board_config < 0) {
5182 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
5183 codec->chip_name);
5184 board_config = AD1882_AUTO;
5185 }
5186
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005187 if (board_config == AD1882_AUTO)
5188 return ad1882_parse_auto_config(codec);
5189
5190 err = alloc_ad_spec(codec);
5191 if (err < 0)
5192 return err;
5193 spec = codec->spec;
Takashi Iwai78bb3cb2012-12-21 15:17:06 +01005194
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01005195 err = snd_hda_attach_beep_device(codec, 0x10);
5196 if (err < 0) {
5197 ad198x_free(codec);
5198 return err;
5199 }
5200 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
5201
Takashi Iwai0ac85512007-06-20 15:46:13 +02005202 spec->multiout.max_channels = 6;
5203 spec->multiout.num_dacs = 3;
5204 spec->multiout.dac_nids = ad1882_dac_nids;
5205 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
5206 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
5207 spec->adc_nids = ad1882_adc_nids;
5208 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005209 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005210 spec->input_mux = &ad1882_capture_source;
5211 else
5212 spec->input_mux = &ad1882a_capture_source;
5213 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005214 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005215 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005216 spec->mixers[1] = ad1882_loopback_mixers;
5217 else
5218 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005219 spec->num_init_verbs = 1;
5220 spec->init_verbs[0] = ad1882_init_verbs;
5221 spec->spdif_route = 0;
Takashi Iwai83012a72012-08-24 18:38:08 +02005222#ifdef CONFIG_PM
Takashi Iwaicb53c622007-08-10 17:21:45 +02005223 spec->loopback.amplist = ad1882_loopbacks;
5224#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01005225 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005226
5227 codec->patch_ops = ad198x_patch_ops;
5228
5229 /* override some parameters */
Takashi Iwai0ac85512007-06-20 15:46:13 +02005230 switch (board_config) {
5231 default:
5232 case AD1882_3STACK:
Takashi Iwai1c8684522012-08-13 11:09:35 +02005233 case AD1882_3STACK_AUTOMUTE:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005234 spec->num_mixers = 3;
5235 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005236 spec->channel_mode = ad1882_modes;
5237 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
5238 spec->need_dac_fix = 1;
5239 spec->multiout.max_channels = 2;
5240 spec->multiout.num_dacs = 1;
Takashi Iwai1c8684522012-08-13 11:09:35 +02005241 if (board_config != AD1882_3STACK) {
5242 spec->init_verbs[spec->num_init_verbs++] =
5243 ad1882_3stack_automute_verbs;
5244 codec->patch_ops.unsol_event = ad1882_3stack_unsol_event;
5245 codec->patch_ops.init = ad1882_3stack_automute_init;
5246 }
Takashi Iwai0ac85512007-06-20 15:46:13 +02005247 break;
5248 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005249 spec->num_mixers = 3;
5250 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005251 break;
5252 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01005253
5254 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02005255 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01005256
Takashi Iwai0ac85512007-06-20 15:46:13 +02005257 return 0;
5258}
Takashi Iwai9ff4bc82013-01-22 16:45:58 +01005259#else /* ENABLE_AD_STATIC_QUIRKS */
5260#define patch_ad1882 ad1882_parse_auto_config
5261#endif /* ENABLE_AD_STATIC_QUIRKS */
Takashi Iwai0ac85512007-06-20 15:46:13 +02005262
5263
5264/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07005265 * patch entries
5266 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02005267static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01005268 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02005269 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005270 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005271 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005272 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
5273 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02005274 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
5275 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005276 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005277 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01005278 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02005279 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005280 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02005281 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
5282 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005283 {} /* terminator */
5284};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01005285
5286MODULE_ALIAS("snd-hda-codec-id:11d4*");
5287
5288MODULE_LICENSE("GPL");
5289MODULE_DESCRIPTION("Analog Devices HD-audio codec");
5290
5291static struct hda_codec_preset_list analog_list = {
5292 .preset = snd_hda_preset_analog,
5293 .owner = THIS_MODULE,
5294};
5295
5296static int __init patch_analog_init(void)
5297{
5298 return snd_hda_add_codec_preset(&analog_list);
5299}
5300
5301static void __exit patch_analog_exit(void)
5302{
5303 snd_hda_delete_codec_preset(&analog_list);
5304}
5305
5306module_init(patch_analog_init)
5307module_exit(patch_analog_exit)