blob: 0f7b8951440f165c1ebd6472ea2ee5f837f7a574 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Takashi Iwai0ac85512007-06-20 15:46:13 +02002 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
3 * AD1986A, AD1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Takashi Iwai2bac6472007-05-18 18:21:41 +02005 * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/pci.h>
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 Iwaic5a4bcd2009-02-06 17:22:05 +010030#include "hda_beep.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020032struct ad198x_spec {
Takashi Iwai498f5b12011-05-02 11:33:15 +020033 const struct snd_kcontrol_new *mixers[6];
Takashi Iwai985be542005-11-02 18:26:49 +010034 int num_mixers;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +010035 unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
Raymond Yau28220842011-02-08 19:58:25 +080036 const struct hda_verb *init_verbs[6]; /* initialization verbs
Takashi Iwai985be542005-11-02 18:26:49 +010037 * don't forget NULL termination!
38 */
39 unsigned int num_init_verbs;
40
41 /* playback */
42 struct hda_multi_out multiout; /* playback set-up
43 * max_channels, dacs must be set
44 * dig_out_nid and hp_nid are optional
45 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +010046 unsigned int cur_eapd;
Takashi Iwai2125cad2006-03-27 12:52:22 +020047 unsigned int need_dac_fix;
Takashi Iwai985be542005-11-02 18:26:49 +010048
Takashi Iwai498f5b12011-05-02 11:33:15 +020049 const hda_nid_t *alt_dac_nid;
50 const struct hda_pcm_stream *stream_analog_alt_playback;
Raymond Yauc66ddf32011-01-17 11:19:03 +010051
Takashi Iwai985be542005-11-02 18:26:49 +010052 /* capture */
53 unsigned int num_adc_nids;
Takashi Iwai498f5b12011-05-02 11:33:15 +020054 const hda_nid_t *adc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010055 hda_nid_t dig_in_nid; /* digital-in NID; optional */
56
57 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020058 const struct hda_input_mux *input_mux;
Takashi Iwai498f5b12011-05-02 11:33:15 +020059 const hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010060 unsigned int cur_mux[3];
61
62 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010063 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010064 int num_channel_mode;
65
66 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020067 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010068
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020069 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010070
71 /* dynamic controls, init_verbs and input_mux */
72 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020073 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010074 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020075 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020076
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010077 unsigned int jack_present: 1;
78 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
79 unsigned int inv_eapd: 1; /* inverted EAPD implementation */
80 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020081
Takashi Iwaicb53c622007-08-10 17:21:45 +020082#ifdef CONFIG_SND_HDA_POWER_SAVE
83 struct hda_loopback_check loopback;
84#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010085 /* for virtual master */
86 hda_nid_t vmaster_nid;
Takashi Iwaiea734962011-01-17 11:29:34 +010087 const char * const *slave_vols;
88 const char * const *slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089};
90
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020091/*
92 * input MUX handling (common part)
93 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010094static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020095{
96 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
97 struct ad198x_spec *spec = codec->spec;
98
99 return snd_hda_input_mux_info(spec->input_mux, uinfo);
100}
101
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100102static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200103{
104 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
105 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100106 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200107
Takashi Iwai985be542005-11-02 18:26:49 +0100108 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200109 return 0;
110}
111
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100112static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200113{
114 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
115 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100116 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200117
118 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100119 spec->capsrc_nids[adc_idx],
120 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200121}
122
123/*
124 * initialization (common callbacks)
125 */
126static int ad198x_init(struct hda_codec *codec)
127{
128 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100129 int i;
130
131 for (i = 0; i < spec->num_init_verbs; i++)
132 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200133 return 0;
134}
135
Takashi Iwaiea734962011-01-17 11:29:34 +0100136static const char * const ad_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100137 "Front Playback Volume",
138 "Surround Playback Volume",
139 "Center Playback Volume",
140 "LFE Playback Volume",
141 "Side Playback Volume",
142 "Headphone Playback Volume",
143 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100144 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100145 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100146 NULL
147};
148
Takashi Iwaiea734962011-01-17 11:29:34 +0100149static const char * const ad_slave_sws[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100150 "Front Playback Switch",
151 "Surround Playback Switch",
152 "Center Playback Switch",
153 "LFE Playback Switch",
154 "Side Playback Switch",
155 "Headphone Playback Switch",
156 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100157 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100158 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100159 NULL
160};
161
Takashi Iwaiea734962011-01-17 11:29:34 +0100162static const char * const ad1988_6stack_fp_slave_vols[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100163 "Front Playback Volume",
164 "Surround Playback Volume",
165 "Center Playback Volume",
166 "LFE Playback Volume",
167 "Side Playback Volume",
168 "IEC958 Playback Volume",
169 NULL
170};
171
Takashi Iwaiea734962011-01-17 11:29:34 +0100172static const char * const ad1988_6stack_fp_slave_sws[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100173 "Front Playback Switch",
174 "Surround Playback Switch",
175 "Center Playback Switch",
176 "LFE Playback Switch",
177 "Side Playback Switch",
178 "IEC958 Playback Switch",
179 NULL
180};
Takashi Iwai603c4012008-07-30 15:01:44 +0200181static void ad198x_free_kctls(struct hda_codec *codec);
182
Takashi Iwai67d634c2009-11-16 15:35:59 +0100183#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100184/* additional beep mixers; the actual parameters are overwritten at build */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200185static const struct snd_kcontrol_new ad_beep_mixer[] = {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100186 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200187 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100188 { } /* end */
189};
190
Takashi Iwai498f5b12011-05-02 11:33:15 +0200191static const struct snd_kcontrol_new ad_beep2_mixer[] = {
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100192 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
193 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
194 { } /* end */
195};
196
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100197#define set_beep_amp(spec, nid, idx, dir) \
198 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100199#else
200#define set_beep_amp(spec, nid, idx, dir) /* NOP */
201#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100202
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200203static int ad198x_build_controls(struct hda_codec *codec)
204{
205 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100206 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100207 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200208 int err;
209
Takashi Iwai985be542005-11-02 18:26:49 +0100210 for (i = 0; i < spec->num_mixers; i++) {
211 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
212 if (err < 0)
213 return err;
214 }
215 if (spec->multiout.dig_out_nid) {
Stephen Warren74b654c2011-06-01 11:14:18 -0600216 err = snd_hda_create_spdif_out_ctls(codec,
217 spec->multiout.dig_out_nid,
218 spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100219 if (err < 0)
220 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100221 err = snd_hda_create_spdif_share_sw(codec,
222 &spec->multiout);
223 if (err < 0)
224 return err;
225 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100226 }
227 if (spec->dig_in_nid) {
228 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
229 if (err < 0)
230 return err;
231 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100232
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100233 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100234#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100235 if (spec->beep_amp) {
Takashi Iwai498f5b12011-05-02 11:33:15 +0200236 const struct snd_kcontrol_new *knew;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100237 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
238 for ( ; knew->name; knew++) {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100239 struct snd_kcontrol *kctl;
240 kctl = snd_ctl_new1(knew, codec);
241 if (!kctl)
242 return -ENOMEM;
243 kctl->private_value = spec->beep_amp;
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100244 err = snd_hda_ctl_add(codec, 0, kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100245 if (err < 0)
246 return err;
247 }
248 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100249#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100250
Takashi Iwai2134ea42008-01-10 16:53:55 +0100251 /* if we have no master control, let's create it */
252 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100253 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100254 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100255 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100256 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100257 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100258 (spec->slave_vols ?
259 spec->slave_vols : ad_slave_vols));
260 if (err < 0)
261 return err;
262 }
263 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
264 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
265 NULL,
266 (spec->slave_sws ?
267 spec->slave_sws : ad_slave_sws));
268 if (err < 0)
269 return err;
270 }
271
Takashi Iwai603c4012008-07-30 15:01:44 +0200272 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100273
274 /* assign Capture Source enums to NID */
275 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
276 if (!kctl)
277 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
278 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100279 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100280 if (err < 0)
281 return err;
282 }
283
284 /* assign IEC958 enums to NID */
285 kctl = snd_hda_find_mixer_ctl(codec,
286 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
287 if (kctl) {
288 err = snd_hda_add_nid(codec, kctl, 0,
289 spec->multiout.dig_out_nid);
290 if (err < 0)
291 return err;
292 }
293
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200294 return 0;
295}
296
Takashi Iwaicb53c622007-08-10 17:21:45 +0200297#ifdef CONFIG_SND_HDA_POWER_SAVE
298static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
299{
300 struct ad198x_spec *spec = codec->spec;
301 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
302}
303#endif
304
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200305/*
306 * Analog playback callbacks
307 */
308static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
309 struct hda_codec *codec,
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;
Takashi Iwai9a081602008-02-12 18:37:26 +0100313 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
314 hinfo);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200315}
316
317static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
318 struct hda_codec *codec,
319 unsigned int stream_tag,
320 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100321 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200322{
323 struct ad198x_spec *spec = codec->spec;
324 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
325 format, substream);
326}
327
328static int ad198x_playback_pcm_cleanup(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_analog_cleanup(codec, &spec->multiout);
334}
335
Takashi Iwai498f5b12011-05-02 11:33:15 +0200336static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100337 .substreams = 1,
338 .channels_min = 2,
339 .channels_max = 2,
340 /* NID is set in ad198x_build_pcms */
Raymond Yauc66ddf32011-01-17 11:19:03 +0100341};
342
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200343/*
344 * Digital out
345 */
346static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
347 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100348 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200349{
350 struct ad198x_spec *spec = codec->spec;
351 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
352}
353
354static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
355 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100356 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200357{
358 struct ad198x_spec *spec = codec->spec;
359 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
360}
361
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200362static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
363 struct hda_codec *codec,
364 unsigned int stream_tag,
365 unsigned int format,
366 struct snd_pcm_substream *substream)
367{
368 struct ad198x_spec *spec = codec->spec;
369 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
370 format, substream);
371}
372
Takashi Iwai9411e212009-02-13 11:32:28 +0100373static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
374 struct hda_codec *codec,
375 struct snd_pcm_substream *substream)
376{
377 struct ad198x_spec *spec = codec->spec;
378 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
379}
380
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200381/*
382 * Analog capture
383 */
384static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
385 struct hda_codec *codec,
386 unsigned int stream_tag,
387 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100388 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200389{
390 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100391 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
392 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200393 return 0;
394}
395
396static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
397 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100398 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200399{
400 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100401 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200402 return 0;
403}
404
405
406/*
407 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200408static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200409 .substreams = 1,
410 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100411 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200412 .nid = 0, /* fill later */
413 .ops = {
414 .open = ad198x_playback_pcm_open,
415 .prepare = ad198x_playback_pcm_prepare,
416 .cleanup = ad198x_playback_pcm_cleanup
417 },
418};
419
Takashi Iwai498f5b12011-05-02 11:33:15 +0200420static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100421 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200422 .channels_min = 2,
423 .channels_max = 2,
424 .nid = 0, /* fill later */
425 .ops = {
426 .prepare = ad198x_capture_pcm_prepare,
427 .cleanup = ad198x_capture_pcm_cleanup
428 },
429};
430
Takashi Iwai498f5b12011-05-02 11:33:15 +0200431static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200432 .substreams = 1,
433 .channels_min = 2,
434 .channels_max = 2,
435 .nid = 0, /* fill later */
436 .ops = {
437 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200438 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100439 .prepare = ad198x_dig_playback_pcm_prepare,
440 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200441 },
442};
443
Takashi Iwai498f5b12011-05-02 11:33:15 +0200444static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100445 .substreams = 1,
446 .channels_min = 2,
447 .channels_max = 2,
448 /* NID is set in alc_build_pcms */
449};
450
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200451static int ad198x_build_pcms(struct hda_codec *codec)
452{
453 struct ad198x_spec *spec = codec->spec;
454 struct hda_pcm *info = spec->pcm_rec;
455
456 codec->num_pcms = 1;
457 codec->pcm_info = info;
458
459 info->name = "AD198x Analog";
460 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
461 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
462 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
463 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100464 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
465 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200466
467 if (spec->multiout.dig_out_nid) {
468 info++;
469 codec->num_pcms++;
470 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100471 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200472 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
473 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100474 if (spec->dig_in_nid) {
475 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
476 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
477 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200478 }
479
Raymond Yauc66ddf32011-01-17 11:19:03 +0100480 if (spec->alt_dac_nid && spec->stream_analog_alt_playback) {
481 codec->num_pcms++;
482 info = spec->pcm_rec + 2;
483 info->name = "AD198x Headphone";
484 info->pcm_type = HDA_PCM_TYPE_AUDIO;
485 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
486 *spec->stream_analog_alt_playback;
487 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
488 spec->alt_dac_nid[0];
489 }
490
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200491 return 0;
492}
493
Takashi Iwai603c4012008-07-30 15:01:44 +0200494static void ad198x_free_kctls(struct hda_codec *codec)
495{
496 struct ad198x_spec *spec = codec->spec;
497
498 if (spec->kctls.list) {
499 struct snd_kcontrol_new *kctl = spec->kctls.list;
500 int i;
501 for (i = 0; i < spec->kctls.used; i++)
502 kfree(kctl[i].name);
503 }
504 snd_array_free(&spec->kctls);
505}
506
Daniel T Chenea52bf22009-12-27 18:48:29 -0500507static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
508 hda_nid_t hp)
509{
510 struct ad198x_spec *spec = codec->spec;
Raymond Yaua01ef052011-06-01 15:09:48 +0800511 if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
512 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500513 !spec->inv_eapd ? 0x00 : 0x02);
Raymond Yaua01ef052011-06-01 15:09:48 +0800514 if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
515 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500516 !spec->inv_eapd ? 0x00 : 0x02);
517}
518
519static void ad198x_power_eapd(struct hda_codec *codec)
520{
521 /* We currently only handle front, HP */
522 switch (codec->vendor_id) {
523 case 0x11d41882:
524 case 0x11d4882a:
525 case 0x11d41884:
526 case 0x11d41984:
527 case 0x11d41883:
528 case 0x11d4184a:
529 case 0x11d4194a:
530 case 0x11d4194b:
Takashi Iwai4dffbe02011-06-03 10:05:02 +0200531 case 0x11d41988:
532 case 0x11d4198b:
533 case 0x11d4989a:
534 case 0x11d4989b:
Daniel T Chenea52bf22009-12-27 18:48:29 -0500535 ad198x_power_eapd_write(codec, 0x12, 0x11);
536 break;
537 case 0x11d41981:
538 case 0x11d41983:
539 ad198x_power_eapd_write(codec, 0x05, 0x06);
540 break;
541 case 0x11d41986:
542 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
543 break;
Daniel T Chenea52bf22009-12-27 18:48:29 -0500544 }
545}
546
Takashi Iwai0da26922011-04-26 15:18:33 +0200547static void ad198x_shutup(struct hda_codec *codec)
548{
549 snd_hda_shutup_pins(codec);
550 ad198x_power_eapd(codec);
551}
552
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200553static void ad198x_free(struct hda_codec *codec)
554{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100555 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100556
Takashi Iwai603c4012008-07-30 15:01:44 +0200557 if (!spec)
558 return;
559
Daniel T Chenea52bf22009-12-27 18:48:29 -0500560 ad198x_shutup(codec);
Takashi Iwai603c4012008-07-30 15:01:44 +0200561 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100562 kfree(spec);
563 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200564}
565
Daniel T Chenea52bf22009-12-27 18:48:29 -0500566#ifdef SND_HDA_NEEDS_RESUME
567static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
568{
569 ad198x_shutup(codec);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500570 return 0;
571}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500572#endif
573
Takashi Iwai498f5b12011-05-02 11:33:15 +0200574static const struct hda_codec_ops ad198x_patch_ops = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200575 .build_controls = ad198x_build_controls,
576 .build_pcms = ad198x_build_pcms,
577 .init = ad198x_init,
578 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200579#ifdef CONFIG_SND_HDA_POWER_SAVE
580 .check_power_status = ad198x_check_power_status,
581#endif
Daniel T Chenea52bf22009-12-27 18:48:29 -0500582#ifdef SND_HDA_NEEDS_RESUME
583 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500584#endif
585 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200586};
587
588
589/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100590 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100591 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100592 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200593#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100594
595static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
596 struct snd_ctl_elem_value *ucontrol)
597{
598 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
599 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100600 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100601 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
602 else
603 ucontrol->value.integer.value[0] = spec->cur_eapd;
604 return 0;
605}
606
607static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
608 struct snd_ctl_elem_value *ucontrol)
609{
610 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
611 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100612 hda_nid_t nid = kcontrol->private_value & 0xff;
613 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100614 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100615 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100616 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200617 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100618 return 0;
619 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200620 snd_hda_codec_write_cache(codec, nid,
621 0, AC_VERB_SET_EAPD_BTLENABLE,
622 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100623 return 1;
624}
625
Takashi Iwai9230d212006-03-13 13:49:49 +0100626static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
627 struct snd_ctl_elem_info *uinfo);
628static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
629 struct snd_ctl_elem_value *ucontrol);
630static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
631 struct snd_ctl_elem_value *ucontrol);
632
633
Takashi Iwai18a815d2006-03-01 19:54:39 +0100634/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200635 * AD1986A specific
636 */
637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638#define AD1986A_SPDIF_OUT 0x02
639#define AD1986A_FRONT_DAC 0x03
640#define AD1986A_SURR_DAC 0x04
641#define AD1986A_CLFE_DAC 0x05
642#define AD1986A_ADC 0x06
643
Takashi Iwai498f5b12011-05-02 11:33:15 +0200644static const hda_nid_t ad1986a_dac_nids[3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
646};
Takashi Iwai498f5b12011-05-02 11:33:15 +0200647static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
648static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
Takashi Iwai498f5b12011-05-02 11:33:15 +0200650static const struct hda_input_mux ad1986a_capture_source = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 .num_items = 7,
652 .items = {
653 { "Mic", 0x0 },
654 { "CD", 0x1 },
655 { "Aux", 0x3 },
656 { "Line", 0x4 },
657 { "Mix", 0x5 },
658 { "Mono", 0x6 },
659 { "Phone", 0x7 },
660 },
661};
662
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Takashi Iwai498f5b12011-05-02 11:33:15 +0200664static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200665 .ops = &snd_hda_bind_vol,
666 .values = {
667 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
668 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
669 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
670 0
671 },
672};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Takashi Iwai498f5b12011-05-02 11:33:15 +0200674static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200675 .ops = &snd_hda_bind_sw,
676 .values = {
677 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
678 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
679 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
680 0
681 },
682};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
684/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 * mixers
686 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200687static const struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200688 /*
689 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
690 */
691 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
692 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
694 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
695 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
696 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
697 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
698 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
699 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
700 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
701 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
702 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
703 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
704 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
705 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
706 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
707 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
708 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
709 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
710 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100711 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
713 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
714 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
715 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
716 {
717 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
718 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200719 .info = ad198x_mux_enum_info,
720 .get = ad198x_mux_enum_get,
721 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 },
723 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
724 { } /* end */
725};
726
Takashi Iwai9230d212006-03-13 13:49:49 +0100727/* additional mixers for 3stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200728static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100729 {
730 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
731 .name = "Channel Mode",
732 .info = ad198x_ch_mode_info,
733 .get = ad198x_ch_mode_get,
734 .put = ad198x_ch_mode_put,
735 },
736 { } /* end */
737};
738
739/* laptop model - 2ch only */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200740static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
Takashi Iwai9230d212006-03-13 13:49:49 +0100741
Takashi Iwai20a45e82007-08-15 22:20:45 +0200742/* master controls both pins 0x1a and 0x1b */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200743static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200744 .ops = &snd_hda_bind_vol,
745 .values = {
746 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
747 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
748 0,
749 },
750};
751
Takashi Iwai498f5b12011-05-02 11:33:15 +0200752static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200753 .ops = &snd_hda_bind_sw,
754 .values = {
755 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
756 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
757 0,
758 },
759};
760
Takashi Iwai498f5b12011-05-02 11:33:15 +0200761static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100762 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
763 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200764 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
765 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100766 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
767 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
768 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
769 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
770 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
771 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
772 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
773 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100774 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100775 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100776 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
777 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
778 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
779 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
780 {
781 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
782 .name = "Capture Source",
783 .info = ad198x_mux_enum_info,
784 .get = ad198x_mux_enum_get,
785 .put = ad198x_mux_enum_put,
786 },
787 { } /* end */
788};
789
Takashi Iwai825aa9722006-03-17 10:50:49 +0100790/* laptop-eapd model - 2ch only */
791
Takashi Iwai498f5b12011-05-02 11:33:15 +0200792static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100793 .num_items = 3,
794 .items = {
795 { "Mic", 0x0 },
796 { "Internal Mic", 0x4 },
797 { "Mix", 0x5 },
798 },
799};
800
Takashi Iwai498f5b12011-05-02 11:33:15 +0200801static const struct hda_input_mux ad1986a_automic_capture_source = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100802 .num_items = 2,
803 .items = {
804 { "Mic", 0x0 },
805 { "Mix", 0x5 },
806 },
807};
808
Takashi Iwai498f5b12011-05-02 11:33:15 +0200809static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200810 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
811 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200812 { } /* end */
813};
814
Takashi Iwai498f5b12011-05-02 11:33:15 +0200815static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100816 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
817 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100818 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
819 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100820 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100821 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
822 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
823 {
824 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
825 .name = "Capture Source",
826 .info = ad198x_mux_enum_info,
827 .get = ad198x_mux_enum_get,
828 .put = ad198x_mux_enum_put,
829 },
830 {
831 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
832 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100833 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100834 .info = ad198x_eapd_info,
835 .get = ad198x_eapd_get,
836 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100837 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100838 },
839 { } /* end */
840};
841
Takashi Iwai498f5b12011-05-02 11:33:15 +0200842static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
Takashi Iwai16d11a82009-06-24 14:07:53 +0200843 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
844 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100845 { } /* end */
846};
847
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100848/* re-connect the mic boost input according to the jack sensing */
849static void ad1986a_automic(struct hda_codec *codec)
850{
851 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100852 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100853 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
854 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100855 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100856}
857
858#define AD1986A_MIC_EVENT 0x36
859
860static void ad1986a_automic_unsol_event(struct hda_codec *codec,
861 unsigned int res)
862{
863 if ((res >> 26) != AD1986A_MIC_EVENT)
864 return;
865 ad1986a_automic(codec);
866}
867
868static int ad1986a_automic_init(struct hda_codec *codec)
869{
870 ad198x_init(codec);
871 ad1986a_automic(codec);
872 return 0;
873}
874
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200875/* laptop-automute - 2ch only */
876
877static void ad1986a_update_hp(struct hda_codec *codec)
878{
879 struct ad198x_spec *spec = codec->spec;
880 unsigned int mute;
881
882 if (spec->jack_present)
883 mute = HDA_AMP_MUTE; /* mute internal speaker */
884 else
885 /* unmute internal speaker if necessary */
886 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
887 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
888 HDA_AMP_MUTE, mute);
889}
890
891static void ad1986a_hp_automute(struct hda_codec *codec)
892{
893 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200894
Takashi Iwaid56757a2009-11-18 08:00:14 +0100895 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +0200896 if (spec->inv_jack_detect)
897 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200898 ad1986a_update_hp(codec);
899}
900
901#define AD1986A_HP_EVENT 0x37
902
903static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
904{
905 if ((res >> 26) != AD1986A_HP_EVENT)
906 return;
907 ad1986a_hp_automute(codec);
908}
909
910static int ad1986a_hp_init(struct hda_codec *codec)
911{
912 ad198x_init(codec);
913 ad1986a_hp_automute(codec);
914 return 0;
915}
916
917/* bind hp and internal speaker mute (with plug check) */
918static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
919 struct snd_ctl_elem_value *ucontrol)
920{
921 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
922 long *valp = ucontrol->value.integer.value;
923 int change;
924
925 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
926 HDA_AMP_MUTE,
927 valp[0] ? 0 : HDA_AMP_MUTE);
928 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
929 HDA_AMP_MUTE,
930 valp[1] ? 0 : HDA_AMP_MUTE);
931 if (change)
932 ad1986a_update_hp(codec);
933 return change;
934}
935
Takashi Iwai498f5b12011-05-02 11:33:15 +0200936static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200937 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
938 {
939 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
940 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100941 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200942 .info = snd_hda_mixer_amp_switch_info,
943 .get = snd_hda_mixer_amp_switch_get,
944 .put = ad1986a_hp_master_sw_put,
945 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
946 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200947 { } /* end */
948};
949
Takashi Iwai16d11a82009-06-24 14:07:53 +0200950
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951/*
952 * initialization verbs
953 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200954static const struct hda_verb ad1986a_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 /* Front, Surround, CLFE DAC; mute as default */
956 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
957 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
958 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
959 /* Downmix - off */
960 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
961 /* HP, Line-Out, Surround, CLFE selectors */
962 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
963 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
964 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
965 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
966 /* Mono selector */
967 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
968 /* Mic selector: Mic 1/2 pin */
969 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
970 /* Line-in selector: Line-in */
971 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
972 /* Mic 1/2 swap */
973 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
974 /* Record selector: mic */
975 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
976 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
977 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
978 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
979 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
980 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
981 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
982 /* PC beep */
983 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
984 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
985 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
986 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
987 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
988 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
989 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200990 /* HP Pin */
991 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
992 /* Front, Surround, CLFE Pins */
993 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
994 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
995 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
996 /* Mono Pin */
997 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
998 /* Mic Pin */
999 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1000 /* Line, Aux, CD, Beep-In Pin */
1001 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1002 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1003 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1004 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1005 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 { } /* end */
1007};
1008
Takashi Iwai498f5b12011-05-02 11:33:15 +02001009static const struct hda_verb ad1986a_ch2_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001010 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001011 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1012 /* Line-in selectors */
1013 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001014 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001015 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1016 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
1017 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001018 { } /* end */
1019};
1020
Takashi Iwai498f5b12011-05-02 11:33:15 +02001021static const struct hda_verb ad1986a_ch4_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001022 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001023 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1024 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
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 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001028 { } /* end */
1029};
1030
Takashi Iwai498f5b12011-05-02 11:33:15 +02001031static const struct hda_verb ad1986a_ch6_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001032 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001033 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1034 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001035 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001036 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1037 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001038 { } /* end */
1039};
1040
Takashi Iwai498f5b12011-05-02 11:33:15 +02001041static const struct hda_channel_mode ad1986a_modes[3] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001042 { 2, ad1986a_ch2_init },
1043 { 4, ad1986a_ch4_init },
1044 { 6, ad1986a_ch6_init },
1045};
1046
Takashi Iwai825aa9722006-03-17 10:50:49 +01001047/* eapd initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001048static const struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001049 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +01001050 {}
1051};
1052
Takashi Iwai498f5b12011-05-02 11:33:15 +02001053static const struct hda_verb ad1986a_automic_verbs[] = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001054 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1055 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1056 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1057 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1058 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1059 {}
1060};
1061
Tobin Davisf36090f2007-01-08 11:07:12 +01001062/* Ultra initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001063static const struct hda_verb ad1986a_ultra_init[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001064 /* eapd initialization */
1065 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1066 /* CLFE -> Mic in */
1067 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1068 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1069 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1070 { } /* end */
1071};
1072
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001073/* pin sensing on HP jack */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001074static const struct hda_verb ad1986a_hp_init_verbs[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001075 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1076 {}
1077};
1078
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001079static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1080 unsigned int res)
1081{
1082 switch (res >> 26) {
1083 case AD1986A_HP_EVENT:
1084 ad1986a_hp_automute(codec);
1085 break;
1086 case AD1986A_MIC_EVENT:
1087 ad1986a_automic(codec);
1088 break;
1089 }
1090}
1091
1092static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1093{
1094 ad198x_init(codec);
1095 ad1986a_hp_automute(codec);
1096 ad1986a_automic(codec);
1097 return 0;
1098}
1099
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001100
Takashi Iwai9230d212006-03-13 13:49:49 +01001101/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001102enum {
1103 AD1986A_6STACK,
1104 AD1986A_3STACK,
1105 AD1986A_LAPTOP,
1106 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001107 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001108 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001109 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001110 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001111 AD1986A_MODELS
1112};
Takashi Iwai9230d212006-03-13 13:49:49 +01001113
Takashi Iwaiea734962011-01-17 11:29:34 +01001114static const char * const ad1986a_models[AD1986A_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001115 [AD1986A_6STACK] = "6stack",
1116 [AD1986A_3STACK] = "3stack",
1117 [AD1986A_LAPTOP] = "laptop",
1118 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001119 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001120 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001121 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001122 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001123};
1124
Takashi Iwai498f5b12011-05-02 11:33:15 +02001125static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001126 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001127 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001128 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001129 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001130 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1131 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1132 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1133 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001134 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001135 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001136 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1137 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1138 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1139 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1140 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001141 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001142 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001143 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001144 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001145 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001146 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001147 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001148 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001149 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001150 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001151 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001152 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001153 {}
1154};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155
Takashi Iwaicb53c622007-08-10 17:21:45 +02001156#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001157static const struct hda_amp_list ad1986a_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001158 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1159 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1160 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1161 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1162 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1163 { } /* end */
1164};
1165#endif
1166
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001167static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1168{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001169 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001170 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1171}
1172
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173static int patch_ad1986a(struct hda_codec *codec)
1174{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001175 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001176 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001178 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 if (spec == NULL)
1180 return -ENOMEM;
1181
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 codec->spec = spec;
1183
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001184 err = snd_hda_attach_beep_device(codec, 0x19);
1185 if (err < 0) {
1186 ad198x_free(codec);
1187 return err;
1188 }
1189 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1190
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 spec->multiout.max_channels = 6;
1192 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1193 spec->multiout.dac_nids = ad1986a_dac_nids;
1194 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001195 spec->num_adc_nids = 1;
1196 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001197 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001198 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001199 spec->num_mixers = 1;
1200 spec->mixers[0] = ad1986a_mixers;
1201 spec->num_init_verbs = 1;
1202 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001203#ifdef CONFIG_SND_HDA_POWER_SAVE
1204 spec->loopback.amplist = ad1986a_loopbacks;
1205#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001206 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001207 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001209 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
Takashi Iwai9230d212006-03-13 13:49:49 +01001211 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001212 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1213 ad1986a_models,
1214 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001215 switch (board_config) {
1216 case AD1986A_3STACK:
1217 spec->num_mixers = 2;
1218 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001219 spec->num_init_verbs = 2;
1220 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001221 spec->channel_mode = ad1986a_modes;
1222 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001223 spec->need_dac_fix = 1;
1224 spec->multiout.max_channels = 2;
1225 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001226 break;
1227 case AD1986A_LAPTOP:
1228 spec->mixers[0] = ad1986a_laptop_mixers;
1229 spec->multiout.max_channels = 2;
1230 spec->multiout.num_dacs = 1;
1231 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1232 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001233 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001234 spec->num_mixers = 3;
1235 spec->mixers[0] = ad1986a_laptop_master_mixers;
1236 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1237 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001238 spec->num_init_verbs = 2;
1239 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1240 spec->multiout.max_channels = 2;
1241 spec->multiout.num_dacs = 1;
1242 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1243 if (!is_jack_available(codec, 0x25))
1244 spec->multiout.dig_out_nid = 0;
1245 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1246 break;
1247 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001248 spec->num_mixers = 2;
1249 spec->mixers[0] = ad1986a_laptop_master_mixers;
1250 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001251 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001252 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001253 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001254 spec->multiout.max_channels = 2;
1255 spec->multiout.num_dacs = 1;
1256 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001257 if (!is_jack_available(codec, 0x25))
1258 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001259 spec->input_mux = &ad1986a_automic_capture_source;
1260 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1261 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001262 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001263 case AD1986A_SAMSUNG_P50:
1264 spec->num_mixers = 2;
1265 spec->mixers[0] = ad1986a_automute_master_mixers;
1266 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1267 spec->num_init_verbs = 4;
1268 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1269 spec->init_verbs[2] = ad1986a_automic_verbs;
1270 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1271 spec->multiout.max_channels = 2;
1272 spec->multiout.num_dacs = 1;
1273 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1274 if (!is_jack_available(codec, 0x25))
1275 spec->multiout.dig_out_nid = 0;
1276 spec->input_mux = &ad1986a_automic_capture_source;
1277 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1278 codec->patch_ops.init = ad1986a_samsung_p50_init;
1279 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001280 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001281 spec->num_mixers = 3;
1282 spec->mixers[0] = ad1986a_automute_master_mixers;
1283 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1284 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001285 spec->num_init_verbs = 3;
1286 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1287 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1288 spec->multiout.max_channels = 2;
1289 spec->multiout.num_dacs = 1;
1290 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001291 if (!is_jack_available(codec, 0x25))
1292 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001293 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1294 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1295 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001296 /* Lenovo N100 seems to report the reversed bit
1297 * for HP jack-sensing
1298 */
1299 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001300 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001301 case AD1986A_ULTRA:
1302 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1303 spec->num_init_verbs = 2;
1304 spec->init_verbs[1] = ad1986a_ultra_init;
1305 spec->multiout.max_channels = 2;
1306 spec->multiout.num_dacs = 1;
1307 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1308 spec->multiout.dig_out_nid = 0;
1309 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001310 }
1311
Takashi Iwaid29240c2007-10-26 12:35:56 +02001312 /* AD1986A has a hardware problem that it can't share a stream
1313 * with multiple output pins. The copy of front to surrounds
1314 * causes noisy or silent outputs at a certain timing, e.g.
1315 * changing the volume.
1316 * So, let's disable the shared stream.
1317 */
1318 spec->multiout.no_share_stream = 1;
1319
Takashi Iwai729d55b2009-12-25 22:49:01 +01001320 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001321 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 return 0;
1324}
1325
1326/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001327 * AD1983 specific
1328 */
1329
1330#define AD1983_SPDIF_OUT 0x02
1331#define AD1983_DAC 0x03
1332#define AD1983_ADC 0x04
1333
Takashi Iwai498f5b12011-05-02 11:33:15 +02001334static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
1335static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
1336static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001337
Takashi Iwai498f5b12011-05-02 11:33:15 +02001338static const struct hda_input_mux ad1983_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001339 .num_items = 4,
1340 .items = {
1341 { "Mic", 0x0 },
1342 { "Line", 0x1 },
1343 { "Mix", 0x2 },
1344 { "Mix Mono", 0x3 },
1345 },
1346};
1347
1348/*
1349 * SPDIF playback route
1350 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001351static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001352{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001353 static const char * const texts[] = { "PCM", "ADC" };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001354
1355 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1356 uinfo->count = 1;
1357 uinfo->value.enumerated.items = 2;
1358 if (uinfo->value.enumerated.item > 1)
1359 uinfo->value.enumerated.item = 1;
1360 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1361 return 0;
1362}
1363
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001364static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001365{
1366 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1367 struct ad198x_spec *spec = codec->spec;
1368
1369 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1370 return 0;
1371}
1372
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001373static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001374{
1375 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1376 struct ad198x_spec *spec = codec->spec;
1377
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001378 if (ucontrol->value.enumerated.item[0] > 1)
1379 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001380 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1381 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001382 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1383 AC_VERB_SET_CONNECT_SEL,
1384 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001385 return 1;
1386 }
1387 return 0;
1388}
1389
Takashi Iwai498f5b12011-05-02 11:33:15 +02001390static const struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001391 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1392 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1393 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1394 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1395 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1396 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1397 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1398 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1399 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1400 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1401 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1402 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001403 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001404 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1405 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1406 {
1407 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1408 .name = "Capture Source",
1409 .info = ad198x_mux_enum_info,
1410 .get = ad198x_mux_enum_get,
1411 .put = ad198x_mux_enum_put,
1412 },
1413 {
1414 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001415 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001416 .info = ad1983_spdif_route_info,
1417 .get = ad1983_spdif_route_get,
1418 .put = ad1983_spdif_route_put,
1419 },
1420 { } /* end */
1421};
1422
Takashi Iwai498f5b12011-05-02 11:33:15 +02001423static const struct hda_verb ad1983_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001424 /* Front, HP, Mono; mute as default */
1425 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1426 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1427 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1428 /* Beep, PCM, Mic, Line-In: mute */
1429 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1430 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1431 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1432 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1433 /* Front, HP selectors; from Mix */
1434 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1435 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1436 /* Mono selector; from Mix */
1437 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1438 /* Mic selector; Mic */
1439 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1440 /* Line-in selector: Line-in */
1441 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1442 /* Mic boost: 0dB */
1443 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1444 /* Record selector: mic */
1445 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1446 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1447 /* SPDIF route: PCM */
1448 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1449 /* Front Pin */
1450 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1451 /* HP Pin */
1452 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1453 /* Mono Pin */
1454 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1455 /* Mic Pin */
1456 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1457 /* Line Pin */
1458 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1459 { } /* end */
1460};
1461
Takashi Iwaicb53c622007-08-10 17:21:45 +02001462#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001463static const struct hda_amp_list ad1983_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001464 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1465 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1466 { } /* end */
1467};
1468#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001469
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001470static int patch_ad1983(struct hda_codec *codec)
1471{
1472 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001473 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001474
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001475 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001476 if (spec == NULL)
1477 return -ENOMEM;
1478
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001479 codec->spec = spec;
1480
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001481 err = snd_hda_attach_beep_device(codec, 0x10);
1482 if (err < 0) {
1483 ad198x_free(codec);
1484 return err;
1485 }
1486 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1487
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001488 spec->multiout.max_channels = 2;
1489 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1490 spec->multiout.dac_nids = ad1983_dac_nids;
1491 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001492 spec->num_adc_nids = 1;
1493 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001494 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001495 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001496 spec->num_mixers = 1;
1497 spec->mixers[0] = ad1983_mixers;
1498 spec->num_init_verbs = 1;
1499 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001500 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001501#ifdef CONFIG_SND_HDA_POWER_SAVE
1502 spec->loopback.amplist = ad1983_loopbacks;
1503#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001504 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001505
1506 codec->patch_ops = ad198x_patch_ops;
1507
Takashi Iwai729d55b2009-12-25 22:49:01 +01001508 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001509 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001510
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001511 return 0;
1512}
1513
1514
1515/*
1516 * AD1981 HD specific
1517 */
1518
1519#define AD1981_SPDIF_OUT 0x02
1520#define AD1981_DAC 0x03
1521#define AD1981_ADC 0x04
1522
Takashi Iwai498f5b12011-05-02 11:33:15 +02001523static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
1524static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
1525static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001526
1527/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001528static const struct hda_input_mux ad1981_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001529 .num_items = 7,
1530 .items = {
1531 { "Front Mic", 0x0 },
1532 { "Line", 0x1 },
1533 { "Mix", 0x2 },
1534 { "Mix Mono", 0x3 },
1535 { "CD", 0x4 },
1536 { "Mic", 0x6 },
1537 { "Aux", 0x7 },
1538 },
1539};
1540
Takashi Iwai498f5b12011-05-02 11:33:15 +02001541static const struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001542 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1543 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1544 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1545 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1546 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1547 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1548 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1549 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1550 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1551 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1552 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1553 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1554 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1555 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1556 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1557 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1558 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1559 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001560 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1561 HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001562 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1563 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1564 {
1565 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1566 .name = "Capture Source",
1567 .info = ad198x_mux_enum_info,
1568 .get = ad198x_mux_enum_get,
1569 .put = ad198x_mux_enum_put,
1570 },
1571 /* identical with AD1983 */
1572 {
1573 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001574 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001575 .info = ad1983_spdif_route_info,
1576 .get = ad1983_spdif_route_get,
1577 .put = ad1983_spdif_route_put,
1578 },
1579 { } /* end */
1580};
1581
Takashi Iwai498f5b12011-05-02 11:33:15 +02001582static const struct hda_verb ad1981_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001583 /* Front, HP, Mono; mute as default */
1584 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1585 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1586 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1587 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1588 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1589 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1590 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1591 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1592 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1593 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1594 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1595 /* Front, HP selectors; from Mix */
1596 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1597 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1598 /* Mono selector; from Mix */
1599 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1600 /* Mic Mixer; select Front Mic */
1601 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1602 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1603 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001604 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1605 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001606 /* Record selector: Front mic */
1607 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1608 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1609 /* SPDIF route: PCM */
1610 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1611 /* Front Pin */
1612 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1613 /* HP Pin */
1614 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1615 /* Mono Pin */
1616 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1617 /* Front & Rear Mic Pins */
1618 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1619 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1620 /* Line Pin */
1621 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1622 /* Digital Beep */
1623 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1624 /* Line-Out as Input: disabled */
1625 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1626 { } /* end */
1627};
1628
Takashi Iwaicb53c622007-08-10 17:21:45 +02001629#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001630static const struct hda_amp_list ad1981_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001631 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1632 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1633 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1634 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1635 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1636 { } /* end */
1637};
1638#endif
1639
Takashi Iwai18a815d2006-03-01 19:54:39 +01001640/*
1641 * Patch for HP nx6320
1642 *
Tobin Davis18768992007-03-12 22:20:51 +01001643 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001644 * speaker output enabled _and_ mute-LED off.
1645 */
1646
1647#define AD1981_HP_EVENT 0x37
1648#define AD1981_MIC_EVENT 0x38
1649
Takashi Iwai498f5b12011-05-02 11:33:15 +02001650static const struct hda_verb ad1981_hp_init_verbs[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001651 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1652 /* pin sensing on HP and Mic jacks */
1653 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1654 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1655 {}
1656};
1657
1658/* turn on/off EAPD (+ mute HP) as a master switch */
1659static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1660 struct snd_ctl_elem_value *ucontrol)
1661{
1662 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1663 struct ad198x_spec *spec = codec->spec;
1664
1665 if (! ad198x_eapd_put(kcontrol, ucontrol))
1666 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001667 /* change speaker pin appropriately */
1668 snd_hda_codec_write(codec, 0x05, 0,
1669 AC_VERB_SET_PIN_WIDGET_CONTROL,
1670 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001671 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001672 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1673 HDA_AMP_MUTE,
1674 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001675 return 1;
1676}
1677
1678/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001679static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001680 .ops = &snd_hda_bind_vol,
1681 .values = {
1682 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1683 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1684 0
1685 },
1686};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001687
1688/* mute internal speaker if HP is plugged */
1689static void ad1981_hp_automute(struct hda_codec *codec)
1690{
1691 unsigned int present;
1692
Takashi Iwaid56757a2009-11-18 08:00:14 +01001693 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001694 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1695 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001696}
1697
1698/* toggle input of built-in and mic jack appropriately */
1699static void ad1981_hp_automic(struct hda_codec *codec)
1700{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001701 static const struct hda_verb mic_jack_on[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001702 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1703 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1704 {}
1705 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02001706 static const struct hda_verb mic_jack_off[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001707 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1708 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1709 {}
1710 };
1711 unsigned int present;
1712
Takashi Iwaid56757a2009-11-18 08:00:14 +01001713 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001714 if (present)
1715 snd_hda_sequence_write(codec, mic_jack_on);
1716 else
1717 snd_hda_sequence_write(codec, mic_jack_off);
1718}
1719
1720/* unsolicited event for HP jack sensing */
1721static void ad1981_hp_unsol_event(struct hda_codec *codec,
1722 unsigned int res)
1723{
1724 res >>= 26;
1725 switch (res) {
1726 case AD1981_HP_EVENT:
1727 ad1981_hp_automute(codec);
1728 break;
1729 case AD1981_MIC_EVENT:
1730 ad1981_hp_automic(codec);
1731 break;
1732 }
1733}
1734
Takashi Iwai498f5b12011-05-02 11:33:15 +02001735static const struct hda_input_mux ad1981_hp_capture_source = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001736 .num_items = 3,
1737 .items = {
1738 { "Mic", 0x0 },
1739 { "Docking-Station", 0x1 },
1740 { "Mix", 0x2 },
1741 },
1742};
1743
Takashi Iwai498f5b12011-05-02 11:33:15 +02001744static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001745 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001746 {
1747 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001748 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001749 .name = "Master Playback Switch",
1750 .info = ad198x_eapd_info,
1751 .get = ad198x_eapd_get,
1752 .put = ad1981_hp_master_sw_put,
1753 .private_value = 0x05,
1754 },
1755 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1756 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1757#if 0
1758 /* FIXME: analog mic/line loopback doesn't work with my tests...
1759 * (although recording is OK)
1760 */
1761 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1762 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1763 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1764 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1765 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1766 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1767 /* FIXME: does this laptop have analog CD connection? */
1768 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1769 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1770#endif
David Henningsson5f99f862011-01-04 15:24:24 +01001771 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1772 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001773 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1774 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1775 {
1776 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1777 .name = "Capture Source",
1778 .info = ad198x_mux_enum_info,
1779 .get = ad198x_mux_enum_get,
1780 .put = ad198x_mux_enum_put,
1781 },
1782 { } /* end */
1783};
1784
1785/* initialize jack-sensing, too */
1786static int ad1981_hp_init(struct hda_codec *codec)
1787{
1788 ad198x_init(codec);
1789 ad1981_hp_automute(codec);
1790 ad1981_hp_automic(codec);
1791 return 0;
1792}
1793
Tobin Davis18768992007-03-12 22:20:51 +01001794/* configuration for Toshiba Laptops */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001795static const struct hda_verb ad1981_toshiba_init_verbs[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001796 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1797 /* pin sensing on HP and Mic jacks */
1798 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1799 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1800 {}
1801};
1802
Takashi Iwai498f5b12011-05-02 11:33:15 +02001803static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001804 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1805 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1806 { }
1807};
1808
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001809/* configuration for Lenovo Thinkpad T60 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001810static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001811 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1812 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1813 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1814 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1815 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1816 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1817 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1818 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001819 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001820 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1821 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1822 {
1823 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1824 .name = "Capture Source",
1825 .info = ad198x_mux_enum_info,
1826 .get = ad198x_mux_enum_get,
1827 .put = ad198x_mux_enum_put,
1828 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001829 /* identical with AD1983 */
1830 {
1831 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1832 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1833 .info = ad1983_spdif_route_info,
1834 .get = ad1983_spdif_route_get,
1835 .put = ad1983_spdif_route_put,
1836 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001837 { } /* end */
1838};
1839
Takashi Iwai498f5b12011-05-02 11:33:15 +02001840static const struct hda_input_mux ad1981_thinkpad_capture_source = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001841 .num_items = 3,
1842 .items = {
1843 { "Mic", 0x0 },
1844 { "Mix", 0x2 },
1845 { "CD", 0x4 },
1846 },
1847};
1848
Takashi Iwai18a815d2006-03-01 19:54:39 +01001849/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001850enum {
1851 AD1981_BASIC,
1852 AD1981_HP,
1853 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001854 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001855 AD1981_MODELS
1856};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001857
Takashi Iwaiea734962011-01-17 11:29:34 +01001858static const char * const ad1981_models[AD1981_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001859 [AD1981_HP] = "hp",
1860 [AD1981_THINKPAD] = "thinkpad",
1861 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001862 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001863};
1864
Takashi Iwai498f5b12011-05-02 11:33:15 +02001865static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001866 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001867 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001868 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001869 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001870 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001871 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001872 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001873 /* HP nx6320 (reversed SSID, H/W bug) */
1874 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001875 {}
1876};
1877
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001878static int patch_ad1981(struct hda_codec *codec)
1879{
1880 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001881 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001882
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001883 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001884 if (spec == NULL)
1885 return -ENOMEM;
1886
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001887 codec->spec = spec;
1888
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001889 err = snd_hda_attach_beep_device(codec, 0x10);
1890 if (err < 0) {
1891 ad198x_free(codec);
1892 return err;
1893 }
1894 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
1895
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001896 spec->multiout.max_channels = 2;
1897 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
1898 spec->multiout.dac_nids = ad1981_dac_nids;
1899 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001900 spec->num_adc_nids = 1;
1901 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001902 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001903 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001904 spec->num_mixers = 1;
1905 spec->mixers[0] = ad1981_mixers;
1906 spec->num_init_verbs = 1;
1907 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001908 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001909#ifdef CONFIG_SND_HDA_POWER_SAVE
1910 spec->loopback.amplist = ad1981_loopbacks;
1911#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001912 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001913
1914 codec->patch_ops = ad198x_patch_ops;
1915
Takashi Iwai18a815d2006-03-01 19:54:39 +01001916 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001917 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
1918 ad1981_models,
1919 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001920 switch (board_config) {
1921 case AD1981_HP:
1922 spec->mixers[0] = ad1981_hp_mixers;
1923 spec->num_init_verbs = 2;
1924 spec->init_verbs[1] = ad1981_hp_init_verbs;
1925 spec->multiout.dig_out_nid = 0;
1926 spec->input_mux = &ad1981_hp_capture_source;
1927
1928 codec->patch_ops.init = ad1981_hp_init;
1929 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05001930 /* set the upper-limit for mixer amp to 0dB for avoiding the
1931 * possible damage by overloading
1932 */
1933 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1934 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1935 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1936 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1937 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01001938 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001939 case AD1981_THINKPAD:
1940 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001941 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04001942 /* set the upper-limit for mixer amp to 0dB for avoiding the
1943 * possible damage by overloading
1944 */
1945 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
1946 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
1947 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
1948 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
1949 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001950 break;
Tobin Davis18768992007-03-12 22:20:51 +01001951 case AD1981_TOSHIBA:
1952 spec->mixers[0] = ad1981_hp_mixers;
1953 spec->mixers[1] = ad1981_toshiba_mixers;
1954 spec->num_init_verbs = 2;
1955 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
1956 spec->multiout.dig_out_nid = 0;
1957 spec->input_mux = &ad1981_hp_capture_source;
1958 codec->patch_ops.init = ad1981_hp_init;
1959 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
1960 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001961 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01001962
1963 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001964 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001965
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001966 return 0;
1967}
1968
1969
1970/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001971 * AD1988
1972 *
1973 * Output pins and routes
1974 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001975 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01001976 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
1977 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
1978 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
1979 * port-D 0x12 (mute/hp) <- 0x29 <- 04
1980 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
1981 * port-F 0x16 (mute) <- 0x2a <- 06
1982 * port-G 0x24 (mute) <- 0x27 <- 05
1983 * port-H 0x25 (mute) <- 0x28 <- 0a
1984 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
1985 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01001986 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
1987 * (*) 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 +01001988 *
1989 * Input pins and routes
1990 *
1991 * pin boost mix input # / adc input #
1992 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
1993 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
1994 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
1995 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
1996 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
1997 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
1998 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
1999 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
2000 *
2001 *
2002 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01002003 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002004 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002005 *
2006 * Inputs of Analog Mix (0x20)
2007 * 0:Port-B (front mic)
2008 * 1:Port-C/G/H (line-in)
2009 * 2:Port-A
2010 * 3:Port-D (line-in/2)
2011 * 4:Port-E/G/H (mic-in)
2012 * 5:Port-F (mic2-in)
2013 * 6:CD
2014 * 7:Beep
2015 *
2016 * ADC selection
2017 * 0:Port-A
2018 * 1:Port-B (front mic-in)
2019 * 2:Port-C (line-in)
2020 * 3:Port-F (mic2-in)
2021 * 4:Port-E (mic-in)
2022 * 5:CD
2023 * 6:Port-G
2024 * 7:Port-H
2025 * 8:Port-D (line-in/2)
2026 * 9:Mix
2027 *
2028 * Proposed pin assignments by the datasheet
2029 *
2030 * 6-stack
2031 * Port-A front headphone
2032 * B front mic-in
2033 * C rear line-in
2034 * D rear front-out
2035 * E rear mic-in
2036 * F rear surround
2037 * G rear CLFE
2038 * H rear side
2039 *
2040 * 3-stack
2041 * Port-A front headphone
2042 * B front mic
2043 * C rear line-in/surround
2044 * D rear front-out
2045 * E rear mic-in/CLFE
2046 *
2047 * laptop
2048 * Port-A headphone
2049 * B mic-in
2050 * C docking station
2051 * D internal speaker (with EAPD)
2052 * E/F quad mic array
2053 */
2054
2055
2056/* models */
2057enum {
2058 AD1988_6STACK,
2059 AD1988_6STACK_DIG,
Raymond Yauc66ddf32011-01-17 11:19:03 +01002060 AD1988_6STACK_DIG_FP,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002061 AD1988_3STACK,
2062 AD1988_3STACK_DIG,
2063 AD1988_LAPTOP,
2064 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002065 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002066 AD1988_MODEL_LAST,
2067};
2068
Takashi Iwaid32410b12005-11-24 16:06:23 +01002069/* reivision id to check workarounds */
2070#define AD1988A_REV2 0x100200
2071
Takashi Iwai1a806f42006-07-03 15:58:16 +02002072#define is_rev2(codec) \
2073 ((codec)->vendor_id == 0x11d41988 && \
2074 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002075
2076/*
2077 * mixers
2078 */
2079
Takashi Iwai498f5b12011-05-02 11:33:15 +02002080static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002081 0x04, 0x06, 0x05, 0x0a
2082};
2083
Takashi Iwai498f5b12011-05-02 11:33:15 +02002084static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002085 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002086};
2087
2088/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002089static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002090 0x04, 0x05, 0x0a, 0x06
2091};
2092
Takashi Iwai498f5b12011-05-02 11:33:15 +02002093static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002094 0x03
2095};
2096
Takashi Iwai498f5b12011-05-02 11:33:15 +02002097static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002098 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002099};
2100
Takashi Iwai498f5b12011-05-02 11:33:15 +02002101static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002102 0x08, 0x09, 0x0f
2103};
2104
Takashi Iwai498f5b12011-05-02 11:33:15 +02002105static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002106 0x0c, 0x0d, 0x0e
2107};
2108
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002109#define AD1988_SPDIF_OUT 0x02
2110#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002111#define AD1988_SPDIF_IN 0x07
2112
Takashi Iwai498f5b12011-05-02 11:33:15 +02002113static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01002114 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002115};
2116
Takashi Iwai498f5b12011-05-02 11:33:15 +02002117static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002118 .num_items = 5,
2119 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002120 { "Front Mic", 0x1 }, /* port-B */
2121 { "Line", 0x2 }, /* port-C */
2122 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002123 { "CD", 0x5 },
2124 { "Mix", 0x9 },
2125 },
2126};
2127
Takashi Iwai498f5b12011-05-02 11:33:15 +02002128static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002129 .num_items = 3,
2130 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002131 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002132 { "CD", 0x5 },
2133 { "Mix", 0x9 },
2134 },
2135};
2136
2137/*
2138 */
2139static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2140 struct snd_ctl_elem_info *uinfo)
2141{
2142 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2143 struct ad198x_spec *spec = codec->spec;
2144 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2145 spec->num_channel_mode);
2146}
2147
2148static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2149 struct snd_ctl_elem_value *ucontrol)
2150{
2151 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2152 struct ad198x_spec *spec = codec->spec;
2153 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2154 spec->num_channel_mode, spec->multiout.max_channels);
2155}
2156
2157static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2158 struct snd_ctl_elem_value *ucontrol)
2159{
2160 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2161 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002162 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2163 spec->num_channel_mode,
2164 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002165 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002166 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002167 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002168}
2169
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002170/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002171static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002172 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2173 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2174 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2175 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2176 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002177 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002178};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002179
Takashi Iwai498f5b12011-05-02 11:33:15 +02002180static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002181 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2182 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2183 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2184 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2185 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002186 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002187};
2188
Takashi Iwai498f5b12011-05-02 11:33:15 +02002189static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002190 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2191 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2192 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2193 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2194 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2195 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2196 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2197
2198 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2199 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2200 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2201 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2202 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2203 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2204 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2205 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2206
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002207 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002208 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2209
David Henningsson5f99f862011-01-04 15:24:24 +01002210 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2211 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002212
2213 { } /* end */
2214};
2215
Takashi Iwai498f5b12011-05-02 11:33:15 +02002216static const struct snd_kcontrol_new ad1988_6stack_fp_mixers[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002217 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
2218
Raymond Yauc66ddf32011-01-17 11:19:03 +01002219 { } /* end */
2220};
2221
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002222/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002223static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002224 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002225 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002226 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2227 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002228 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002229};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002230
Takashi Iwai498f5b12011-05-02 11:33:15 +02002231static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002232 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002233 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2234 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2235 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002236 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002237};
2238
Takashi Iwai498f5b12011-05-02 11:33:15 +02002239static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002240 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002241 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2242 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2243 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002244 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2245 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2246
2247 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2248 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2249 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2250 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2251 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2252 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2253 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2254 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2255
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002256 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002257 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2258
David Henningsson5f99f862011-01-04 15:24:24 +01002259 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2260 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002261 {
2262 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2263 .name = "Channel Mode",
2264 .info = ad198x_ch_mode_info,
2265 .get = ad198x_ch_mode_get,
2266 .put = ad198x_ch_mode_put,
2267 },
2268
2269 { } /* end */
2270};
2271
2272/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002273static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002274 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2275 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2276 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2277
2278 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2279 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2280 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2281 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2282 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2283 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2284
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002285 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002286 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2287
David Henningsson5f99f862011-01-04 15:24:24 +01002288 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002289
2290 {
2291 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2292 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002293 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002294 .info = ad198x_eapd_info,
2295 .get = ad198x_eapd_get,
2296 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002297 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002298 },
2299
2300 { } /* end */
2301};
2302
2303/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002304static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002305 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2306 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2307 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2308 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2309 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2310 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2311 {
2312 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2313 /* The multiple "Capture Source" controls confuse alsamixer
2314 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002315 */
2316 /* .name = "Capture Source", */
2317 .name = "Input Source",
2318 .count = 3,
2319 .info = ad198x_mux_enum_info,
2320 .get = ad198x_mux_enum_get,
2321 .put = ad198x_mux_enum_put,
2322 },
2323 { } /* end */
2324};
2325
2326static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2327 struct snd_ctl_elem_info *uinfo)
2328{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002329 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002330 "PCM", "ADC1", "ADC2", "ADC3"
2331 };
2332 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2333 uinfo->count = 1;
2334 uinfo->value.enumerated.items = 4;
2335 if (uinfo->value.enumerated.item >= 4)
2336 uinfo->value.enumerated.item = 3;
2337 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2338 return 0;
2339}
2340
2341static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2342 struct snd_ctl_elem_value *ucontrol)
2343{
2344 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2345 unsigned int sel;
2346
Takashi Iwaibddcf542007-07-24 18:04:05 +02002347 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2348 AC_AMP_GET_INPUT);
2349 if (!(sel & 0x80))
2350 ucontrol->value.enumerated.item[0] = 0;
2351 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002352 sel = snd_hda_codec_read(codec, 0x0b, 0,
2353 AC_VERB_GET_CONNECT_SEL, 0);
2354 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002355 sel++;
2356 else
2357 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002358 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002359 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002360 return 0;
2361}
2362
2363static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2364 struct snd_ctl_elem_value *ucontrol)
2365{
2366 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002367 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002368 int change;
2369
Takashi Iwai35b26722007-05-05 12:17:17 +02002370 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002371 if (val > 3)
2372 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002373 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002374 sel = snd_hda_codec_read(codec, 0x1d, 0,
2375 AC_VERB_GET_AMP_GAIN_MUTE,
2376 AC_AMP_GET_INPUT);
2377 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002378 if (change) {
2379 snd_hda_codec_write_cache(codec, 0x1d, 0,
2380 AC_VERB_SET_AMP_GAIN_MUTE,
2381 AMP_IN_UNMUTE(0));
2382 snd_hda_codec_write_cache(codec, 0x1d, 0,
2383 AC_VERB_SET_AMP_GAIN_MUTE,
2384 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002385 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002386 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002387 sel = snd_hda_codec_read(codec, 0x1d, 0,
2388 AC_VERB_GET_AMP_GAIN_MUTE,
2389 AC_AMP_GET_INPUT | 0x01);
2390 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002391 if (change) {
2392 snd_hda_codec_write_cache(codec, 0x1d, 0,
2393 AC_VERB_SET_AMP_GAIN_MUTE,
2394 AMP_IN_MUTE(0));
2395 snd_hda_codec_write_cache(codec, 0x1d, 0,
2396 AC_VERB_SET_AMP_GAIN_MUTE,
2397 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002398 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002399 sel = snd_hda_codec_read(codec, 0x0b, 0,
2400 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2401 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002402 if (change)
2403 snd_hda_codec_write_cache(codec, 0x0b, 0,
2404 AC_VERB_SET_CONNECT_SEL,
2405 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002406 }
2407 return change;
2408}
2409
Takashi Iwai498f5b12011-05-02 11:33:15 +02002410static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002411 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2412 {
2413 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2414 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002415 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002416 .info = ad1988_spdif_playback_source_info,
2417 .get = ad1988_spdif_playback_source_get,
2418 .put = ad1988_spdif_playback_source_put,
2419 },
2420 { } /* end */
2421};
2422
Takashi Iwai498f5b12011-05-02 11:33:15 +02002423static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002424 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2425 { } /* end */
2426};
2427
Takashi Iwai498f5b12011-05-02 11:33:15 +02002428static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002429 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002430 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002431 { } /* end */
2432};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002433
2434/*
2435 * initialization verbs
2436 */
2437
2438/*
2439 * for 6-stack (+dig)
2440 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002441static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002442 /* Front, Surround, CLFE, side DAC; unmute as default */
2443 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2444 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2445 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2446 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002447 /* Port-A front headphon path */
2448 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2449 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2450 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2451 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2452 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2453 /* Port-D line-out path */
2454 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2455 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2456 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2457 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2458 /* Port-F surround path */
2459 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2460 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2461 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2462 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2463 /* Port-G CLFE path */
2464 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2465 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2466 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2467 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2468 /* Port-H side path */
2469 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2470 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2471 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2472 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2473 /* Mono out path */
2474 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2475 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2476 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2477 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2478 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2479 /* Port-B front mic-in path */
2480 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2481 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2482 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2483 /* Port-C line-in path */
2484 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2485 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2486 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2487 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2488 /* Port-E mic-in path */
2489 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2490 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2491 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2492 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002493 /* Analog CD Input */
2494 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002495 /* Analog Mix output amp */
2496 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002497
2498 { }
2499};
2500
Takashi Iwai498f5b12011-05-02 11:33:15 +02002501static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002502 /* Headphone; unmute as default */
2503 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2504 /* Port-A front headphon path */
2505 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2506 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2507 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2508 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2509 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002510
2511 { }
2512};
2513
Takashi Iwai498f5b12011-05-02 11:33:15 +02002514static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002515 /* mute analog mix */
2516 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2517 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2518 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2519 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2520 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2521 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2522 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2523 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2524 /* select ADCs - front-mic */
2525 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2526 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2527 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002528
2529 { }
2530};
2531
Takashi Iwai498f5b12011-05-02 11:33:15 +02002532static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002533 /* SPDIF out sel */
2534 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2535 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2536 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002537 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002538 /* SPDIF out pin */
2539 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002540
2541 { }
2542};
2543
Takashi Iwai498f5b12011-05-02 11:33:15 +02002544static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002545 /* unmute SPDIF input pin */
2546 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2547 { }
2548};
2549
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002550/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002551static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002552 /* SPDIF-1 out pin */
2553 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002554 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002555 /* SPDIF-2/HDMI out pin */
2556 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2557 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002558 { }
2559};
2560
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002561/*
2562 * verbs for 3stack (+dig)
2563 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002564static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002565 /* set port-C to line-in */
2566 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2567 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2568 /* set port-E to mic-in */
2569 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2570 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2571 { } /* end */
2572};
2573
Takashi Iwai498f5b12011-05-02 11:33:15 +02002574static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002575 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002576 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002577 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002578 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002579 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002580 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002581 { } /* end */
2582};
2583
Takashi Iwai498f5b12011-05-02 11:33:15 +02002584static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002585 { 2, ad1988_3stack_ch2_init },
2586 { 6, ad1988_3stack_ch6_init },
2587};
2588
Takashi Iwai498f5b12011-05-02 11:33:15 +02002589static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002590 /* Front, Surround, CLFE, side DAC; unmute as default */
2591 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2592 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2593 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2594 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002595 /* Port-A front headphon path */
2596 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2597 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2598 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2599 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2600 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2601 /* Port-D line-out path */
2602 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2603 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2604 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2605 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2606 /* Mono out path */
2607 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2608 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2609 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2610 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2611 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2612 /* Port-B front mic-in path */
2613 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2614 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2615 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002616 /* Port-C line-in/surround path - 6ch mode as default */
2617 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2618 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002619 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002620 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002621 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002622 /* Port-E mic-in/CLFE path - 6ch mode as default */
2623 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2624 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002625 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002626 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002627 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2628 /* mute analog mix */
2629 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2630 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2631 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2632 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2633 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2634 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2635 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2636 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2637 /* select ADCs - front-mic */
2638 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2639 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2640 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002641 /* Analog Mix output amp */
2642 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002643 { }
2644};
2645
2646/*
2647 * verbs for laptop mode (+dig)
2648 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002649static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002650 /* unmute port-A and mute port-D */
2651 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2652 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2653 { } /* end */
2654};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002655static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002656 /* mute port-A and unmute port-D */
2657 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2658 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2659 { } /* end */
2660};
2661
2662#define AD1988_HP_EVENT 0x01
2663
Takashi Iwai498f5b12011-05-02 11:33:15 +02002664static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002665 /* Front, Surround, CLFE, side DAC; unmute as default */
2666 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2667 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2668 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2669 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002670 /* Port-A front headphon path */
2671 {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
2672 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2673 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2674 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2675 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2676 /* unsolicited event for pin-sense */
2677 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2678 /* Port-D line-out path + EAPD */
2679 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2680 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2681 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2682 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2683 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2684 /* Mono out path */
2685 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2686 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2687 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2688 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2689 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2690 /* Port-B mic-in path */
2691 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2692 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2693 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2694 /* Port-C docking station - try to output */
2695 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2696 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2697 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2698 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2699 /* mute analog mix */
2700 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2701 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2702 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2703 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2704 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2705 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2706 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2707 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2708 /* select ADCs - mic */
2709 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2710 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2711 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002712 /* Analog Mix output amp */
2713 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002714 { }
2715};
2716
2717static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2718{
2719 if ((res >> 26) != AD1988_HP_EVENT)
2720 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002721 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002722 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2723 else
2724 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2725}
2726
Takashi Iwaicb53c622007-08-10 17:21:45 +02002727#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02002728static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002729 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2730 { 0x20, HDA_INPUT, 1 }, /* Line */
2731 { 0x20, HDA_INPUT, 4 }, /* Mic */
2732 { 0x20, HDA_INPUT, 6 }, /* CD */
2733 { } /* end */
2734};
2735#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002736
2737/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002738 * Automatic parse of I/O pins from the BIOS configuration
2739 */
2740
Takashi Iwaid32410b12005-11-24 16:06:23 +01002741enum {
2742 AD_CTL_WIDGET_VOL,
2743 AD_CTL_WIDGET_MUTE,
2744 AD_CTL_BIND_MUTE,
2745};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002746static const struct snd_kcontrol_new ad1988_control_templates[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002747 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2748 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2749 HDA_BIND_MUTE(NULL, 0, 0, 0),
2750};
2751
2752/* add dynamic controls */
2753static int add_control(struct ad198x_spec *spec, int type, const char *name,
2754 unsigned long val)
2755{
2756 struct snd_kcontrol_new *knew;
2757
Takashi Iwai603c4012008-07-30 15:01:44 +02002758 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2759 knew = snd_array_new(&spec->kctls);
2760 if (!knew)
2761 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002762 *knew = ad1988_control_templates[type];
2763 knew->name = kstrdup(name, GFP_KERNEL);
2764 if (! knew->name)
2765 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002766 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002767 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002768 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002769 return 0;
2770}
2771
2772#define AD1988_PIN_CD_NID 0x18
2773#define AD1988_PIN_BEEP_NID 0x10
2774
Takashi Iwai498f5b12011-05-02 11:33:15 +02002775static const hda_nid_t ad1988_mixer_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002776 /* A B C D E F G H */
2777 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2778};
2779
2780static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2781{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002782 static const hda_nid_t idx_to_dac[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002783 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002784 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002785 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02002786 static const hda_nid_t idx_to_dac_rev2[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002787 /* A B C D E F G H */
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002788 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002789 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002790 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002791 return idx_to_dac_rev2[idx];
2792 else
2793 return idx_to_dac[idx];
2794}
2795
Takashi Iwai498f5b12011-05-02 11:33:15 +02002796static const hda_nid_t ad1988_boost_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002797 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2798};
2799
2800static int ad1988_pin_idx(hda_nid_t nid)
2801{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002802 static const hda_nid_t ad1988_io_pins[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002803 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2804 };
2805 int i;
2806 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2807 if (ad1988_io_pins[i] == nid)
2808 return i;
2809 return 0; /* should be -1 */
2810}
2811
2812static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2813{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002814 static const int loopback_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002815 2, 0, 1, 3, 4, 5, 1, 4
2816 };
2817 switch (nid) {
2818 case AD1988_PIN_CD_NID:
2819 return 6;
2820 default:
2821 return loopback_idx[ad1988_pin_idx(nid)];
2822 }
2823}
2824
2825static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2826{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002827 static const int adc_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002828 0, 1, 2, 8, 4, 3, 6, 7
2829 };
2830 switch (nid) {
2831 case AD1988_PIN_CD_NID:
2832 return 5;
2833 default:
2834 return adc_idx[ad1988_pin_idx(nid)];
2835 }
2836}
2837
2838/* fill in the dac_nids table from the parsed pin configuration */
2839static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2840 const struct auto_pin_cfg *cfg)
2841{
2842 struct ad198x_spec *spec = codec->spec;
2843 int i, idx;
2844
2845 spec->multiout.dac_nids = spec->private_dac_nids;
2846
2847 /* check the pins hardwired to audio widget */
2848 for (i = 0; i < cfg->line_outs; i++) {
2849 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
Takashi Iwaidda14412011-05-02 11:29:30 +02002850 spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002851 }
2852 spec->multiout.num_dacs = cfg->line_outs;
2853 return 0;
2854}
2855
2856/* add playback controls from the parsed DAC table */
2857static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2858 const struct auto_pin_cfg *cfg)
2859{
2860 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01002861 static const char * const chname[4] = {
2862 "Front", "Surround", NULL /*CLFE*/, "Side"
2863 };
Takashi Iwaid32410b12005-11-24 16:06:23 +01002864 hda_nid_t nid;
2865 int i, err;
2866
2867 for (i = 0; i < cfg->line_outs; i++) {
2868 hda_nid_t dac = spec->multiout.dac_nids[i];
2869 if (! dac)
2870 continue;
2871 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2872 if (i == 2) {
2873 /* Center/LFE */
2874 err = add_control(spec, AD_CTL_WIDGET_VOL,
2875 "Center Playback Volume",
2876 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2877 if (err < 0)
2878 return err;
2879 err = add_control(spec, AD_CTL_WIDGET_VOL,
2880 "LFE Playback Volume",
2881 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2882 if (err < 0)
2883 return err;
2884 err = add_control(spec, AD_CTL_BIND_MUTE,
2885 "Center Playback Switch",
2886 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2887 if (err < 0)
2888 return err;
2889 err = add_control(spec, AD_CTL_BIND_MUTE,
2890 "LFE Playback Switch",
2891 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
2892 if (err < 0)
2893 return err;
2894 } else {
2895 sprintf(name, "%s Playback Volume", chname[i]);
2896 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2897 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
2898 if (err < 0)
2899 return err;
2900 sprintf(name, "%s Playback Switch", chname[i]);
2901 err = add_control(spec, AD_CTL_BIND_MUTE, name,
2902 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
2903 if (err < 0)
2904 return err;
2905 }
2906 }
2907 return 0;
2908}
2909
2910/* add playback controls for speaker and HP outputs */
2911static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
2912 const char *pfx)
2913{
2914 struct ad198x_spec *spec = codec->spec;
2915 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02002916 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002917 char name[32];
2918
2919 if (! pin)
2920 return 0;
2921
2922 idx = ad1988_pin_idx(pin);
2923 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02002924 /* check whether the corresponding DAC was already taken */
2925 for (i = 0; i < spec->autocfg.line_outs; i++) {
2926 hda_nid_t pin = spec->autocfg.line_out_pins[i];
2927 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
2928 if (dac == nid)
2929 break;
2930 }
2931 if (i >= spec->autocfg.line_outs) {
2932 /* specify the DAC as the extra output */
2933 if (!spec->multiout.hp_nid)
2934 spec->multiout.hp_nid = nid;
2935 else
2936 spec->multiout.extra_out_nid[0] = nid;
2937 /* control HP volume/switch on the output mixer amp */
2938 sprintf(name, "%s Playback Volume", pfx);
2939 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2940 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
2941 if (err < 0)
2942 return err;
2943 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01002944 nid = ad1988_mixer_nids[idx];
2945 sprintf(name, "%s Playback Switch", pfx);
2946 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
2947 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
2948 return err;
2949 return 0;
2950}
2951
2952/* create input playback/capture controls for the given pin */
2953static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
Takashi Iwai9e042e72010-08-30 13:04:44 +02002954 const char *ctlname, int ctlidx, int boost)
Takashi Iwaid32410b12005-11-24 16:06:23 +01002955{
2956 char name[32];
2957 int err, idx;
2958
2959 sprintf(name, "%s Playback Volume", ctlname);
2960 idx = ad1988_pin_to_loopback_idx(pin);
2961 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
2962 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2963 return err;
2964 sprintf(name, "%s Playback Switch", ctlname);
2965 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
2966 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
2967 return err;
2968 if (boost) {
2969 hda_nid_t bnid;
2970 idx = ad1988_pin_idx(pin);
2971 bnid = ad1988_boost_nids[idx];
2972 if (bnid) {
David Henningsson5f99f862011-01-04 15:24:24 +01002973 sprintf(name, "%s Boost Volume", ctlname);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002974 return add_control(spec, AD_CTL_WIDGET_VOL, name,
2975 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
2976
2977 }
2978 }
2979 return 0;
2980}
2981
2982/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02002983static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002984 const struct auto_pin_cfg *cfg)
2985{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002986 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002987 struct hda_input_mux *imux = &spec->private_imux;
Takashi Iwai10a20af2010-09-09 16:28:02 +02002988 int i, err, type, type_idx;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002989
Takashi Iwai9e042e72010-08-30 13:04:44 +02002990 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02002991 const char *label;
Takashi Iwai9e042e72010-08-30 13:04:44 +02002992 type = cfg->inputs[i].type;
Takashi Iwai10a20af2010-09-09 16:28:02 +02002993 label = hda_get_autocfg_input_label(codec, cfg, i);
2994 snd_hda_add_imux_item(imux, label,
2995 ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
2996 &type_idx);
Takashi Iwai9e042e72010-08-30 13:04:44 +02002997 err = new_analog_input(spec, cfg->inputs[i].pin,
Takashi Iwai10a20af2010-09-09 16:28:02 +02002998 label, type_idx,
Takashi Iwai86e29592010-09-09 14:50:17 +02002999 type == AUTO_PIN_MIC);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003000 if (err < 0)
3001 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003002 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02003003 snd_hda_add_imux_item(imux, "Mix", 9, NULL);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003004
3005 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
3006 "Analog Mix Playback Volume",
3007 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3008 return err;
3009 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
3010 "Analog Mix Playback Switch",
3011 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3012 return err;
3013
3014 return 0;
3015}
3016
3017static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
3018 hda_nid_t nid, int pin_type,
3019 int dac_idx)
3020{
3021 /* set as output */
3022 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
3023 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
3024 switch (nid) {
3025 case 0x11: /* port-A - DAC 04 */
3026 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3027 break;
3028 case 0x14: /* port-B - DAC 06 */
3029 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
3030 break;
3031 case 0x15: /* port-C - DAC 05 */
3032 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
3033 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003034 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01003035 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3036 break;
3037 case 0x13: /* mono - DAC 04 */
3038 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3039 break;
3040 }
3041}
3042
3043static void ad1988_auto_init_multi_out(struct hda_codec *codec)
3044{
3045 struct ad198x_spec *spec = codec->spec;
3046 int i;
3047
3048 for (i = 0; i < spec->autocfg.line_outs; i++) {
3049 hda_nid_t nid = spec->autocfg.line_out_pins[i];
3050 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
3051 }
3052}
3053
3054static void ad1988_auto_init_extra_out(struct hda_codec *codec)
3055{
3056 struct ad198x_spec *spec = codec->spec;
3057 hda_nid_t pin;
3058
Takashi Iwai82bc9552006-03-21 11:24:42 +01003059 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003060 if (pin) /* connect to front */
3061 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003062 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003063 if (pin) /* connect to front */
3064 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
3065}
3066
3067static void ad1988_auto_init_analog_input(struct hda_codec *codec)
3068{
3069 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9e042e72010-08-30 13:04:44 +02003070 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003071 int i, idx;
3072
Takashi Iwai9e042e72010-08-30 13:04:44 +02003073 for (i = 0; i < cfg->num_inputs; i++) {
3074 hda_nid_t nid = cfg->inputs[i].pin;
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003075 int type = cfg->inputs[i].type;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003076 switch (nid) {
3077 case 0x15: /* port-C */
3078 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3079 break;
3080 case 0x17: /* port-E */
3081 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3082 break;
3083 }
3084 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003085 type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003086 if (nid != AD1988_PIN_CD_NID)
3087 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3088 AMP_OUT_MUTE);
3089 idx = ad1988_pin_idx(nid);
3090 if (ad1988_boost_nids[idx])
3091 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3092 AC_VERB_SET_AMP_GAIN_MUTE,
3093 AMP_OUT_ZERO);
3094 }
3095}
3096
3097/* parse the BIOS configuration and set up the alc_spec */
3098/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3099static int ad1988_parse_auto_config(struct hda_codec *codec)
3100{
3101 struct ad198x_spec *spec = codec->spec;
3102 int err;
3103
Kailang Yangdf694da2005-12-05 19:42:22 +01003104 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003105 return err;
3106 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3107 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003108 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003109 return 0; /* can't find valid BIOS pin config */
3110 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003111 (err = ad1988_auto_create_extra_out(codec,
3112 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003113 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003114 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003115 "Headphone")) < 0 ||
Takashi Iwai10a20af2010-09-09 16:28:02 +02003116 (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003117 return err;
3118
3119 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3120
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003121 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003122 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3123 if (spec->autocfg.dig_in_pin)
3124 spec->dig_in_nid = AD1988_SPDIF_IN;
3125
Takashi Iwai603c4012008-07-30 15:01:44 +02003126 if (spec->kctls.list)
3127 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003128
3129 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3130
3131 spec->input_mux = &spec->private_imux;
3132
3133 return 1;
3134}
3135
3136/* init callback for auto-configuration model -- overriding the default init */
3137static int ad1988_auto_init(struct hda_codec *codec)
3138{
3139 ad198x_init(codec);
3140 ad1988_auto_init_multi_out(codec);
3141 ad1988_auto_init_extra_out(codec);
3142 ad1988_auto_init_analog_input(codec);
3143 return 0;
3144}
3145
Takashi Iwaid32410b12005-11-24 16:06:23 +01003146/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003147 */
3148
Takashi Iwaiea734962011-01-17 11:29:34 +01003149static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003150 [AD1988_6STACK] = "6stack",
3151 [AD1988_6STACK_DIG] = "6stack-dig",
Raymond Yauc66ddf32011-01-17 11:19:03 +01003152 [AD1988_6STACK_DIG_FP] = "6stack-dig-fp",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003153 [AD1988_3STACK] = "3stack",
3154 [AD1988_3STACK_DIG] = "3stack-dig",
3155 [AD1988_LAPTOP] = "laptop",
3156 [AD1988_LAPTOP_DIG] = "laptop-dig",
3157 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003158};
3159
Takashi Iwai498f5b12011-05-02 11:33:15 +02003160static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003161 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003162 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003163 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01003164 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003165 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003166 {}
3167};
3168
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003169static int patch_ad1988(struct hda_codec *codec)
3170{
3171 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003172 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003173
3174 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3175 if (spec == NULL)
3176 return -ENOMEM;
3177
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003178 codec->spec = spec;
3179
Takashi Iwai1a806f42006-07-03 15:58:16 +02003180 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003181 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3182
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003183 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003184 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003185 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003186 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3187 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003188 board_config = AD1988_AUTO;
3189 }
3190
3191 if (board_config == AD1988_AUTO) {
3192 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003193 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003194 if (err < 0) {
3195 ad198x_free(codec);
3196 return err;
3197 } else if (! err) {
3198 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3199 board_config = AD1988_6STACK;
3200 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003201 }
3202
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003203 err = snd_hda_attach_beep_device(codec, 0x10);
3204 if (err < 0) {
3205 ad198x_free(codec);
3206 return err;
3207 }
3208 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3209
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003210 switch (board_config) {
3211 case AD1988_6STACK:
3212 case AD1988_6STACK_DIG:
Raymond Yauc66ddf32011-01-17 11:19:03 +01003213 case AD1988_6STACK_DIG_FP:
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003214 spec->multiout.max_channels = 8;
3215 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003216 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003217 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3218 else
3219 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003220 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003221 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003222 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003223 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3224 else
3225 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08003226 spec->mixers[1] = ad1988_6stack_mixers2;
3227 spec->num_init_verbs = 1;
3228 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yauc66ddf32011-01-17 11:19:03 +01003229 if (board_config == AD1988_6STACK_DIG_FP) {
Raymond Yau28220842011-02-08 19:58:25 +08003230 spec->num_mixers++;
3231 spec->mixers[2] = ad1988_6stack_fp_mixers;
3232 spec->num_init_verbs++;
3233 spec->init_verbs[1] = ad1988_6stack_fp_init_verbs;
Raymond Yauc66ddf32011-01-17 11:19:03 +01003234 spec->slave_vols = ad1988_6stack_fp_slave_vols;
3235 spec->slave_sws = ad1988_6stack_fp_slave_sws;
3236 spec->alt_dac_nid = ad1988_alt_dac_nid;
3237 spec->stream_analog_alt_playback =
3238 &ad198x_pcm_analog_alt_playback;
Raymond Yau28220842011-02-08 19:58:25 +08003239 }
Raymond Yauc66ddf32011-01-17 11:19:03 +01003240 if ((board_config == AD1988_6STACK_DIG) ||
3241 (board_config == AD1988_6STACK_DIG_FP)) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003242 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3243 spec->dig_in_nid = AD1988_SPDIF_IN;
3244 }
3245 break;
3246 case AD1988_3STACK:
3247 case AD1988_3STACK_DIG:
3248 spec->multiout.max_channels = 6;
3249 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003250 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003251 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3252 else
3253 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003254 spec->input_mux = &ad1988_6stack_capture_source;
3255 spec->channel_mode = ad1988_3stack_modes;
3256 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003257 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003258 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003259 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3260 else
3261 spec->mixers[0] = ad1988_3stack_mixers1;
3262 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003263 spec->num_init_verbs = 1;
3264 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3265 if (board_config == AD1988_3STACK_DIG)
3266 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3267 break;
3268 case AD1988_LAPTOP:
3269 case AD1988_LAPTOP_DIG:
3270 spec->multiout.max_channels = 2;
3271 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003272 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003273 spec->input_mux = &ad1988_laptop_capture_source;
3274 spec->num_mixers = 1;
3275 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003276 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003277 spec->num_init_verbs = 1;
3278 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3279 if (board_config == AD1988_LAPTOP_DIG)
3280 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3281 break;
3282 }
3283
Takashi Iwaid32410b12005-11-24 16:06:23 +01003284 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3285 spec->adc_nids = ad1988_adc_nids;
3286 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003287 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3288 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3289 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003290 if (codec->vendor_id >= 0x11d4989a) {
3291 spec->mixers[spec->num_mixers++] =
3292 ad1989_spdif_out_mixers;
3293 spec->init_verbs[spec->num_init_verbs++] =
3294 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003295 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003296 } else {
3297 spec->mixers[spec->num_mixers++] =
3298 ad1988_spdif_out_mixers;
3299 spec->init_verbs[spec->num_init_verbs++] =
3300 ad1988_spdif_init_verbs;
3301 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003302 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003303 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003304 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003305 spec->init_verbs[spec->num_init_verbs++] =
3306 ad1988_spdif_in_init_verbs;
3307 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003308
3309 codec->patch_ops = ad198x_patch_ops;
3310 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003311 case AD1988_AUTO:
3312 codec->patch_ops.init = ad1988_auto_init;
3313 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003314 case AD1988_LAPTOP:
3315 case AD1988_LAPTOP_DIG:
3316 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3317 break;
3318 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003319#ifdef CONFIG_SND_HDA_POWER_SAVE
3320 spec->loopback.amplist = ad1988_loopbacks;
3321#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003322 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003323
Takashi Iwai729d55b2009-12-25 22:49:01 +01003324 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003325 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003326
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003327 return 0;
3328}
3329
3330
3331/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003332 * AD1884 / AD1984
3333 *
3334 * port-B - front line/mic-in
3335 * port-E - aux in/out
3336 * port-F - aux in/out
3337 * port-C - rear line/mic-in
3338 * port-D - rear line/hp-out
3339 * port-A - front line/hp-out
3340 *
3341 * AD1984 = AD1884 + two digital mic-ins
3342 *
3343 * FIXME:
3344 * For simplicity, we share the single DAC for both HP and line-outs
3345 * right now. The inidividual playbacks could be easily implemented,
3346 * but no build-up framework is given, so far.
3347 */
3348
Takashi Iwai498f5b12011-05-02 11:33:15 +02003349static const hda_nid_t ad1884_dac_nids[1] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003350 0x04,
3351};
3352
Takashi Iwai498f5b12011-05-02 11:33:15 +02003353static const hda_nid_t ad1884_adc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003354 0x08, 0x09,
3355};
3356
Takashi Iwai498f5b12011-05-02 11:33:15 +02003357static const hda_nid_t ad1884_capsrc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003358 0x0c, 0x0d,
3359};
3360
3361#define AD1884_SPDIF_OUT 0x02
3362
Takashi Iwai498f5b12011-05-02 11:33:15 +02003363static const struct hda_input_mux ad1884_capture_source = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003364 .num_items = 4,
3365 .items = {
3366 { "Front Mic", 0x0 },
3367 { "Mic", 0x1 },
3368 { "CD", 0x2 },
3369 { "Mix", 0x3 },
3370 },
3371};
3372
Takashi Iwai498f5b12011-05-02 11:33:15 +02003373static const struct snd_kcontrol_new ad1884_base_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003374 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3375 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3376 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3377 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3378 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3379 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3380 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3381 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3382 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3383 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3384 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3385 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003386 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3387 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003388 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3389 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3390 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3391 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3392 {
3393 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3394 /* The multiple "Capture Source" controls confuse alsamixer
3395 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003396 */
3397 /* .name = "Capture Source", */
3398 .name = "Input Source",
3399 .count = 2,
3400 .info = ad198x_mux_enum_info,
3401 .get = ad198x_mux_enum_get,
3402 .put = ad198x_mux_enum_put,
3403 },
3404 /* SPDIF controls */
3405 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3406 {
3407 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3408 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3409 /* identical with ad1983 */
3410 .info = ad1983_spdif_route_info,
3411 .get = ad1983_spdif_route_get,
3412 .put = ad1983_spdif_route_put,
3413 },
3414 { } /* end */
3415};
3416
Takashi Iwai498f5b12011-05-02 11:33:15 +02003417static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003418 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3419 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3420 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003421 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003422 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003423 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003424 { } /* end */
3425};
3426
3427/*
3428 * initialization verbs
3429 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003430static const struct hda_verb ad1884_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003431 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003432 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3433 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003434 /* Port-A (HP) mixer */
3435 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3436 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3437 /* Port-A pin */
3438 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3439 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3440 /* HP selector - select DAC2 */
3441 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3442 /* Port-D (Line-out) mixer */
3443 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3444 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3445 /* Port-D pin */
3446 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3447 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3448 /* Mono-out mixer */
3449 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3450 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3451 /* Mono-out pin */
3452 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3453 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3454 /* Mono selector */
3455 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3456 /* Port-B (front mic) pin */
3457 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003458 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003459 /* Port-C (rear mic) pin */
3460 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003461 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003462 /* Analog mixer; mute as default */
3463 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3464 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3465 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3466 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3467 /* Analog Mix output amp */
3468 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3469 /* SPDIF output selector */
3470 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3471 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3472 { } /* end */
3473};
3474
Takashi Iwaicb53c622007-08-10 17:21:45 +02003475#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02003476static const struct hda_amp_list ad1884_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003477 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3478 { 0x20, HDA_INPUT, 1 }, /* Mic */
3479 { 0x20, HDA_INPUT, 2 }, /* CD */
3480 { 0x20, HDA_INPUT, 4 }, /* Docking */
3481 { } /* end */
3482};
3483#endif
3484
Takashi Iwaiea734962011-01-17 11:29:34 +01003485static const char * const ad1884_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +01003486 "PCM Playback Volume",
3487 "Mic Playback Volume",
3488 "Mono Playback Volume",
3489 "Front Mic Playback Volume",
3490 "Mic Playback Volume",
3491 "CD Playback Volume",
3492 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003493 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003494 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003495 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003496 NULL
3497};
3498
Takashi Iwai2bac6472007-05-18 18:21:41 +02003499static int patch_ad1884(struct hda_codec *codec)
3500{
3501 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003502 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003503
3504 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3505 if (spec == NULL)
3506 return -ENOMEM;
3507
Takashi Iwai2bac6472007-05-18 18:21:41 +02003508 codec->spec = spec;
3509
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003510 err = snd_hda_attach_beep_device(codec, 0x10);
3511 if (err < 0) {
3512 ad198x_free(codec);
3513 return err;
3514 }
3515 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3516
Takashi Iwai2bac6472007-05-18 18:21:41 +02003517 spec->multiout.max_channels = 2;
3518 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3519 spec->multiout.dac_nids = ad1884_dac_nids;
3520 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3521 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3522 spec->adc_nids = ad1884_adc_nids;
3523 spec->capsrc_nids = ad1884_capsrc_nids;
3524 spec->input_mux = &ad1884_capture_source;
3525 spec->num_mixers = 1;
3526 spec->mixers[0] = ad1884_base_mixers;
3527 spec->num_init_verbs = 1;
3528 spec->init_verbs[0] = ad1884_init_verbs;
3529 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003530#ifdef CONFIG_SND_HDA_POWER_SAVE
3531 spec->loopback.amplist = ad1884_loopbacks;
3532#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003533 spec->vmaster_nid = 0x04;
3534 /* we need to cover all playback volumes */
3535 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003536
3537 codec->patch_ops = ad198x_patch_ops;
3538
Takashi Iwai729d55b2009-12-25 22:49:01 +01003539 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003540 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003541
Takashi Iwai2bac6472007-05-18 18:21:41 +02003542 return 0;
3543}
3544
3545/*
3546 * Lenovo Thinkpad T61/X61
3547 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003548static const struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003549 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003550 .items = {
3551 { "Mic", 0x0 },
3552 { "Internal Mic", 0x1 },
3553 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003554 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003555 },
3556};
3557
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003558
3559/*
3560 * Dell Precision T3400
3561 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003562static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003563 .num_items = 3,
3564 .items = {
3565 { "Front Mic", 0x0 },
3566 { "Line-In", 0x1 },
3567 { "Mix", 0x3 },
3568 },
3569};
3570
3571
Takashi Iwai498f5b12011-05-02 11:33:15 +02003572static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003573 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3574 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3575 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3576 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3577 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3578 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3579 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3580 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003581 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3582 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003583 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3584 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003585 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3586 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3587 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003588 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3589 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3590 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3591 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3592 {
3593 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3594 /* The multiple "Capture Source" controls confuse alsamixer
3595 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003596 */
3597 /* .name = "Capture Source", */
3598 .name = "Input Source",
3599 .count = 2,
3600 .info = ad198x_mux_enum_info,
3601 .get = ad198x_mux_enum_get,
3602 .put = ad198x_mux_enum_put,
3603 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003604 /* SPDIF controls */
3605 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3606 {
3607 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3608 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3609 /* identical with ad1983 */
3610 .info = ad1983_spdif_route_info,
3611 .get = ad1983_spdif_route_get,
3612 .put = ad1983_spdif_route_put,
3613 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003614 { } /* end */
3615};
3616
3617/* additional verbs */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003618static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003619 /* Port-E (docking station mic) pin */
3620 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3621 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3622 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003623 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003624 /* Analog PC Beeper - allow firmware/ACPI beeps */
3625 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003626 /* Analog mixer - docking mic; mute as default */
3627 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003628 /* enable EAPD bit */
3629 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003630 { } /* end */
3631};
3632
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003633/*
3634 * Dell Precision T3400
3635 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003636static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003637 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3638 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3639 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3640 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3641 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3642 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3643 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3644 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3645 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003646 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
3647 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003648 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3649 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3650 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3651 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3652 {
3653 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3654 /* The multiple "Capture Source" controls confuse alsamixer
3655 * So call somewhat different..
3656 */
3657 /* .name = "Capture Source", */
3658 .name = "Input Source",
3659 .count = 2,
3660 .info = ad198x_mux_enum_info,
3661 .get = ad198x_mux_enum_get,
3662 .put = ad198x_mux_enum_put,
3663 },
3664 { } /* end */
3665};
3666
Takashi Iwai2bac6472007-05-18 18:21:41 +02003667/* Digial MIC ADC NID 0x05 + 0x06 */
3668static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3669 struct hda_codec *codec,
3670 unsigned int stream_tag,
3671 unsigned int format,
3672 struct snd_pcm_substream *substream)
3673{
3674 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3675 stream_tag, 0, format);
3676 return 0;
3677}
3678
3679static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3680 struct hda_codec *codec,
3681 struct snd_pcm_substream *substream)
3682{
Takashi Iwai888afa12008-03-18 09:57:50 +01003683 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003684 return 0;
3685}
3686
Takashi Iwai498f5b12011-05-02 11:33:15 +02003687static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003688 .substreams = 2,
3689 .channels_min = 2,
3690 .channels_max = 2,
3691 .nid = 0x05,
3692 .ops = {
3693 .prepare = ad1984_pcm_dmic_prepare,
3694 .cleanup = ad1984_pcm_dmic_cleanup
3695 },
3696};
3697
3698static int ad1984_build_pcms(struct hda_codec *codec)
3699{
3700 struct ad198x_spec *spec = codec->spec;
3701 struct hda_pcm *info;
3702 int err;
3703
3704 err = ad198x_build_pcms(codec);
3705 if (err < 0)
3706 return err;
3707
3708 info = spec->pcm_rec + codec->num_pcms;
3709 codec->num_pcms++;
3710 info->name = "AD1984 Digital Mic";
3711 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3712 return 0;
3713}
3714
3715/* models */
3716enum {
3717 AD1984_BASIC,
3718 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003719 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003720 AD1984_MODELS
3721};
3722
Takashi Iwaiea734962011-01-17 11:29:34 +01003723static const char * const ad1984_models[AD1984_MODELS] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003724 [AD1984_BASIC] = "basic",
3725 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003726 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003727};
3728
Takashi Iwai498f5b12011-05-02 11:33:15 +02003729static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003730 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003731 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003732 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003733 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003734 {}
3735};
3736
3737static int patch_ad1984(struct hda_codec *codec)
3738{
3739 struct ad198x_spec *spec;
3740 int board_config, err;
3741
3742 err = patch_ad1884(codec);
3743 if (err < 0)
3744 return err;
3745 spec = codec->spec;
3746 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3747 ad1984_models, ad1984_cfg_tbl);
3748 switch (board_config) {
3749 case AD1984_BASIC:
3750 /* additional digital mics */
3751 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3752 codec->patch_ops.build_pcms = ad1984_build_pcms;
3753 break;
3754 case AD1984_THINKPAD:
Jerone Young68c18692010-08-03 01:46:44 -05003755 if (codec->subsystem_id == 0x17aa20fb) {
3756 /* Thinpad X300 does not have the ability to do SPDIF,
3757 or attach to docking station to use SPDIF */
3758 spec->multiout.dig_out_nid = 0;
3759 } else
3760 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003761 spec->input_mux = &ad1984_thinkpad_capture_source;
3762 spec->mixers[0] = ad1984_thinkpad_mixers;
3763 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003764 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003765 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003766 case AD1984_DELL_DESKTOP:
3767 spec->multiout.dig_out_nid = 0;
3768 spec->input_mux = &ad1984_dell_desktop_capture_source;
3769 spec->mixers[0] = ad1984_dell_desktop_mixers;
3770 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003771 }
3772 return 0;
3773}
3774
3775
3776/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003777 * AD1883 / AD1884A / AD1984A / AD1984B
3778 *
3779 * port-B (0x14) - front mic-in
3780 * port-E (0x1c) - rear mic-in
3781 * port-F (0x16) - CD / ext out
3782 * port-C (0x15) - rear line-in
3783 * port-D (0x12) - rear line-out
3784 * port-A (0x11) - front hp-out
3785 *
3786 * AD1984A = AD1884A + digital-mic
3787 * AD1883 = equivalent with AD1984A
3788 * AD1984B = AD1984A + extra SPDIF-out
3789 *
3790 * FIXME:
3791 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3792 */
3793
Takashi Iwai498f5b12011-05-02 11:33:15 +02003794static const hda_nid_t ad1884a_dac_nids[1] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003795 0x03,
3796};
3797
3798#define ad1884a_adc_nids ad1884_adc_nids
3799#define ad1884a_capsrc_nids ad1884_capsrc_nids
3800
3801#define AD1884A_SPDIF_OUT 0x02
3802
Takashi Iwai498f5b12011-05-02 11:33:15 +02003803static const struct hda_input_mux ad1884a_capture_source = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003804 .num_items = 5,
3805 .items = {
3806 { "Front Mic", 0x0 },
3807 { "Mic", 0x4 },
3808 { "Line", 0x1 },
3809 { "CD", 0x2 },
3810 { "Mix", 0x3 },
3811 },
3812};
3813
Takashi Iwai498f5b12011-05-02 11:33:15 +02003814static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003815 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3816 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3817 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3818 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3819 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3820 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3821 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3822 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3823 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3824 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3825 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3826 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3827 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3828 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3829 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3830 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003831 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3832 HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
3833 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003834 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3835 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3836 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3837 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3838 {
3839 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3840 /* The multiple "Capture Source" controls confuse alsamixer
3841 * So call somewhat different..
3842 */
3843 /* .name = "Capture Source", */
3844 .name = "Input Source",
3845 .count = 2,
3846 .info = ad198x_mux_enum_info,
3847 .get = ad198x_mux_enum_get,
3848 .put = ad198x_mux_enum_put,
3849 },
3850 /* SPDIF controls */
3851 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3852 {
3853 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3854 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3855 /* identical with ad1983 */
3856 .info = ad1983_spdif_route_info,
3857 .get = ad1983_spdif_route_get,
3858 .put = ad1983_spdif_route_put,
3859 },
3860 { } /* end */
3861};
3862
3863/*
3864 * initialization verbs
3865 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003866static const struct hda_verb ad1884a_init_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003867 /* DACs; unmute as default */
3868 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3869 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3870 /* Port-A (HP) mixer - route only from analog mixer */
3871 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3872 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3873 /* Port-A pin */
3874 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3875 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3876 /* Port-D (Line-out) mixer - route only from analog mixer */
3877 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3878 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3879 /* Port-D pin */
3880 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3881 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3882 /* Mono-out mixer - route only from analog mixer */
3883 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3884 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3885 /* Mono-out pin */
3886 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3887 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3888 /* Port-B (front mic) pin */
3889 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003890 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003891 /* Port-C (rear line-in) pin */
3892 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003893 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01003894 /* Port-E (rear mic) pin */
3895 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3896 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3897 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
3898 /* Port-F (CD) pin */
3899 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
3900 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3901 /* Analog mixer; mute as default */
3902 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3903 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3904 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3905 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3906 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
3907 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
3908 /* Analog Mix output amp */
3909 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3910 /* capture sources */
3911 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
3912 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3913 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
3914 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3915 /* SPDIF output amp */
3916 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3917 { } /* end */
3918};
3919
3920#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02003921static const struct hda_amp_list ad1884a_loopbacks[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003922 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3923 { 0x20, HDA_INPUT, 1 }, /* Mic */
3924 { 0x20, HDA_INPUT, 2 }, /* CD */
3925 { 0x20, HDA_INPUT, 4 }, /* Docking */
3926 { } /* end */
3927};
3928#endif
3929
3930/*
3931 * Laptop model
3932 *
3933 * Port A: Headphone jack
3934 * Port B: MIC jack
3935 * Port C: Internal MIC
3936 * Port D: Dock Line Out (if enabled)
3937 * Port E: Dock Line In (if enabled)
3938 * Port F: Internal speakers
3939 */
3940
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003941static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
3942 struct snd_ctl_elem_value *ucontrol)
3943{
3944 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3945 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3946 int mute = (!ucontrol->value.integer.value[0] &&
3947 !ucontrol->value.integer.value[1]);
3948 /* toggle GPIO1 according to the mute state */
3949 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3950 mute ? 0x02 : 0x0);
3951 return ret;
3952}
Takashi Iwaic5059252008-02-16 09:43:56 +01003953
Takashi Iwai498f5b12011-05-02 11:33:15 +02003954static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003955 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003956 {
3957 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3958 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003959 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02003960 .info = snd_hda_mixer_amp_switch_info,
3961 .get = snd_hda_mixer_amp_switch_get,
3962 .put = ad1884a_mobile_master_sw_put,
3963 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3964 },
Takashi Iwaic5059252008-02-16 09:43:56 +01003965 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3966 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3967 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3968 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3969 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3970 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3971 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3972 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3973 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003974 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3975 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3976 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003977 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3978 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003979 { } /* end */
3980};
3981
Takashi Iwai498f5b12011-05-02 11:33:15 +02003982static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003983 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02003984 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
3985 {
3986 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3987 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01003988 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02003989 .info = snd_hda_mixer_amp_switch_info,
3990 .get = snd_hda_mixer_amp_switch_get,
3991 .put = ad1884a_mobile_master_sw_put,
3992 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
3993 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003994 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3995 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02003996 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
3997 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01003998 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3999 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004000 { } /* end */
4001};
4002
Takashi Iwaic5059252008-02-16 09:43:56 +01004003/* mute internal speaker if HP is plugged */
4004static void ad1884a_hp_automute(struct hda_codec *codec)
4005{
4006 unsigned int present;
4007
Takashi Iwaid56757a2009-11-18 08:00:14 +01004008 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01004009 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4010 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4011 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4012 present ? 0x00 : 0x02);
4013}
4014
Takashi Iwai269ef192008-05-30 15:32:15 +02004015/* switch to external mic if plugged */
4016static void ad1884a_hp_automic(struct hda_codec *codec)
4017{
4018 unsigned int present;
4019
Takashi Iwaid56757a2009-11-18 08:00:14 +01004020 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02004021 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
4022 present ? 0 : 1);
4023}
4024
Takashi Iwaic5059252008-02-16 09:43:56 +01004025#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02004026#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01004027
4028/* unsolicited event for HP jack sensing */
4029static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
4030{
Takashi Iwai269ef192008-05-30 15:32:15 +02004031 switch (res >> 26) {
4032 case AD1884A_HP_EVENT:
4033 ad1884a_hp_automute(codec);
4034 break;
4035 case AD1884A_MIC_EVENT:
4036 ad1884a_hp_automic(codec);
4037 break;
4038 }
Takashi Iwaic5059252008-02-16 09:43:56 +01004039}
4040
4041/* initialize jack-sensing, too */
4042static int ad1884a_hp_init(struct hda_codec *codec)
4043{
4044 ad198x_init(codec);
4045 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02004046 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01004047 return 0;
4048}
4049
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004050/* mute internal speaker if HP or docking HP is plugged */
4051static void ad1884a_laptop_automute(struct hda_codec *codec)
4052{
4053 unsigned int present;
4054
Takashi Iwaid56757a2009-11-18 08:00:14 +01004055 present = snd_hda_jack_detect(codec, 0x11);
4056 if (!present)
4057 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004058 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4059 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4060 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4061 present ? 0x00 : 0x02);
4062}
4063
4064/* switch to external mic if plugged */
4065static void ad1884a_laptop_automic(struct hda_codec *codec)
4066{
4067 unsigned int idx;
4068
Takashi Iwaid56757a2009-11-18 08:00:14 +01004069 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004070 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01004071 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004072 idx = 4;
4073 else
4074 idx = 1;
4075 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
4076}
4077
4078/* unsolicited event for HP jack sensing */
4079static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
4080 unsigned int res)
4081{
4082 switch (res >> 26) {
4083 case AD1884A_HP_EVENT:
4084 ad1884a_laptop_automute(codec);
4085 break;
4086 case AD1884A_MIC_EVENT:
4087 ad1884a_laptop_automic(codec);
4088 break;
4089 }
4090}
4091
4092/* initialize jack-sensing, too */
4093static int ad1884a_laptop_init(struct hda_codec *codec)
4094{
4095 ad198x_init(codec);
4096 ad1884a_laptop_automute(codec);
4097 ad1884a_laptop_automic(codec);
4098 return 0;
4099}
4100
Takashi Iwaic5059252008-02-16 09:43:56 +01004101/* additional verbs for laptop model */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004102static const struct hda_verb ad1884a_laptop_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004103 /* Port-A (HP) pin - always unmuted */
4104 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4105 /* Port-F (int speaker) mixer - route only from analog mixer */
4106 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4107 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004108 /* Port-F (int speaker) pin */
4109 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004110 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004111 /* required for compaq 6530s/6531s speaker output */
4112 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004113 /* Port-C pin - internal mic-in */
4114 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4115 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4116 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004117 /* Port-D (docking line-out) pin - default unmuted */
4118 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004119 /* analog mix */
4120 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4121 /* unsolicited event for pin-sense */
4122 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004123 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004124 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004125 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004126 /* allow to touch GPIO1 (for mute control) */
4127 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4128 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4129 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004130 { } /* end */
4131};
4132
Takashi Iwai498f5b12011-05-02 11:33:15 +02004133static const struct hda_verb ad1884a_mobile_verbs[] = {
Takashi Iwai73156132009-04-23 08:24:48 +02004134 /* DACs; unmute as default */
4135 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4136 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4137 /* Port-A (HP) mixer - route only from analog mixer */
4138 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4139 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4140 /* Port-A pin */
4141 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4142 /* Port-A (HP) pin - always unmuted */
4143 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4144 /* Port-B (mic jack) pin */
4145 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4146 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4147 /* Port-C (int mic) pin */
4148 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4149 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4150 /* Port-F (int speaker) mixer - route only from analog mixer */
4151 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4152 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4153 /* Port-F pin */
4154 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4155 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4156 /* Analog mixer; mute as default */
4157 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4158 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4159 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4160 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4161 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4162 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4163 /* Analog Mix output amp */
4164 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4165 /* capture sources */
4166 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4167 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4168 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4169 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4170 /* unsolicited event for pin-sense */
4171 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4172 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02004173 /* allow to touch GPIO1 (for mute control) */
4174 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4175 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4176 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004177 { } /* end */
4178};
4179
Takashi Iwaic5059252008-02-16 09:43:56 +01004180/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004181 * Thinkpad X300
4182 * 0x11 - HP
4183 * 0x12 - speaker
4184 * 0x14 - mic-in
4185 * 0x17 - built-in mic
4186 */
4187
Takashi Iwai498f5b12011-05-02 11:33:15 +02004188static const struct hda_verb ad1984a_thinkpad_verbs[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004189 /* HP unmute */
4190 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4191 /* analog mix */
4192 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4193 /* turn on EAPD */
4194 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4195 /* unsolicited event for pin-sense */
4196 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4197 /* internal mic - dmic */
4198 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004199 /* set magic COEFs for dmic */
4200 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4201 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004202 { } /* end */
4203};
4204
Takashi Iwai498f5b12011-05-02 11:33:15 +02004205static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004206 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4207 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4208 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4209 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4210 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4211 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004212 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4213 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004214 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4215 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4216 {
4217 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4218 .name = "Capture Source",
4219 .info = ad198x_mux_enum_info,
4220 .get = ad198x_mux_enum_get,
4221 .put = ad198x_mux_enum_put,
4222 },
4223 { } /* end */
4224};
4225
Takashi Iwai498f5b12011-05-02 11:33:15 +02004226static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004227 .num_items = 3,
4228 .items = {
4229 { "Mic", 0x0 },
4230 { "Internal Mic", 0x5 },
4231 { "Mix", 0x3 },
4232 },
4233};
4234
4235/* mute internal speaker if HP is plugged */
4236static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4237{
4238 unsigned int present;
4239
Takashi Iwaid56757a2009-11-18 08:00:14 +01004240 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004241 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4242 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4243}
4244
4245/* unsolicited event for HP jack sensing */
4246static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4247 unsigned int res)
4248{
4249 if ((res >> 26) != AD1884A_HP_EVENT)
4250 return;
4251 ad1984a_thinkpad_automute(codec);
4252}
4253
4254/* initialize jack-sensing, too */
4255static int ad1984a_thinkpad_init(struct hda_codec *codec)
4256{
4257 ad198x_init(codec);
4258 ad1984a_thinkpad_automute(codec);
4259 return 0;
4260}
4261
4262/*
David Henningsson677cd902011-02-07 15:19:34 +01004263 * Precision R5500
4264 * 0x12 - HP/line-out
4265 * 0x13 - speaker (mono)
4266 * 0x15 - mic-in
4267 */
4268
Takashi Iwai498f5b12011-05-02 11:33:15 +02004269static const struct hda_verb ad1984a_precision_verbs[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004270 /* Unmute main output path */
4271 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4272 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
4273 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
4274 /* Analog mixer; mute as default */
4275 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4276 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4277 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4278 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4279 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4280 /* Select mic as input */
4281 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
4282 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
4283 /* Configure as mic */
4284 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4285 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4286 /* HP unmute */
4287 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4288 /* turn on EAPD */
4289 {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4290 /* unsolicited event for pin-sense */
4291 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4292 { } /* end */
4293};
4294
Takashi Iwai498f5b12011-05-02 11:33:15 +02004295static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004296 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4297 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4298 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4299 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4300 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4301 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4302 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4303 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4304 HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
4305 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4306 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4307 { } /* end */
4308};
4309
4310
4311/* mute internal speaker if HP is plugged */
4312static void ad1984a_precision_automute(struct hda_codec *codec)
4313{
4314 unsigned int present;
4315
4316 present = snd_hda_jack_detect(codec, 0x12);
4317 snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
4318 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4319}
4320
4321
4322/* unsolicited event for HP jack sensing */
4323static void ad1984a_precision_unsol_event(struct hda_codec *codec,
4324 unsigned int res)
4325{
4326 if ((res >> 26) != AD1884A_HP_EVENT)
4327 return;
4328 ad1984a_precision_automute(codec);
4329}
4330
4331/* initialize jack-sensing, too */
4332static int ad1984a_precision_init(struct hda_codec *codec)
4333{
4334 ad198x_init(codec);
4335 ad1984a_precision_automute(codec);
4336 return 0;
4337}
4338
4339
4340/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004341 * HP Touchsmart
4342 * port-A (0x11) - front hp-out
4343 * port-B (0x14) - unused
4344 * port-C (0x15) - unused
4345 * port-D (0x12) - rear line out
4346 * port-E (0x1c) - front mic-in
4347 * port-F (0x16) - Internal speakers
4348 * digital-mic (0x17) - Internal mic
4349 */
4350
Takashi Iwai498f5b12011-05-02 11:33:15 +02004351static const struct hda_verb ad1984a_touchsmart_verbs[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004352 /* DACs; unmute as default */
4353 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4354 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4355 /* Port-A (HP) mixer - route only from analog mixer */
4356 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4357 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4358 /* Port-A pin */
4359 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4360 /* Port-A (HP) pin - always unmuted */
4361 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4362 /* Port-E (int speaker) mixer - route only from analog mixer */
4363 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4364 /* Port-E pin */
4365 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4366 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4367 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4368 /* Port-F (int speaker) mixer - route only from analog mixer */
4369 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4370 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4371 /* Port-F pin */
4372 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4373 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4374 /* Analog mixer; mute as default */
4375 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4376 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4377 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4378 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4379 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4380 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4381 /* Analog Mix output amp */
4382 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4383 /* capture sources */
4384 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4385 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4386 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4387 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4388 /* unsolicited event for pin-sense */
4389 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4390 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4391 /* allow to touch GPIO1 (for mute control) */
4392 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4393 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4394 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4395 /* internal mic - dmic */
4396 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4397 /* set magic COEFs for dmic */
4398 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4399 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4400 { } /* end */
4401};
4402
Takashi Iwai498f5b12011-05-02 11:33:15 +02004403static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004404 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4405/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4406 {
4407 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004408 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004409 .name = "Master Playback Switch",
4410 .info = snd_hda_mixer_amp_switch_info,
4411 .get = snd_hda_mixer_amp_switch_get,
4412 .put = ad1884a_mobile_master_sw_put,
4413 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4414 },
4415 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4416 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4417 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4418 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004419 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
4420 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004421 { } /* end */
4422};
4423
4424/* switch to external mic if plugged */
4425static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4426{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004427 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004428 snd_hda_codec_write(codec, 0x0c, 0,
4429 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004430 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004431 snd_hda_codec_write(codec, 0x0c, 0,
4432 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004433}
4434
4435
4436/* unsolicited event for HP jack sensing */
4437static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4438 unsigned int res)
4439{
4440 switch (res >> 26) {
4441 case AD1884A_HP_EVENT:
4442 ad1884a_hp_automute(codec);
4443 break;
4444 case AD1884A_MIC_EVENT:
4445 ad1984a_touchsmart_automic(codec);
4446 break;
4447 }
4448}
4449
4450/* initialize jack-sensing, too */
4451static int ad1984a_touchsmart_init(struct hda_codec *codec)
4452{
4453 ad198x_init(codec);
4454 ad1884a_hp_automute(codec);
4455 ad1984a_touchsmart_automic(codec);
4456 return 0;
4457}
4458
4459
4460/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004461 */
4462
4463enum {
4464 AD1884A_DESKTOP,
4465 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004466 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004467 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004468 AD1984A_TOUCHSMART,
David Henningsson677cd902011-02-07 15:19:34 +01004469 AD1984A_PRECISION,
Takashi Iwaic5059252008-02-16 09:43:56 +01004470 AD1884A_MODELS
4471};
4472
Takashi Iwaiea734962011-01-17 11:29:34 +01004473static const char * const ad1884a_models[AD1884A_MODELS] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004474 [AD1884A_DESKTOP] = "desktop",
4475 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004476 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004477 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004478 [AD1984A_TOUCHSMART] = "touchsmart",
David Henningsson677cd902011-02-07 15:19:34 +01004479 [AD1984A_PRECISION] = "precision",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004480};
4481
Takashi Iwai498f5b12011-05-02 11:33:15 +02004482static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004483 SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004484 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004485 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004486 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004487 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004488 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004489 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4490 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004491 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004492 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004493 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004494 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004495};
4496
4497static int patch_ad1884a(struct hda_codec *codec)
4498{
4499 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004500 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004501
4502 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4503 if (spec == NULL)
4504 return -ENOMEM;
4505
Takashi Iwaic5059252008-02-16 09:43:56 +01004506 codec->spec = spec;
4507
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004508 err = snd_hda_attach_beep_device(codec, 0x10);
4509 if (err < 0) {
4510 ad198x_free(codec);
4511 return err;
4512 }
4513 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4514
Takashi Iwaic5059252008-02-16 09:43:56 +01004515 spec->multiout.max_channels = 2;
4516 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4517 spec->multiout.dac_nids = ad1884a_dac_nids;
4518 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4519 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4520 spec->adc_nids = ad1884a_adc_nids;
4521 spec->capsrc_nids = ad1884a_capsrc_nids;
4522 spec->input_mux = &ad1884a_capture_source;
4523 spec->num_mixers = 1;
4524 spec->mixers[0] = ad1884a_base_mixers;
4525 spec->num_init_verbs = 1;
4526 spec->init_verbs[0] = ad1884a_init_verbs;
4527 spec->spdif_route = 0;
4528#ifdef CONFIG_SND_HDA_POWER_SAVE
4529 spec->loopback.amplist = ad1884a_loopbacks;
4530#endif
4531 codec->patch_ops = ad198x_patch_ops;
4532
4533 /* override some parameters */
4534 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004535 ad1884a_models,
4536 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004537 switch (board_config) {
4538 case AD1884A_LAPTOP:
4539 spec->mixers[0] = ad1884a_laptop_mixers;
4540 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4541 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004542 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4543 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004544 /* set the upper-limit for mixer amp to 0dB for avoiding the
4545 * possible damage by overloading
4546 */
4547 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4548 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4549 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4550 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4551 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004552 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004553 case AD1884A_MOBILE:
4554 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004555 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004556 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004557 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4558 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004559 /* set the upper-limit for mixer amp to 0dB for avoiding the
4560 * possible damage by overloading
4561 */
4562 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4563 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4564 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4565 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4566 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004567 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004568 case AD1884A_THINKPAD:
4569 spec->mixers[0] = ad1984a_thinkpad_mixers;
4570 spec->init_verbs[spec->num_init_verbs++] =
4571 ad1984a_thinkpad_verbs;
4572 spec->multiout.dig_out_nid = 0;
4573 spec->input_mux = &ad1984a_thinkpad_capture_source;
4574 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4575 codec->patch_ops.init = ad1984a_thinkpad_init;
4576 break;
David Henningsson677cd902011-02-07 15:19:34 +01004577 case AD1984A_PRECISION:
4578 spec->mixers[0] = ad1984a_precision_mixers;
4579 spec->init_verbs[spec->num_init_verbs++] =
4580 ad1984a_precision_verbs;
4581 spec->multiout.dig_out_nid = 0;
4582 codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
4583 codec->patch_ops.init = ad1984a_precision_init;
4584 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004585 case AD1984A_TOUCHSMART:
4586 spec->mixers[0] = ad1984a_touchsmart_mixers;
4587 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4588 spec->multiout.dig_out_nid = 0;
4589 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4590 codec->patch_ops.init = ad1984a_touchsmart_init;
4591 /* set the upper-limit for mixer amp to 0dB for avoiding the
4592 * possible damage by overloading
4593 */
4594 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4595 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4596 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4597 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4598 (1 << AC_AMPCAP_MUTE_SHIFT));
4599 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004600 }
4601
Takashi Iwai729d55b2009-12-25 22:49:01 +01004602 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004603 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004604
Takashi Iwaic5059252008-02-16 09:43:56 +01004605 return 0;
4606}
4607
4608
4609/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004610 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004611 *
4612 * port-A - front hp-out
4613 * port-B - front mic-in
4614 * port-C - rear line-in, shared surr-out (3stack)
4615 * port-D - rear line-out
4616 * port-E - rear mic-in, shared clfe-out (3stack)
4617 * port-F - rear surr-out (6stack)
4618 * port-G - rear clfe-out (6stack)
4619 */
4620
Takashi Iwai498f5b12011-05-02 11:33:15 +02004621static const hda_nid_t ad1882_dac_nids[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004622 0x04, 0x03, 0x05
4623};
4624
Takashi Iwai498f5b12011-05-02 11:33:15 +02004625static const hda_nid_t ad1882_adc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004626 0x08, 0x09,
4627};
4628
Takashi Iwai498f5b12011-05-02 11:33:15 +02004629static const hda_nid_t ad1882_capsrc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004630 0x0c, 0x0d,
4631};
4632
4633#define AD1882_SPDIF_OUT 0x02
4634
4635/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004636static const struct hda_input_mux ad1882_capture_source = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004637 .num_items = 5,
4638 .items = {
4639 { "Front Mic", 0x1 },
4640 { "Mic", 0x4 },
4641 { "Line", 0x2 },
4642 { "CD", 0x3 },
4643 { "Mix", 0x7 },
4644 },
4645};
4646
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004647/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004648static const struct hda_input_mux ad1882a_capture_source = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004649 .num_items = 5,
4650 .items = {
4651 { "Front Mic", 0x1 },
4652 { "Mic", 0x4},
4653 { "Line", 0x2 },
4654 { "Digital Mic", 0x06 },
4655 { "Mix", 0x7 },
4656 },
4657};
4658
Takashi Iwai498f5b12011-05-02 11:33:15 +02004659static const struct snd_kcontrol_new ad1882_base_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004660 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4661 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4662 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4663 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4664 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4665 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4666 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4667 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004668
David Henningsson5f99f862011-01-04 15:24:24 +01004669 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
4670 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
4671 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
Takashi Iwai0ac85512007-06-20 15:46:13 +02004672 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4673 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4674 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4675 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4676 {
4677 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4678 /* The multiple "Capture Source" controls confuse alsamixer
4679 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004680 */
4681 /* .name = "Capture Source", */
4682 .name = "Input Source",
4683 .count = 2,
4684 .info = ad198x_mux_enum_info,
4685 .get = ad198x_mux_enum_get,
4686 .put = ad198x_mux_enum_put,
4687 },
4688 /* SPDIF controls */
4689 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4690 {
4691 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4692 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4693 /* identical with ad1983 */
4694 .info = ad1983_spdif_route_info,
4695 .get = ad1983_spdif_route_get,
4696 .put = ad1983_spdif_route_put,
4697 },
4698 { } /* end */
4699};
4700
Takashi Iwai498f5b12011-05-02 11:33:15 +02004701static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004702 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4703 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4704 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4705 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4706 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4707 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4708 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4709 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004710 { } /* end */
4711};
4712
Takashi Iwai498f5b12011-05-02 11:33:15 +02004713static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004714 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4715 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4716 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4717 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4718 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4719 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4720 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4721 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004722 HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004723 { } /* end */
4724};
4725
Takashi Iwai498f5b12011-05-02 11:33:15 +02004726static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004727 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4728 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4729 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4730 {
4731 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4732 .name = "Channel Mode",
4733 .info = ad198x_ch_mode_info,
4734 .get = ad198x_ch_mode_get,
4735 .put = ad198x_ch_mode_put,
4736 },
4737 { } /* end */
4738};
4739
Takashi Iwai498f5b12011-05-02 11:33:15 +02004740static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004741 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4742 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4743 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4744 { } /* end */
4745};
4746
Takashi Iwai498f5b12011-05-02 11:33:15 +02004747static const struct hda_verb ad1882_ch2_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004748 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4749 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4750 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4751 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4752 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4753 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4754 { } /* end */
4755};
4756
Takashi Iwai498f5b12011-05-02 11:33:15 +02004757static const struct hda_verb ad1882_ch4_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004758 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4759 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4760 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4761 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4762 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4763 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4764 { } /* end */
4765};
4766
Takashi Iwai498f5b12011-05-02 11:33:15 +02004767static const struct hda_verb ad1882_ch6_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004768 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4769 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4770 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4771 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4772 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4773 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4774 { } /* end */
4775};
4776
Takashi Iwai498f5b12011-05-02 11:33:15 +02004777static const struct hda_channel_mode ad1882_modes[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004778 { 2, ad1882_ch2_init },
4779 { 4, ad1882_ch4_init },
4780 { 6, ad1882_ch6_init },
4781};
4782
4783/*
4784 * initialization verbs
4785 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004786static const struct hda_verb ad1882_init_verbs[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004787 /* DACs; mute as default */
4788 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4789 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4790 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4791 /* Port-A (HP) mixer */
4792 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4793 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4794 /* Port-A pin */
4795 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4796 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4797 /* HP selector - select DAC2 */
4798 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4799 /* Port-D (Line-out) mixer */
4800 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4801 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4802 /* Port-D pin */
4803 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4804 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4805 /* Mono-out mixer */
4806 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4807 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4808 /* Mono-out pin */
4809 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4810 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4811 /* Port-B (front mic) pin */
4812 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4813 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4814 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4815 /* Port-C (line-in) pin */
4816 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4817 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4818 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4819 /* Port-C mixer - mute as input */
4820 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4821 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4822 /* Port-E (mic-in) pin */
4823 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4824 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4825 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4826 /* Port-E mixer - mute as input */
4827 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4828 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4829 /* Port-F (surround) */
4830 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4831 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4832 /* Port-G (CLFE) */
4833 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4834 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4835 /* Analog mixer; mute as default */
4836 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4837 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4838 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4839 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4840 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4841 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4842 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4843 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4844 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4845 /* Analog Mix output amp */
4846 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4847 /* SPDIF output selector */
4848 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4849 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4850 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4851 { } /* end */
4852};
4853
Takashi Iwaicb53c622007-08-10 17:21:45 +02004854#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02004855static const struct hda_amp_list ad1882_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02004856 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4857 { 0x20, HDA_INPUT, 1 }, /* Mic */
4858 { 0x20, HDA_INPUT, 4 }, /* Line */
4859 { 0x20, HDA_INPUT, 6 }, /* CD */
4860 { } /* end */
4861};
4862#endif
4863
Takashi Iwai0ac85512007-06-20 15:46:13 +02004864/* models */
4865enum {
4866 AD1882_3STACK,
4867 AD1882_6STACK,
4868 AD1882_MODELS
4869};
4870
Takashi Iwaiea734962011-01-17 11:29:34 +01004871static const char * const ad1882_models[AD1986A_MODELS] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004872 [AD1882_3STACK] = "3stack",
4873 [AD1882_6STACK] = "6stack",
4874};
4875
4876
4877static int patch_ad1882(struct hda_codec *codec)
4878{
4879 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004880 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004881
4882 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4883 if (spec == NULL)
4884 return -ENOMEM;
4885
Takashi Iwai0ac85512007-06-20 15:46:13 +02004886 codec->spec = spec;
4887
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004888 err = snd_hda_attach_beep_device(codec, 0x10);
4889 if (err < 0) {
4890 ad198x_free(codec);
4891 return err;
4892 }
4893 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4894
Takashi Iwai0ac85512007-06-20 15:46:13 +02004895 spec->multiout.max_channels = 6;
4896 spec->multiout.num_dacs = 3;
4897 spec->multiout.dac_nids = ad1882_dac_nids;
4898 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
4899 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
4900 spec->adc_nids = ad1882_adc_nids;
4901 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004902 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004903 spec->input_mux = &ad1882_capture_source;
4904 else
4905 spec->input_mux = &ad1882a_capture_source;
4906 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004907 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01004908 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004909 spec->mixers[1] = ad1882_loopback_mixers;
4910 else
4911 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004912 spec->num_init_verbs = 1;
4913 spec->init_verbs[0] = ad1882_init_verbs;
4914 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02004915#ifdef CONFIG_SND_HDA_POWER_SAVE
4916 spec->loopback.amplist = ad1882_loopbacks;
4917#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01004918 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004919
4920 codec->patch_ops = ad198x_patch_ops;
4921
4922 /* override some parameters */
4923 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
4924 ad1882_models, NULL);
4925 switch (board_config) {
4926 default:
4927 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004928 spec->num_mixers = 3;
4929 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004930 spec->channel_mode = ad1882_modes;
4931 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
4932 spec->need_dac_fix = 1;
4933 spec->multiout.max_channels = 2;
4934 spec->multiout.num_dacs = 1;
4935 break;
4936 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004937 spec->num_mixers = 3;
4938 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004939 break;
4940 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01004941
4942 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004943 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004944
Takashi Iwai0ac85512007-06-20 15:46:13 +02004945 return 0;
4946}
4947
4948
4949/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07004950 * patch entries
4951 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004952static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004953 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02004954 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004955 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004956 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004957 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
4958 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02004959 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
4960 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02004961 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004962 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01004963 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02004964 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004965 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02004966 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
4967 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07004968 {} /* terminator */
4969};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01004970
4971MODULE_ALIAS("snd-hda-codec-id:11d4*");
4972
4973MODULE_LICENSE("GPL");
4974MODULE_DESCRIPTION("Analog Devices HD-audio codec");
4975
4976static struct hda_codec_preset_list analog_list = {
4977 .preset = snd_hda_preset_analog,
4978 .owner = THIS_MODULE,
4979};
4980
4981static int __init patch_analog_init(void)
4982{
4983 return snd_hda_add_codec_preset(&analog_list);
4984}
4985
4986static void __exit patch_analog_exit(void)
4987{
4988 snd_hda_delete_codec_preset(&analog_list);
4989}
4990
4991module_init(patch_analog_init)
4992module_exit(patch_analog_exit)