blob: d8aac588f23ba3b827a6ffa34199065c29103e93 [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 Yau34588702011-09-23 19:03:25 +080051 int independent_hp;
52 int num_active_streams;
Raymond Yauc66ddf32011-01-17 11:19:03 +010053
Takashi Iwai985be542005-11-02 18:26:49 +010054 /* capture */
55 unsigned int num_adc_nids;
Takashi Iwai498f5b12011-05-02 11:33:15 +020056 const hda_nid_t *adc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010057 hda_nid_t dig_in_nid; /* digital-in NID; optional */
58
59 /* capture source */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020060 const struct hda_input_mux *input_mux;
Takashi Iwai498f5b12011-05-02 11:33:15 +020061 const hda_nid_t *capsrc_nids;
Takashi Iwai985be542005-11-02 18:26:49 +010062 unsigned int cur_mux[3];
63
64 /* channel model */
Takashi Iwaid2a6d7d2005-11-17 11:06:29 +010065 const struct hda_channel_mode *channel_mode;
Takashi Iwai985be542005-11-02 18:26:49 +010066 int num_channel_mode;
67
68 /* PCM information */
Takashi Iwai2bac6472007-05-18 18:21:41 +020069 struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
Takashi Iwai985be542005-11-02 18:26:49 +010070
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020071 unsigned int spdif_route;
Takashi Iwaid32410b12005-11-24 16:06:23 +010072
73 /* dynamic controls, init_verbs and input_mux */
74 struct auto_pin_cfg autocfg;
Takashi Iwai603c4012008-07-30 15:01:44 +020075 struct snd_array kctls;
Takashi Iwaid32410b12005-11-24 16:06:23 +010076 struct hda_input_mux private_imux;
Takashi Iwai41923e42007-10-22 17:20:10 +020077 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
Takashi Iwaicb53c622007-08-10 17:21:45 +020078
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +010079 unsigned int jack_present: 1;
80 unsigned int inv_jack_detect: 1;/* inverted jack-detection */
81 unsigned int inv_eapd: 1; /* inverted EAPD implementation */
82 unsigned int analog_beep: 1; /* analog beep input present */
Takashi Iwai8ab78c72007-09-06 14:29:53 +020083
Takashi Iwaicb53c622007-08-10 17:21:45 +020084#ifdef CONFIG_SND_HDA_POWER_SAVE
85 struct hda_loopback_check loopback;
86#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +010087 /* for virtual master */
88 hda_nid_t vmaster_nid;
Takashi Iwaiea734962011-01-17 11:29:34 +010089 const char * const *slave_vols;
90 const char * const *slave_sws;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091};
92
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020093/*
94 * input MUX handling (common part)
95 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +010096static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +020097{
98 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
99 struct ad198x_spec *spec = codec->spec;
100
101 return snd_hda_input_mux_info(spec->input_mux, uinfo);
102}
103
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100104static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200105{
106 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
107 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100108 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200109
Takashi Iwai985be542005-11-02 18:26:49 +0100110 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200111 return 0;
112}
113
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100114static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200115{
116 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
117 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100118 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200119
120 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
Takashi Iwai2e5b9562005-11-21 16:36:15 +0100121 spec->capsrc_nids[adc_idx],
122 &spec->cur_mux[adc_idx]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200123}
124
125/*
126 * initialization (common callbacks)
127 */
128static int ad198x_init(struct hda_codec *codec)
129{
130 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100131 int i;
132
133 for (i = 0; i < spec->num_init_verbs; i++)
134 snd_hda_sequence_write(codec, spec->init_verbs[i]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200135 return 0;
136}
137
Takashi Iwaiea734962011-01-17 11:29:34 +0100138static const char * const ad_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100139 "Front Playback Volume",
140 "Surround Playback Volume",
141 "Center Playback Volume",
142 "LFE Playback Volume",
143 "Side Playback Volume",
144 "Headphone Playback Volume",
145 "Mono Playback Volume",
Takashi Iwai628ed132008-01-25 11:56:57 +0100146 "Speaker Playback Volume",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100147 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100148 NULL
149};
150
Takashi Iwaiea734962011-01-17 11:29:34 +0100151static const char * const ad_slave_sws[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +0100152 "Front Playback Switch",
153 "Surround Playback Switch",
154 "Center Playback Switch",
155 "LFE Playback Switch",
156 "Side Playback Switch",
157 "Headphone Playback Switch",
158 "Mono Playback Switch",
Takashi Iwai628ed132008-01-25 11:56:57 +0100159 "Speaker Playback Switch",
Takashi Iwai4806ef02008-01-26 09:58:13 +0100160 "IEC958 Playback Switch",
Takashi Iwai2134ea42008-01-10 16:53:55 +0100161 NULL
162};
163
Takashi Iwaiea734962011-01-17 11:29:34 +0100164static const char * const ad1988_6stack_fp_slave_vols[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100165 "Front Playback Volume",
166 "Surround Playback Volume",
167 "Center Playback Volume",
168 "LFE Playback Volume",
169 "Side Playback Volume",
170 "IEC958 Playback Volume",
171 NULL
172};
173
Takashi Iwaiea734962011-01-17 11:29:34 +0100174static const char * const ad1988_6stack_fp_slave_sws[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100175 "Front Playback Switch",
176 "Surround Playback Switch",
177 "Center Playback Switch",
178 "LFE Playback Switch",
179 "Side Playback Switch",
180 "IEC958 Playback Switch",
181 NULL
182};
Takashi Iwai603c4012008-07-30 15:01:44 +0200183static void ad198x_free_kctls(struct hda_codec *codec);
184
Takashi Iwai67d634c2009-11-16 15:35:59 +0100185#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100186/* additional beep mixers; the actual parameters are overwritten at build */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200187static const struct snd_kcontrol_new ad_beep_mixer[] = {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100188 HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
Jaroslav Kysela123c07a2009-10-21 14:48:23 +0200189 HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100190 { } /* end */
191};
192
Takashi Iwai498f5b12011-05-02 11:33:15 +0200193static const struct snd_kcontrol_new ad_beep2_mixer[] = {
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100194 HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
195 HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
196 { } /* end */
197};
198
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100199#define set_beep_amp(spec, nid, idx, dir) \
200 ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100201#else
202#define set_beep_amp(spec, nid, idx, dir) /* NOP */
203#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100204
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200205static int ad198x_build_controls(struct hda_codec *codec)
206{
207 struct ad198x_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100208 struct snd_kcontrol *kctl;
Takashi Iwai985be542005-11-02 18:26:49 +0100209 unsigned int i;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200210 int err;
211
Takashi Iwai985be542005-11-02 18:26:49 +0100212 for (i = 0; i < spec->num_mixers; i++) {
213 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
214 if (err < 0)
215 return err;
216 }
217 if (spec->multiout.dig_out_nid) {
Stephen Warren74b654c2011-06-01 11:14:18 -0600218 err = snd_hda_create_spdif_out_ctls(codec,
219 spec->multiout.dig_out_nid,
220 spec->multiout.dig_out_nid);
Takashi Iwai985be542005-11-02 18:26:49 +0100221 if (err < 0)
222 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +0100223 err = snd_hda_create_spdif_share_sw(codec,
224 &spec->multiout);
225 if (err < 0)
226 return err;
227 spec->multiout.share_spdif = 1;
Takashi Iwai985be542005-11-02 18:26:49 +0100228 }
229 if (spec->dig_in_nid) {
230 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
231 if (err < 0)
232 return err;
233 }
Takashi Iwai2134ea42008-01-10 16:53:55 +0100234
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100235 /* create beep controls if needed */
Takashi Iwai67d634c2009-11-16 15:35:59 +0100236#ifdef CONFIG_SND_HDA_INPUT_BEEP
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100237 if (spec->beep_amp) {
Takashi Iwai498f5b12011-05-02 11:33:15 +0200238 const struct snd_kcontrol_new *knew;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +0100239 knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
240 for ( ; knew->name; knew++) {
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100241 struct snd_kcontrol *kctl;
242 kctl = snd_ctl_new1(knew, codec);
243 if (!kctl)
244 return -ENOMEM;
245 kctl->private_value = spec->beep_amp;
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100246 err = snd_hda_ctl_add(codec, 0, kctl);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100247 if (err < 0)
248 return err;
249 }
250 }
Takashi Iwai67d634c2009-11-16 15:35:59 +0100251#endif
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100252
Takashi Iwai2134ea42008-01-10 16:53:55 +0100253 /* if we have no master control, let's create it */
254 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100255 unsigned int vmaster_tlv[4];
Takashi Iwai2134ea42008-01-10 16:53:55 +0100256 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100257 HDA_OUTPUT, vmaster_tlv);
Takashi Iwai2134ea42008-01-10 16:53:55 +0100258 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai1c82ed12008-02-18 13:05:50 +0100259 vmaster_tlv,
Takashi Iwai2134ea42008-01-10 16:53:55 +0100260 (spec->slave_vols ?
261 spec->slave_vols : ad_slave_vols));
262 if (err < 0)
263 return err;
264 }
265 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
266 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
267 NULL,
268 (spec->slave_sws ?
269 spec->slave_sws : ad_slave_sws));
270 if (err < 0)
271 return err;
272 }
273
Takashi Iwai603c4012008-07-30 15:01:44 +0200274 ad198x_free_kctls(codec); /* no longer needed */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100275
276 /* assign Capture Source enums to NID */
277 kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
278 if (!kctl)
279 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
280 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +0100281 err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100282 if (err < 0)
283 return err;
284 }
285
286 /* assign IEC958 enums to NID */
287 kctl = snd_hda_find_mixer_ctl(codec,
288 SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
289 if (kctl) {
290 err = snd_hda_add_nid(codec, kctl, 0,
291 spec->multiout.dig_out_nid);
292 if (err < 0)
293 return err;
294 }
295
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200296 return 0;
297}
298
Takashi Iwaicb53c622007-08-10 17:21:45 +0200299#ifdef CONFIG_SND_HDA_POWER_SAVE
300static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
301{
302 struct ad198x_spec *spec = codec->spec;
303 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
304}
305#endif
306
Raymond Yau34588702011-09-23 19:03:25 +0800307static void activate_ctl(struct hda_codec *codec, const char *name, int active)
308{
309 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
310 if (ctl) {
311 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
312 ctl->vd[0].access |= active ? 0 :
313 SNDRV_CTL_ELEM_ACCESS_INACTIVE;
314 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
315 ctl->vd[0].access |= active ?
316 SNDRV_CTL_ELEM_ACCESS_WRITE : 0;
317 snd_ctl_notify(codec->bus->card,
318 SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
319 }
320}
321
322static void set_stream_active(struct hda_codec *codec, bool active)
323{
324 struct ad198x_spec *spec = codec->spec;
325 if (active)
326 spec->num_active_streams++;
327 else
328 spec->num_active_streams--;
329 activate_ctl(codec, "Independent HP", spec->num_active_streams == 0);
330}
331
332static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol,
333 struct snd_ctl_elem_info *uinfo)
334{
335 static const char * const texts[] = { "OFF", "ON", NULL};
336 int index;
337 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
338 uinfo->count = 1;
339 uinfo->value.enumerated.items = 2;
340 index = uinfo->value.enumerated.item;
341 if (index >= 2)
342 index = 1;
343 strcpy(uinfo->value.enumerated.name, texts[index]);
344 return 0;
345}
346
347static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol,
348 struct snd_ctl_elem_value *ucontrol)
349{
350 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
351 struct ad198x_spec *spec = codec->spec;
352 ucontrol->value.enumerated.item[0] = spec->independent_hp;
353 return 0;
354}
355
356static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol,
357 struct snd_ctl_elem_value *ucontrol)
358{
359 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
360 struct ad198x_spec *spec = codec->spec;
361 unsigned int select = ucontrol->value.enumerated.item[0];
362 if (spec->independent_hp != select) {
363 spec->independent_hp = select;
364 if (spec->independent_hp)
365 spec->multiout.hp_nid = 0;
366 else
367 spec->multiout.hp_nid = spec->alt_dac_nid[0];
368 return 1;
369 }
370 return 0;
371}
372
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200373/*
374 * Analog playback callbacks
375 */
376static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
377 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100378 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200379{
380 struct ad198x_spec *spec = codec->spec;
Raymond Yau34588702011-09-23 19:03:25 +0800381 int err;
382 set_stream_active(codec, true);
383 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
Takashi Iwai9a081602008-02-12 18:37:26 +0100384 hinfo);
Raymond Yau34588702011-09-23 19:03:25 +0800385 if (err < 0) {
386 set_stream_active(codec, false);
387 return err;
388 }
389 return 0;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200390}
391
392static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
393 struct hda_codec *codec,
394 unsigned int stream_tag,
395 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100396 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200397{
398 struct ad198x_spec *spec = codec->spec;
399 return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
400 format, substream);
401}
402
403static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
404 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100405 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200406{
407 struct ad198x_spec *spec = codec->spec;
408 return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
409}
410
Raymond Yau34588702011-09-23 19:03:25 +0800411static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo,
412 struct hda_codec *codec,
413 struct snd_pcm_substream *substream)
414{
415 set_stream_active(codec, false);
416 return 0;
417}
418
419static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
420 struct hda_codec *codec,
421 struct snd_pcm_substream *substream)
422{
423 struct ad198x_spec *spec = codec->spec;
424 if (!spec->independent_hp)
425 return -EBUSY;
426 set_stream_active(codec, true);
427 return 0;
428}
429
430static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
431 struct hda_codec *codec,
432 struct snd_pcm_substream *substream)
433{
434 set_stream_active(codec, false);
435 return 0;
436}
437
Takashi Iwai498f5b12011-05-02 11:33:15 +0200438static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
Raymond Yauc66ddf32011-01-17 11:19:03 +0100439 .substreams = 1,
440 .channels_min = 2,
441 .channels_max = 2,
Raymond Yau34588702011-09-23 19:03:25 +0800442 .ops = {
443 .open = ad1988_alt_playback_pcm_open,
444 .close = ad1988_alt_playback_pcm_close
445 },
Raymond Yauc66ddf32011-01-17 11:19:03 +0100446};
447
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200448/*
449 * Digital out
450 */
451static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
452 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100453 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200454{
455 struct ad198x_spec *spec = codec->spec;
456 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
457}
458
459static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
460 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100461 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200462{
463 struct ad198x_spec *spec = codec->spec;
464 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
465}
466
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200467static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
468 struct hda_codec *codec,
469 unsigned int stream_tag,
470 unsigned int format,
471 struct snd_pcm_substream *substream)
472{
473 struct ad198x_spec *spec = codec->spec;
474 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
475 format, substream);
476}
477
Takashi Iwai9411e212009-02-13 11:32:28 +0100478static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
479 struct hda_codec *codec,
480 struct snd_pcm_substream *substream)
481{
482 struct ad198x_spec *spec = codec->spec;
483 return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
484}
485
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200486/*
487 * Analog capture
488 */
489static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
490 struct hda_codec *codec,
491 unsigned int stream_tag,
492 unsigned int format,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100493 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200494{
495 struct ad198x_spec *spec = codec->spec;
Takashi Iwai985be542005-11-02 18:26:49 +0100496 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
497 stream_tag, 0, format);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200498 return 0;
499}
500
501static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
502 struct hda_codec *codec,
Takashi Iwaic8b6bf92005-11-17 14:57:47 +0100503 struct snd_pcm_substream *substream)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200504{
505 struct ad198x_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +0100506 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200507 return 0;
508}
509
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200510/*
511 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200512static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200513 .substreams = 1,
514 .channels_min = 2,
Takashi Iwai985be542005-11-02 18:26:49 +0100515 .channels_max = 6, /* changed later */
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200516 .nid = 0, /* fill later */
517 .ops = {
518 .open = ad198x_playback_pcm_open,
519 .prepare = ad198x_playback_pcm_prepare,
Raymond Yau34588702011-09-23 19:03:25 +0800520 .cleanup = ad198x_playback_pcm_cleanup,
521 .close = ad198x_playback_pcm_close
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200522 },
523};
524
Takashi Iwai498f5b12011-05-02 11:33:15 +0200525static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100526 .substreams = 1,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200527 .channels_min = 2,
528 .channels_max = 2,
529 .nid = 0, /* fill later */
530 .ops = {
531 .prepare = ad198x_capture_pcm_prepare,
532 .cleanup = ad198x_capture_pcm_cleanup
533 },
534};
535
Takashi Iwai498f5b12011-05-02 11:33:15 +0200536static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200537 .substreams = 1,
538 .channels_min = 2,
539 .channels_max = 2,
540 .nid = 0, /* fill later */
541 .ops = {
542 .open = ad198x_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +0200543 .close = ad198x_dig_playback_pcm_close,
Takashi Iwai9411e212009-02-13 11:32:28 +0100544 .prepare = ad198x_dig_playback_pcm_prepare,
545 .cleanup = ad198x_dig_playback_pcm_cleanup
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200546 },
547};
548
Takashi Iwai498f5b12011-05-02 11:33:15 +0200549static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
Takashi Iwai985be542005-11-02 18:26:49 +0100550 .substreams = 1,
551 .channels_min = 2,
552 .channels_max = 2,
553 /* NID is set in alc_build_pcms */
554};
555
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200556static int ad198x_build_pcms(struct hda_codec *codec)
557{
558 struct ad198x_spec *spec = codec->spec;
559 struct hda_pcm *info = spec->pcm_rec;
560
561 codec->num_pcms = 1;
562 codec->pcm_info = info;
563
564 info->name = "AD198x Analog";
565 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
566 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
567 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
568 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
Takashi Iwai985be542005-11-02 18:26:49 +0100569 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
570 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200571
572 if (spec->multiout.dig_out_nid) {
573 info++;
574 codec->num_pcms++;
575 info->name = "AD198x Digital";
Takashi Iwai7ba72ba2008-02-06 14:03:20 +0100576 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200577 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
578 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
Takashi Iwai985be542005-11-02 18:26:49 +0100579 if (spec->dig_in_nid) {
580 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
581 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
582 }
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200583 }
584
Raymond Yauc66ddf32011-01-17 11:19:03 +0100585 if (spec->alt_dac_nid && spec->stream_analog_alt_playback) {
586 codec->num_pcms++;
587 info = spec->pcm_rec + 2;
588 info->name = "AD198x Headphone";
589 info->pcm_type = HDA_PCM_TYPE_AUDIO;
590 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
591 *spec->stream_analog_alt_playback;
592 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
593 spec->alt_dac_nid[0];
594 }
595
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200596 return 0;
597}
598
Takashi Iwai603c4012008-07-30 15:01:44 +0200599static void ad198x_free_kctls(struct hda_codec *codec)
600{
601 struct ad198x_spec *spec = codec->spec;
602
603 if (spec->kctls.list) {
604 struct snd_kcontrol_new *kctl = spec->kctls.list;
605 int i;
606 for (i = 0; i < spec->kctls.used; i++)
607 kfree(kctl[i].name);
608 }
609 snd_array_free(&spec->kctls);
610}
611
Daniel T Chenea52bf22009-12-27 18:48:29 -0500612static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
613 hda_nid_t hp)
614{
615 struct ad198x_spec *spec = codec->spec;
Raymond Yaua01ef052011-06-01 15:09:48 +0800616 if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
617 snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500618 !spec->inv_eapd ? 0x00 : 0x02);
Raymond Yaua01ef052011-06-01 15:09:48 +0800619 if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
620 snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500621 !spec->inv_eapd ? 0x00 : 0x02);
622}
623
624static void ad198x_power_eapd(struct hda_codec *codec)
625{
626 /* We currently only handle front, HP */
627 switch (codec->vendor_id) {
628 case 0x11d41882:
629 case 0x11d4882a:
630 case 0x11d41884:
631 case 0x11d41984:
632 case 0x11d41883:
633 case 0x11d4184a:
634 case 0x11d4194a:
635 case 0x11d4194b:
Takashi Iwai4dffbe02011-06-03 10:05:02 +0200636 case 0x11d41988:
637 case 0x11d4198b:
638 case 0x11d4989a:
639 case 0x11d4989b:
Daniel T Chenea52bf22009-12-27 18:48:29 -0500640 ad198x_power_eapd_write(codec, 0x12, 0x11);
641 break;
642 case 0x11d41981:
643 case 0x11d41983:
644 ad198x_power_eapd_write(codec, 0x05, 0x06);
645 break;
646 case 0x11d41986:
647 ad198x_power_eapd_write(codec, 0x1b, 0x1a);
648 break;
Daniel T Chenea52bf22009-12-27 18:48:29 -0500649 }
650}
651
Takashi Iwai0da26922011-04-26 15:18:33 +0200652static void ad198x_shutup(struct hda_codec *codec)
653{
654 snd_hda_shutup_pins(codec);
655 ad198x_power_eapd(codec);
656}
657
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200658static void ad198x_free(struct hda_codec *codec)
659{
Takashi Iwaid32410b12005-11-24 16:06:23 +0100660 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +0100661
Takashi Iwai603c4012008-07-30 15:01:44 +0200662 if (!spec)
663 return;
664
Daniel T Chenea52bf22009-12-27 18:48:29 -0500665 ad198x_shutup(codec);
Takashi Iwai603c4012008-07-30 15:01:44 +0200666 ad198x_free_kctls(codec);
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100667 kfree(spec);
668 snd_hda_detach_beep_device(codec);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200669}
670
Takashi Iwai2a439522011-07-26 09:52:50 +0200671#ifdef CONFIG_PM
Daniel T Chenea52bf22009-12-27 18:48:29 -0500672static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
673{
674 ad198x_shutup(codec);
Daniel T Chenea52bf22009-12-27 18:48:29 -0500675 return 0;
676}
Daniel T Chenea52bf22009-12-27 18:48:29 -0500677#endif
678
Takashi Iwai498f5b12011-05-02 11:33:15 +0200679static const struct hda_codec_ops ad198x_patch_ops = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200680 .build_controls = ad198x_build_controls,
681 .build_pcms = ad198x_build_pcms,
682 .init = ad198x_init,
683 .free = ad198x_free,
Takashi Iwaicb53c622007-08-10 17:21:45 +0200684#ifdef CONFIG_SND_HDA_POWER_SAVE
685 .check_power_status = ad198x_check_power_status,
686#endif
Takashi Iwai2a439522011-07-26 09:52:50 +0200687#ifdef CONFIG_PM
Daniel T Chenea52bf22009-12-27 18:48:29 -0500688 .suspend = ad198x_suspend,
Daniel T Chenea52bf22009-12-27 18:48:29 -0500689#endif
690 .reboot_notify = ad198x_shutup,
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200691};
692
693
694/*
Takashi Iwai18a815d2006-03-01 19:54:39 +0100695 * EAPD control
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100696 * the private value = nid
Takashi Iwai18a815d2006-03-01 19:54:39 +0100697 */
Takashi Iwaia5ce8892007-07-23 15:42:26 +0200698#define ad198x_eapd_info snd_ctl_boolean_mono_info
Takashi Iwai18a815d2006-03-01 19:54:39 +0100699
700static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
701 struct snd_ctl_elem_value *ucontrol)
702{
703 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
704 struct ad198x_spec *spec = codec->spec;
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100705 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100706 ucontrol->value.integer.value[0] = ! spec->cur_eapd;
707 else
708 ucontrol->value.integer.value[0] = spec->cur_eapd;
709 return 0;
710}
711
712static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
713 struct snd_ctl_elem_value *ucontrol)
714{
715 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
716 struct ad198x_spec *spec = codec->spec;
Takashi Iwai18a815d2006-03-01 19:54:39 +0100717 hda_nid_t nid = kcontrol->private_value & 0xff;
718 unsigned int eapd;
Takashi Iwai68ea7b22007-11-15 15:54:38 +0100719 eapd = !!ucontrol->value.integer.value[0];
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100720 if (spec->inv_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100721 eapd = !eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200722 if (eapd == spec->cur_eapd)
Takashi Iwai18a815d2006-03-01 19:54:39 +0100723 return 0;
724 spec->cur_eapd = eapd;
Takashi Iwai82beb8f2007-08-10 17:09:26 +0200725 snd_hda_codec_write_cache(codec, nid,
726 0, AC_VERB_SET_EAPD_BTLENABLE,
727 eapd ? 0x02 : 0x00);
Takashi Iwai18a815d2006-03-01 19:54:39 +0100728 return 1;
729}
730
Takashi Iwai9230d212006-03-13 13:49:49 +0100731static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
732 struct snd_ctl_elem_info *uinfo);
733static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
734 struct snd_ctl_elem_value *ucontrol);
735static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
736 struct snd_ctl_elem_value *ucontrol);
737
738
Takashi Iwai18a815d2006-03-01 19:54:39 +0100739/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200740 * AD1986A specific
741 */
742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743#define AD1986A_SPDIF_OUT 0x02
744#define AD1986A_FRONT_DAC 0x03
745#define AD1986A_SURR_DAC 0x04
746#define AD1986A_CLFE_DAC 0x05
747#define AD1986A_ADC 0x06
748
Takashi Iwai498f5b12011-05-02 11:33:15 +0200749static const hda_nid_t ad1986a_dac_nids[3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
751};
Takashi Iwai498f5b12011-05-02 11:33:15 +0200752static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
753static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Takashi Iwai498f5b12011-05-02 11:33:15 +0200755static const struct hda_input_mux ad1986a_capture_source = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 .num_items = 7,
757 .items = {
758 { "Mic", 0x0 },
759 { "CD", 0x1 },
760 { "Aux", 0x3 },
761 { "Line", 0x4 },
762 { "Mix", 0x5 },
763 { "Mono", 0x6 },
764 { "Phone", 0x7 },
765 },
766};
767
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Takashi Iwai498f5b12011-05-02 11:33:15 +0200769static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200770 .ops = &snd_hda_bind_vol,
771 .values = {
772 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
773 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
774 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
775 0
776 },
777};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
Takashi Iwai498f5b12011-05-02 11:33:15 +0200779static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200780 .ops = &snd_hda_bind_sw,
781 .values = {
782 HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
783 HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
784 HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
785 0
786 },
787};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
789/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 * mixers
791 */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200792static const struct snd_kcontrol_new ad1986a_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200793 /*
794 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
795 */
796 HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
797 HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
799 HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
800 HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
801 HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
802 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
803 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
804 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
805 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
806 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
807 HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
808 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
809 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
810 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
811 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
812 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
813 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
814 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
815 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100816 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
818 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
819 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
820 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
821 {
822 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
823 .name = "Capture Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +0200824 .info = ad198x_mux_enum_info,
825 .get = ad198x_mux_enum_get,
826 .put = ad198x_mux_enum_put,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 },
828 HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
829 { } /* end */
830};
831
Takashi Iwai9230d212006-03-13 13:49:49 +0100832/* additional mixers for 3stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200833static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100834 {
835 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
836 .name = "Channel Mode",
837 .info = ad198x_ch_mode_info,
838 .get = ad198x_ch_mode_get,
839 .put = ad198x_ch_mode_put,
840 },
841 { } /* end */
842};
843
844/* laptop model - 2ch only */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200845static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
Takashi Iwai9230d212006-03-13 13:49:49 +0100846
Takashi Iwai20a45e82007-08-15 22:20:45 +0200847/* master controls both pins 0x1a and 0x1b */
Takashi Iwai498f5b12011-05-02 11:33:15 +0200848static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200849 .ops = &snd_hda_bind_vol,
850 .values = {
851 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
852 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
853 0,
854 },
855};
856
Takashi Iwai498f5b12011-05-02 11:33:15 +0200857static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
Takashi Iwai20a45e82007-08-15 22:20:45 +0200858 .ops = &snd_hda_bind_sw,
859 .values = {
860 HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
861 HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
862 0,
863 },
864};
865
Takashi Iwai498f5b12011-05-02 11:33:15 +0200866static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +0100867 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
868 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai20a45e82007-08-15 22:20:45 +0200869 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
870 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai9230d212006-03-13 13:49:49 +0100871 HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
872 HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
873 HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
874 HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
875 HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
876 HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
877 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
878 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100879 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +0100880 /*
Takashi Iwai9230d212006-03-13 13:49:49 +0100881 HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
882 HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
883 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
884 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
885 {
886 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
887 .name = "Capture Source",
888 .info = ad198x_mux_enum_info,
889 .get = ad198x_mux_enum_get,
890 .put = ad198x_mux_enum_put,
891 },
892 { } /* end */
893};
894
Takashi Iwai825aa9722006-03-17 10:50:49 +0100895/* laptop-eapd model - 2ch only */
896
Takashi Iwai498f5b12011-05-02 11:33:15 +0200897static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100898 .num_items = 3,
899 .items = {
900 { "Mic", 0x0 },
901 { "Internal Mic", 0x4 },
902 { "Mix", 0x5 },
903 },
904};
905
Takashi Iwai498f5b12011-05-02 11:33:15 +0200906static const struct hda_input_mux ad1986a_automic_capture_source = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100907 .num_items = 2,
908 .items = {
909 { "Mic", 0x0 },
910 { "Mix", 0x5 },
911 },
912};
913
Takashi Iwai498f5b12011-05-02 11:33:15 +0200914static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
Takashi Iwai532d5382007-07-27 19:02:40 +0200915 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
916 HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
Takashi Iwai16d11a82009-06-24 14:07:53 +0200917 { } /* end */
918};
919
Takashi Iwai498f5b12011-05-02 11:33:15 +0200920static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
Takashi Iwai825aa9722006-03-17 10:50:49 +0100921 HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
922 HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100923 HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
924 HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +0100925 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
Takashi Iwai1725b822008-11-21 02:25:48 +0100926 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
927 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
928 {
929 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
930 .name = "Capture Source",
931 .info = ad198x_mux_enum_info,
932 .get = ad198x_mux_enum_get,
933 .put = ad198x_mux_enum_put,
934 },
935 {
936 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
937 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100938 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwai1725b822008-11-21 02:25:48 +0100939 .info = ad198x_eapd_info,
940 .get = ad198x_eapd_get,
941 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +0100942 .private_value = 0x1b, /* port-D */
Takashi Iwai1725b822008-11-21 02:25:48 +0100943 },
944 { } /* end */
945};
946
Takashi Iwai498f5b12011-05-02 11:33:15 +0200947static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
Takashi Iwai16d11a82009-06-24 14:07:53 +0200948 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
949 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
Takashi Iwai825aa9722006-03-17 10:50:49 +0100950 { } /* end */
951};
952
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100953/* re-connect the mic boost input according to the jack sensing */
954static void ad1986a_automic(struct hda_codec *codec)
955{
956 unsigned int present;
Takashi Iwaid56757a2009-11-18 08:00:14 +0100957 present = snd_hda_jack_detect(codec, 0x1f);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100958 /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
959 snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
Takashi Iwaid56757a2009-11-18 08:00:14 +0100960 present ? 0 : 2);
Takashi Iwai5d5d5f42008-02-12 12:11:36 +0100961}
962
963#define AD1986A_MIC_EVENT 0x36
964
965static void ad1986a_automic_unsol_event(struct hda_codec *codec,
966 unsigned int res)
967{
968 if ((res >> 26) != AD1986A_MIC_EVENT)
969 return;
970 ad1986a_automic(codec);
971}
972
973static int ad1986a_automic_init(struct hda_codec *codec)
974{
975 ad198x_init(codec);
976 ad1986a_automic(codec);
977 return 0;
978}
979
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200980/* laptop-automute - 2ch only */
981
982static void ad1986a_update_hp(struct hda_codec *codec)
983{
984 struct ad198x_spec *spec = codec->spec;
985 unsigned int mute;
986
987 if (spec->jack_present)
988 mute = HDA_AMP_MUTE; /* mute internal speaker */
989 else
990 /* unmute internal speaker if necessary */
991 mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
992 snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
993 HDA_AMP_MUTE, mute);
994}
995
996static void ad1986a_hp_automute(struct hda_codec *codec)
997{
998 struct ad198x_spec *spec = codec->spec;
Takashi Iwai8ab78c72007-09-06 14:29:53 +0200999
Takashi Iwaid56757a2009-11-18 08:00:14 +01001000 spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
Takashi Iwai03c405a2009-06-24 14:10:15 +02001001 if (spec->inv_jack_detect)
1002 spec->jack_present = !spec->jack_present;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001003 ad1986a_update_hp(codec);
1004}
1005
1006#define AD1986A_HP_EVENT 0x37
1007
1008static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
1009{
1010 if ((res >> 26) != AD1986A_HP_EVENT)
1011 return;
1012 ad1986a_hp_automute(codec);
1013}
1014
1015static int ad1986a_hp_init(struct hda_codec *codec)
1016{
1017 ad198x_init(codec);
1018 ad1986a_hp_automute(codec);
1019 return 0;
1020}
1021
1022/* bind hp and internal speaker mute (with plug check) */
1023static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1024 struct snd_ctl_elem_value *ucontrol)
1025{
1026 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1027 long *valp = ucontrol->value.integer.value;
1028 int change;
1029
1030 change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
1031 HDA_AMP_MUTE,
1032 valp[0] ? 0 : HDA_AMP_MUTE);
1033 change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
1034 HDA_AMP_MUTE,
1035 valp[1] ? 0 : HDA_AMP_MUTE);
1036 if (change)
1037 ad1986a_update_hp(codec);
1038 return change;
1039}
1040
Takashi Iwai498f5b12011-05-02 11:33:15 +02001041static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001042 HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
1043 {
1044 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1045 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01001046 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001047 .info = snd_hda_mixer_amp_switch_info,
1048 .get = snd_hda_mixer_amp_switch_get,
1049 .put = ad1986a_hp_master_sw_put,
1050 .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
1051 },
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001052 { } /* end */
1053};
1054
Takashi Iwai16d11a82009-06-24 14:07:53 +02001055
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056/*
1057 * initialization verbs
1058 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001059static const struct hda_verb ad1986a_init_verbs[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 /* Front, Surround, CLFE DAC; mute as default */
1061 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1062 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1063 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1064 /* Downmix - off */
1065 {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1066 /* HP, Line-Out, Surround, CLFE selectors */
1067 {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
1068 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
1069 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1070 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1071 /* Mono selector */
1072 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
1073 /* Mic selector: Mic 1/2 pin */
1074 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1075 /* Line-in selector: Line-in */
1076 {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
1077 /* Mic 1/2 swap */
1078 {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
1079 /* Record selector: mic */
1080 {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
1081 /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
1082 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1083 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1084 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1085 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1086 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1087 /* PC beep */
1088 {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
1089 /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
1090 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1091 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1092 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1093 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1094 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001095 /* HP Pin */
1096 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1097 /* Front, Surround, CLFE Pins */
1098 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1099 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1100 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1101 /* Mono Pin */
1102 {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1103 /* Mic Pin */
1104 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1105 /* Line, Aux, CD, Beep-In Pin */
1106 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1107 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1108 {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1109 {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1110 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 { } /* end */
1112};
1113
Takashi Iwai498f5b12011-05-02 11:33:15 +02001114static const struct hda_verb ad1986a_ch2_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001115 /* Surround out -> Line In */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001116 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
1117 /* Line-in selectors */
1118 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001119 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001120 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1121 /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
1122 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001123 { } /* end */
1124};
1125
Takashi Iwai498f5b12011-05-02 11:33:15 +02001126static const struct hda_verb ad1986a_ch4_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001127 /* Surround out -> Surround */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001128 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1129 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001130 /* CLFE -> Mic in */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001131 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
1132 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001133 { } /* end */
1134};
1135
Takashi Iwai498f5b12011-05-02 11:33:15 +02001136static const struct hda_verb ad1986a_ch6_init[] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001137 /* Surround out -> Surround out */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001138 { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1139 { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001140 /* CLFE -> CLFE */
Takashi Iwaifb956c12007-04-18 23:03:56 +02001141 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
1142 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
Takashi Iwai9230d212006-03-13 13:49:49 +01001143 { } /* end */
1144};
1145
Takashi Iwai498f5b12011-05-02 11:33:15 +02001146static const struct hda_channel_mode ad1986a_modes[3] = {
Takashi Iwai9230d212006-03-13 13:49:49 +01001147 { 2, ad1986a_ch2_init },
1148 { 4, ad1986a_ch4_init },
1149 { 6, ad1986a_ch6_init },
1150};
1151
Takashi Iwai825aa9722006-03-17 10:50:49 +01001152/* eapd initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001153static const struct hda_verb ad1986a_eapd_init_verbs[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001154 {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
Takashi Iwai825aa9722006-03-17 10:50:49 +01001155 {}
1156};
1157
Takashi Iwai498f5b12011-05-02 11:33:15 +02001158static const struct hda_verb ad1986a_automic_verbs[] = {
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001159 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1160 {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1161 /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
1162 {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
1163 {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
1164 {}
1165};
1166
Tobin Davisf36090f2007-01-08 11:07:12 +01001167/* Ultra initialization */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001168static const struct hda_verb ad1986a_ultra_init[] = {
Tobin Davisf36090f2007-01-08 11:07:12 +01001169 /* eapd initialization */
1170 { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
1171 /* CLFE -> Mic in */
1172 { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
1173 { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1174 { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
1175 { } /* end */
1176};
1177
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001178/* pin sensing on HP jack */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001179static const struct hda_verb ad1986a_hp_init_verbs[] = {
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001180 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
1181 {}
1182};
1183
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001184static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
1185 unsigned int res)
1186{
1187 switch (res >> 26) {
1188 case AD1986A_HP_EVENT:
1189 ad1986a_hp_automute(codec);
1190 break;
1191 case AD1986A_MIC_EVENT:
1192 ad1986a_automic(codec);
1193 break;
1194 }
1195}
1196
1197static int ad1986a_samsung_p50_init(struct hda_codec *codec)
1198{
1199 ad198x_init(codec);
1200 ad1986a_hp_automute(codec);
1201 ad1986a_automic(codec);
1202 return 0;
1203}
1204
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001205
Takashi Iwai9230d212006-03-13 13:49:49 +01001206/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001207enum {
1208 AD1986A_6STACK,
1209 AD1986A_3STACK,
1210 AD1986A_LAPTOP,
1211 AD1986A_LAPTOP_EAPD,
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001212 AD1986A_LAPTOP_AUTOMUTE,
Tobin Davisf36090f2007-01-08 11:07:12 +01001213 AD1986A_ULTRA,
Takashi Iwai1725b822008-11-21 02:25:48 +01001214 AD1986A_SAMSUNG,
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001215 AD1986A_SAMSUNG_P50,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001216 AD1986A_MODELS
1217};
Takashi Iwai9230d212006-03-13 13:49:49 +01001218
Takashi Iwaiea734962011-01-17 11:29:34 +01001219static const char * const ad1986a_models[AD1986A_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001220 [AD1986A_6STACK] = "6stack",
1221 [AD1986A_3STACK] = "3stack",
1222 [AD1986A_LAPTOP] = "laptop",
1223 [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001224 [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
Tobin Davisf36090f2007-01-08 11:07:12 +01001225 [AD1986A_ULTRA] = "ultra",
Takashi Iwai1725b822008-11-21 02:25:48 +01001226 [AD1986A_SAMSUNG] = "samsung",
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001227 [AD1986A_SAMSUNG_P50] = "samsung-p50",
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001228};
1229
Takashi Iwai498f5b12011-05-02 11:33:15 +02001230static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001231 SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001232 SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001233 SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001234 SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001235 SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
1236 SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
1237 SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
1238 SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
Tobin Davisd9f9b8b2007-11-05 15:13:51 +01001239 SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
Tobin Davis658fba02007-04-23 16:41:12 +02001240 SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001241 SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
1242 SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
1243 SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
1244 SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
1245 SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001246 SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
Daniel T Chenba579eb2010-02-20 11:16:30 -05001247 SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001248 SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001249 SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001250 SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
Tobin Davisf36090f2007-01-08 11:07:12 +01001251 SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
Takashi Iwaidea0a502009-02-09 17:14:52 +01001252 SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001253 SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
Tobin Davis18768992007-03-12 22:20:51 +01001254 SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001255 SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001256 SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001257 SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
Takashi Iwai9230d212006-03-13 13:49:49 +01001258 {}
1259};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
Takashi Iwaicb53c622007-08-10 17:21:45 +02001261#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001262static const struct hda_amp_list ad1986a_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001263 { 0x13, HDA_OUTPUT, 0 }, /* Mic */
1264 { 0x14, HDA_OUTPUT, 0 }, /* Phone */
1265 { 0x15, HDA_OUTPUT, 0 }, /* CD */
1266 { 0x16, HDA_OUTPUT, 0 }, /* Aux */
1267 { 0x17, HDA_OUTPUT, 0 }, /* Line */
1268 { } /* end */
1269};
1270#endif
1271
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001272static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
1273{
Takashi Iwai2f334f92009-02-20 14:37:42 +01001274 unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001275 return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
1276}
1277
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278static int patch_ad1986a(struct hda_codec *codec)
1279{
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001280 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001281 int err, board_config;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001283 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 if (spec == NULL)
1285 return -ENOMEM;
1286
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 codec->spec = spec;
1288
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001289 err = snd_hda_attach_beep_device(codec, 0x19);
1290 if (err < 0) {
1291 ad198x_free(codec);
1292 return err;
1293 }
1294 set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
1295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 spec->multiout.max_channels = 6;
1297 spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
1298 spec->multiout.dac_nids = ad1986a_dac_nids;
1299 spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001300 spec->num_adc_nids = 1;
1301 spec->adc_nids = ad1986a_adc_nids;
Takashi Iwaia7ee8202006-03-01 20:05:39 +01001302 spec->capsrc_nids = ad1986a_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001303 spec->input_mux = &ad1986a_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001304 spec->num_mixers = 1;
1305 spec->mixers[0] = ad1986a_mixers;
1306 spec->num_init_verbs = 1;
1307 spec->init_verbs[0] = ad1986a_init_verbs;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001308#ifdef CONFIG_SND_HDA_POWER_SAVE
1309 spec->loopback.amplist = ad1986a_loopbacks;
1310#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001311 spec->vmaster_nid = 0x1b;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01001312 spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001314 codec->patch_ops = ad198x_patch_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
Takashi Iwai9230d212006-03-13 13:49:49 +01001316 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001317 board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
1318 ad1986a_models,
1319 ad1986a_cfg_tbl);
Takashi Iwai9230d212006-03-13 13:49:49 +01001320 switch (board_config) {
1321 case AD1986A_3STACK:
1322 spec->num_mixers = 2;
1323 spec->mixers[1] = ad1986a_3st_mixers;
Takashi Iwaifb956c12007-04-18 23:03:56 +02001324 spec->num_init_verbs = 2;
1325 spec->init_verbs[1] = ad1986a_ch2_init;
Takashi Iwai9230d212006-03-13 13:49:49 +01001326 spec->channel_mode = ad1986a_modes;
1327 spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
Takashi Iwai2125cad2006-03-27 12:52:22 +02001328 spec->need_dac_fix = 1;
1329 spec->multiout.max_channels = 2;
1330 spec->multiout.num_dacs = 1;
Takashi Iwai9230d212006-03-13 13:49:49 +01001331 break;
1332 case AD1986A_LAPTOP:
1333 spec->mixers[0] = ad1986a_laptop_mixers;
1334 spec->multiout.max_channels = 2;
1335 spec->multiout.num_dacs = 1;
1336 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1337 break;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001338 case AD1986A_LAPTOP_EAPD:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001339 spec->num_mixers = 3;
1340 spec->mixers[0] = ad1986a_laptop_master_mixers;
1341 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1342 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai1725b822008-11-21 02:25:48 +01001343 spec->num_init_verbs = 2;
1344 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1345 spec->multiout.max_channels = 2;
1346 spec->multiout.num_dacs = 1;
1347 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1348 if (!is_jack_available(codec, 0x25))
1349 spec->multiout.dig_out_nid = 0;
1350 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1351 break;
1352 case AD1986A_SAMSUNG:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001353 spec->num_mixers = 2;
1354 spec->mixers[0] = ad1986a_laptop_master_mixers;
1355 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001356 spec->num_init_verbs = 3;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001357 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001358 spec->init_verbs[2] = ad1986a_automic_verbs;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001359 spec->multiout.max_channels = 2;
1360 spec->multiout.num_dacs = 1;
1361 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001362 if (!is_jack_available(codec, 0x25))
1363 spec->multiout.dig_out_nid = 0;
Takashi Iwai5d5d5f42008-02-12 12:11:36 +01001364 spec->input_mux = &ad1986a_automic_capture_source;
1365 codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
1366 codec->patch_ops.init = ad1986a_automic_init;
Takashi Iwai825aa9722006-03-17 10:50:49 +01001367 break;
Takashi Iwaic912e7a2009-06-24 14:14:34 +02001368 case AD1986A_SAMSUNG_P50:
1369 spec->num_mixers = 2;
1370 spec->mixers[0] = ad1986a_automute_master_mixers;
1371 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1372 spec->num_init_verbs = 4;
1373 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1374 spec->init_verbs[2] = ad1986a_automic_verbs;
1375 spec->init_verbs[3] = ad1986a_hp_init_verbs;
1376 spec->multiout.max_channels = 2;
1377 spec->multiout.num_dacs = 1;
1378 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1379 if (!is_jack_available(codec, 0x25))
1380 spec->multiout.dig_out_nid = 0;
1381 spec->input_mux = &ad1986a_automic_capture_source;
1382 codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
1383 codec->patch_ops.init = ad1986a_samsung_p50_init;
1384 break;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001385 case AD1986A_LAPTOP_AUTOMUTE:
Takashi Iwai16d11a82009-06-24 14:07:53 +02001386 spec->num_mixers = 3;
1387 spec->mixers[0] = ad1986a_automute_master_mixers;
1388 spec->mixers[1] = ad1986a_laptop_eapd_mixers;
1389 spec->mixers[2] = ad1986a_laptop_intmic_mixers;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001390 spec->num_init_verbs = 3;
1391 spec->init_verbs[1] = ad1986a_eapd_init_verbs;
1392 spec->init_verbs[2] = ad1986a_hp_init_verbs;
1393 spec->multiout.max_channels = 2;
1394 spec->multiout.num_dacs = 1;
1395 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
Takashi Iwai8c0d9642008-01-28 12:30:17 +01001396 if (!is_jack_available(codec, 0x25))
1397 spec->multiout.dig_out_nid = 0;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001398 spec->input_mux = &ad1986a_laptop_eapd_capture_source;
1399 codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
1400 codec->patch_ops.init = ad1986a_hp_init;
Takashi Iwai03c405a2009-06-24 14:10:15 +02001401 /* Lenovo N100 seems to report the reversed bit
1402 * for HP jack-sensing
1403 */
1404 spec->inv_jack_detect = 1;
Takashi Iwai8ab78c72007-09-06 14:29:53 +02001405 break;
Tobin Davisf36090f2007-01-08 11:07:12 +01001406 case AD1986A_ULTRA:
1407 spec->mixers[0] = ad1986a_laptop_eapd_mixers;
1408 spec->num_init_verbs = 2;
1409 spec->init_verbs[1] = ad1986a_ultra_init;
1410 spec->multiout.max_channels = 2;
1411 spec->multiout.num_dacs = 1;
1412 spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
1413 spec->multiout.dig_out_nid = 0;
1414 break;
Takashi Iwai9230d212006-03-13 13:49:49 +01001415 }
1416
Takashi Iwaid29240c2007-10-26 12:35:56 +02001417 /* AD1986A has a hardware problem that it can't share a stream
1418 * with multiple output pins. The copy of front to surrounds
1419 * causes noisy or silent outputs at a certain timing, e.g.
1420 * changing the volume.
1421 * So, let's disable the shared stream.
1422 */
1423 spec->multiout.no_share_stream = 1;
1424
Takashi Iwai729d55b2009-12-25 22:49:01 +01001425 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001426 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001427
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428 return 0;
1429}
1430
1431/*
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001432 * AD1983 specific
1433 */
1434
1435#define AD1983_SPDIF_OUT 0x02
1436#define AD1983_DAC 0x03
1437#define AD1983_ADC 0x04
1438
Takashi Iwai498f5b12011-05-02 11:33:15 +02001439static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
1440static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
1441static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001442
Takashi Iwai498f5b12011-05-02 11:33:15 +02001443static const struct hda_input_mux ad1983_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001444 .num_items = 4,
1445 .items = {
1446 { "Mic", 0x0 },
1447 { "Line", 0x1 },
1448 { "Mix", 0x2 },
1449 { "Mix Mono", 0x3 },
1450 },
1451};
1452
1453/*
1454 * SPDIF playback route
1455 */
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001456static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001457{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001458 static const char * const texts[] = { "PCM", "ADC" };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001459
1460 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1461 uinfo->count = 1;
1462 uinfo->value.enumerated.items = 2;
1463 if (uinfo->value.enumerated.item > 1)
1464 uinfo->value.enumerated.item = 1;
1465 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1466 return 0;
1467}
1468
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001469static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001470{
1471 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1472 struct ad198x_spec *spec = codec->spec;
1473
1474 ucontrol->value.enumerated.item[0] = spec->spdif_route;
1475 return 0;
1476}
1477
Takashi Iwaic8b6bf92005-11-17 14:57:47 +01001478static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001479{
1480 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1481 struct ad198x_spec *spec = codec->spec;
1482
Takashi Iwai68ea7b22007-11-15 15:54:38 +01001483 if (ucontrol->value.enumerated.item[0] > 1)
1484 return -EINVAL;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001485 if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
1486 spec->spdif_route = ucontrol->value.enumerated.item[0];
Takashi Iwai82beb8f2007-08-10 17:09:26 +02001487 snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
1488 AC_VERB_SET_CONNECT_SEL,
1489 spec->spdif_route);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001490 return 1;
1491 }
1492 return 0;
1493}
1494
Takashi Iwai498f5b12011-05-02 11:33:15 +02001495static const struct snd_kcontrol_new ad1983_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001496 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1497 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1498 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1499 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1500 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1501 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1502 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1503 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1504 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1505 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1506 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1507 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001508 HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001509 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1510 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1511 {
1512 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1513 .name = "Capture Source",
1514 .info = ad198x_mux_enum_info,
1515 .get = ad198x_mux_enum_get,
1516 .put = ad198x_mux_enum_put,
1517 },
1518 {
1519 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001520 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001521 .info = ad1983_spdif_route_info,
1522 .get = ad1983_spdif_route_get,
1523 .put = ad1983_spdif_route_put,
1524 },
1525 { } /* end */
1526};
1527
Takashi Iwai498f5b12011-05-02 11:33:15 +02001528static const struct hda_verb ad1983_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001529 /* Front, HP, Mono; mute as default */
1530 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1531 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1532 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1533 /* Beep, PCM, Mic, Line-In: mute */
1534 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1535 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1536 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1537 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1538 /* Front, HP selectors; from Mix */
1539 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1540 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1541 /* Mono selector; from Mix */
1542 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1543 /* Mic selector; Mic */
1544 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
1545 /* Line-in selector: Line-in */
1546 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
1547 /* Mic boost: 0dB */
1548 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1549 /* Record selector: mic */
1550 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1551 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1552 /* SPDIF route: PCM */
1553 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1554 /* Front Pin */
1555 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1556 /* HP Pin */
1557 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1558 /* Mono Pin */
1559 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1560 /* Mic Pin */
1561 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1562 /* Line Pin */
1563 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1564 { } /* end */
1565};
1566
Takashi Iwaicb53c622007-08-10 17:21:45 +02001567#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001568static const struct hda_amp_list ad1983_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001569 { 0x12, HDA_OUTPUT, 0 }, /* Mic */
1570 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1571 { } /* end */
1572};
1573#endif
Takashi Iwai985be542005-11-02 18:26:49 +01001574
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001575static int patch_ad1983(struct hda_codec *codec)
1576{
1577 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001578 int err;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001579
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001580 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001581 if (spec == NULL)
1582 return -ENOMEM;
1583
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001584 codec->spec = spec;
1585
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001586 err = snd_hda_attach_beep_device(codec, 0x10);
1587 if (err < 0) {
1588 ad198x_free(codec);
1589 return err;
1590 }
1591 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
1592
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001593 spec->multiout.max_channels = 2;
1594 spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
1595 spec->multiout.dac_nids = ad1983_dac_nids;
1596 spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01001597 spec->num_adc_nids = 1;
1598 spec->adc_nids = ad1983_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01001599 spec->capsrc_nids = ad1983_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001600 spec->input_mux = &ad1983_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01001601 spec->num_mixers = 1;
1602 spec->mixers[0] = ad1983_mixers;
1603 spec->num_init_verbs = 1;
1604 spec->init_verbs[0] = ad1983_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001605 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02001606#ifdef CONFIG_SND_HDA_POWER_SAVE
1607 spec->loopback.amplist = ad1983_loopbacks;
1608#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01001609 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001610
1611 codec->patch_ops = ad198x_patch_ops;
1612
Takashi Iwai729d55b2009-12-25 22:49:01 +01001613 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02001614 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01001615
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001616 return 0;
1617}
1618
1619
1620/*
1621 * AD1981 HD specific
1622 */
1623
1624#define AD1981_SPDIF_OUT 0x02
1625#define AD1981_DAC 0x03
1626#define AD1981_ADC 0x04
1627
Takashi Iwai498f5b12011-05-02 11:33:15 +02001628static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
1629static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
1630static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001631
1632/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001633static const struct hda_input_mux ad1981_capture_source = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001634 .num_items = 7,
1635 .items = {
1636 { "Front Mic", 0x0 },
1637 { "Line", 0x1 },
1638 { "Mix", 0x2 },
1639 { "Mix Mono", 0x3 },
1640 { "CD", 0x4 },
1641 { "Mic", 0x6 },
1642 { "Aux", 0x7 },
1643 },
1644};
1645
Takashi Iwai498f5b12011-05-02 11:33:15 +02001646static const struct snd_kcontrol_new ad1981_mixers[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001647 HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1648 HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1649 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
1650 HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
1651 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
1652 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
1653 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1654 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1655 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1656 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1657 HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1658 HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1659 HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
1660 HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
1661 HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1662 HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1663 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1664 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001665 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1666 HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001667 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1668 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1669 {
1670 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1671 .name = "Capture Source",
1672 .info = ad198x_mux_enum_info,
1673 .get = ad198x_mux_enum_get,
1674 .put = ad198x_mux_enum_put,
1675 },
1676 /* identical with AD1983 */
1677 {
1678 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Takashi Iwai6540dff2006-06-13 11:57:22 +02001679 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001680 .info = ad1983_spdif_route_info,
1681 .get = ad1983_spdif_route_get,
1682 .put = ad1983_spdif_route_put,
1683 },
1684 { } /* end */
1685};
1686
Takashi Iwai498f5b12011-05-02 11:33:15 +02001687static const struct hda_verb ad1981_init_verbs[] = {
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001688 /* Front, HP, Mono; mute as default */
1689 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1690 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1691 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1692 /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
1693 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1694 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1695 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1696 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1697 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1698 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1699 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1700 /* Front, HP selectors; from Mix */
1701 {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
1702 {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
1703 /* Mono selector; from Mix */
1704 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
1705 /* Mic Mixer; select Front Mic */
1706 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1707 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1708 /* Mic boost: 0dB */
Takashi Iwai6d6e17d2009-01-23 12:33:54 +01001709 {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1710 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001711 /* Record selector: Front mic */
1712 {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
1713 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1714 /* SPDIF route: PCM */
1715 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
1716 /* Front Pin */
1717 {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1718 /* HP Pin */
1719 {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
1720 /* Mono Pin */
1721 {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
1722 /* Front & Rear Mic Pins */
1723 {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1724 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
1725 /* Line Pin */
1726 {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
1727 /* Digital Beep */
1728 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
1729 /* Line-Out as Input: disabled */
1730 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1731 { } /* end */
1732};
1733
Takashi Iwaicb53c622007-08-10 17:21:45 +02001734#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02001735static const struct hda_amp_list ad1981_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001736 { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
1737 { 0x13, HDA_OUTPUT, 0 }, /* Line */
1738 { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
1739 { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
1740 { 0x1d, HDA_OUTPUT, 0 }, /* CD */
1741 { } /* end */
1742};
1743#endif
1744
Takashi Iwai18a815d2006-03-01 19:54:39 +01001745/*
1746 * Patch for HP nx6320
1747 *
Tobin Davis18768992007-03-12 22:20:51 +01001748 * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
Takashi Iwai18a815d2006-03-01 19:54:39 +01001749 * speaker output enabled _and_ mute-LED off.
1750 */
1751
1752#define AD1981_HP_EVENT 0x37
1753#define AD1981_MIC_EVENT 0x38
1754
Takashi Iwai498f5b12011-05-02 11:33:15 +02001755static const struct hda_verb ad1981_hp_init_verbs[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001756 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
1757 /* pin sensing on HP and Mic jacks */
1758 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1759 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1760 {}
1761};
1762
1763/* turn on/off EAPD (+ mute HP) as a master switch */
1764static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1765 struct snd_ctl_elem_value *ucontrol)
1766{
1767 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1768 struct ad198x_spec *spec = codec->spec;
1769
1770 if (! ad198x_eapd_put(kcontrol, ucontrol))
1771 return 0;
Takashi Iwaif0824812008-02-11 15:54:34 +01001772 /* change speaker pin appropriately */
1773 snd_hda_codec_write(codec, 0x05, 0,
1774 AC_VERB_SET_PIN_WIDGET_CONTROL,
1775 spec->cur_eapd ? PIN_OUT : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001776 /* toggle HP mute appropriately */
Takashi Iwai47fd8302007-08-10 17:11:07 +02001777 snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
1778 HDA_AMP_MUTE,
1779 spec->cur_eapd ? 0 : HDA_AMP_MUTE);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001780 return 1;
1781}
1782
1783/* bind volumes of both NID 0x05 and 0x06 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001784static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001785 .ops = &snd_hda_bind_vol,
1786 .values = {
1787 HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
1788 HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
1789 0
1790 },
1791};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001792
1793/* mute internal speaker if HP is plugged */
1794static void ad1981_hp_automute(struct hda_codec *codec)
1795{
1796 unsigned int present;
1797
Takashi Iwaid56757a2009-11-18 08:00:14 +01001798 present = snd_hda_jack_detect(codec, 0x06);
Takashi Iwai47fd8302007-08-10 17:11:07 +02001799 snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
1800 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001801}
1802
1803/* toggle input of built-in and mic jack appropriately */
1804static void ad1981_hp_automic(struct hda_codec *codec)
1805{
Takashi Iwai498f5b12011-05-02 11:33:15 +02001806 static const struct hda_verb mic_jack_on[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001807 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1808 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1809 {}
1810 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02001811 static const struct hda_verb mic_jack_off[] = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001812 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
1813 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
1814 {}
1815 };
1816 unsigned int present;
1817
Takashi Iwaid56757a2009-11-18 08:00:14 +01001818 present = snd_hda_jack_detect(codec, 0x08);
Takashi Iwai18a815d2006-03-01 19:54:39 +01001819 if (present)
1820 snd_hda_sequence_write(codec, mic_jack_on);
1821 else
1822 snd_hda_sequence_write(codec, mic_jack_off);
1823}
1824
1825/* unsolicited event for HP jack sensing */
1826static void ad1981_hp_unsol_event(struct hda_codec *codec,
1827 unsigned int res)
1828{
1829 res >>= 26;
1830 switch (res) {
1831 case AD1981_HP_EVENT:
1832 ad1981_hp_automute(codec);
1833 break;
1834 case AD1981_MIC_EVENT:
1835 ad1981_hp_automic(codec);
1836 break;
1837 }
1838}
1839
Takashi Iwai498f5b12011-05-02 11:33:15 +02001840static const struct hda_input_mux ad1981_hp_capture_source = {
Takashi Iwai18a815d2006-03-01 19:54:39 +01001841 .num_items = 3,
1842 .items = {
1843 { "Mic", 0x0 },
1844 { "Docking-Station", 0x1 },
1845 { "Mix", 0x2 },
1846 },
1847};
1848
Takashi Iwai498f5b12011-05-02 11:33:15 +02001849static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
Takashi Iwaicca3b372007-08-10 17:12:15 +02001850 HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001851 {
1852 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001853 .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
Takashi Iwai18a815d2006-03-01 19:54:39 +01001854 .name = "Master Playback Switch",
1855 .info = ad198x_eapd_info,
1856 .get = ad198x_eapd_get,
1857 .put = ad1981_hp_master_sw_put,
1858 .private_value = 0x05,
1859 },
1860 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1861 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1862#if 0
1863 /* FIXME: analog mic/line loopback doesn't work with my tests...
1864 * (although recording is OK)
1865 */
1866 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1867 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1868 HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
1869 HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
1870 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
1871 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
1872 /* FIXME: does this laptop have analog CD connection? */
1873 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1874 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
1875#endif
David Henningsson5f99f862011-01-04 15:24:24 +01001876 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
1877 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001878 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1879 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1880 {
1881 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1882 .name = "Capture Source",
1883 .info = ad198x_mux_enum_info,
1884 .get = ad198x_mux_enum_get,
1885 .put = ad198x_mux_enum_put,
1886 },
1887 { } /* end */
1888};
1889
1890/* initialize jack-sensing, too */
1891static int ad1981_hp_init(struct hda_codec *codec)
1892{
1893 ad198x_init(codec);
1894 ad1981_hp_automute(codec);
1895 ad1981_hp_automic(codec);
1896 return 0;
1897}
1898
Tobin Davis18768992007-03-12 22:20:51 +01001899/* configuration for Toshiba Laptops */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001900static const struct hda_verb ad1981_toshiba_init_verbs[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001901 {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
1902 /* pin sensing on HP and Mic jacks */
1903 {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
1904 {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
1905 {}
1906};
1907
Takashi Iwai498f5b12011-05-02 11:33:15 +02001908static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
Tobin Davis18768992007-03-12 22:20:51 +01001909 HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
1910 HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
1911 { }
1912};
1913
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001914/* configuration for Lenovo Thinkpad T60 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02001915static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001916 HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
1917 HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
1918 HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
1919 HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
1920 HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
1921 HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
1922 HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
1923 HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01001924 HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001925 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
1926 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
1927 {
1928 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1929 .name = "Capture Source",
1930 .info = ad198x_mux_enum_info,
1931 .get = ad198x_mux_enum_get,
1932 .put = ad198x_mux_enum_put,
1933 },
Takashi Iwai6540dff2006-06-13 11:57:22 +02001934 /* identical with AD1983 */
1935 {
1936 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1937 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
1938 .info = ad1983_spdif_route_info,
1939 .get = ad1983_spdif_route_get,
1940 .put = ad1983_spdif_route_put,
1941 },
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001942 { } /* end */
1943};
1944
Takashi Iwai498f5b12011-05-02 11:33:15 +02001945static const struct hda_input_mux ad1981_thinkpad_capture_source = {
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001946 .num_items = 3,
1947 .items = {
1948 { "Mic", 0x0 },
1949 { "Mix", 0x2 },
1950 { "CD", 0x4 },
1951 },
1952};
1953
Takashi Iwai18a815d2006-03-01 19:54:39 +01001954/* models */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001955enum {
1956 AD1981_BASIC,
1957 AD1981_HP,
1958 AD1981_THINKPAD,
Tobin Davis18768992007-03-12 22:20:51 +01001959 AD1981_TOSHIBA,
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001960 AD1981_MODELS
1961};
Takashi Iwai18a815d2006-03-01 19:54:39 +01001962
Takashi Iwaiea734962011-01-17 11:29:34 +01001963static const char * const ad1981_models[AD1981_MODELS] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001964 [AD1981_HP] = "hp",
1965 [AD1981_THINKPAD] = "thinkpad",
1966 [AD1981_BASIC] = "basic",
Tobin Davis18768992007-03-12 22:20:51 +01001967 [AD1981_TOSHIBA] = "toshiba"
Takashi Iwaif5fcc132006-11-24 17:07:44 +01001968};
1969
Takashi Iwai498f5b12011-05-02 11:33:15 +02001970static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001971 SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
Takashi Iwai470eaf62008-06-30 16:40:10 +02001972 SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
Takashi Iwai8970ccd2006-04-18 12:50:40 +02001973 /* All HP models */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001974 SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001975 SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
Takashi Iwai01686c5f2006-04-18 12:54:11 +02001976 /* Lenovo Thinkpad T60/X60/Z6xx */
Takashi Iwaidea0a502009-02-09 17:14:52 +01001977 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01001978 /* HP nx6320 (reversed SSID, H/W bug) */
1979 SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
Takashi Iwai18a815d2006-03-01 19:54:39 +01001980 {}
1981};
1982
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001983static int patch_ad1981(struct hda_codec *codec)
1984{
1985 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001986 int err, board_config;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001987
Takashi Iwaie560d8d2005-09-09 14:21:46 +02001988 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001989 if (spec == NULL)
1990 return -ENOMEM;
1991
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02001992 codec->spec = spec;
1993
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01001994 err = snd_hda_attach_beep_device(codec, 0x10);
1995 if (err < 0) {
1996 ad198x_free(codec);
1997 return err;
1998 }
1999 set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
2000
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002001 spec->multiout.max_channels = 2;
2002 spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
2003 spec->multiout.dac_nids = ad1981_dac_nids;
2004 spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
Takashi Iwai985be542005-11-02 18:26:49 +01002005 spec->num_adc_nids = 1;
2006 spec->adc_nids = ad1981_adc_nids;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002007 spec->capsrc_nids = ad1981_capsrc_nids;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002008 spec->input_mux = &ad1981_capture_source;
Takashi Iwai985be542005-11-02 18:26:49 +01002009 spec->num_mixers = 1;
2010 spec->mixers[0] = ad1981_mixers;
2011 spec->num_init_verbs = 1;
2012 spec->init_verbs[0] = ad1981_init_verbs;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002013 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002014#ifdef CONFIG_SND_HDA_POWER_SAVE
2015 spec->loopback.amplist = ad1981_loopbacks;
2016#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01002017 spec->vmaster_nid = 0x05;
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002018
2019 codec->patch_ops = ad198x_patch_ops;
2020
Takashi Iwai18a815d2006-03-01 19:54:39 +01002021 /* override some parameters */
Takashi Iwaif5fcc132006-11-24 17:07:44 +01002022 board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
2023 ad1981_models,
2024 ad1981_cfg_tbl);
Takashi Iwai18a815d2006-03-01 19:54:39 +01002025 switch (board_config) {
2026 case AD1981_HP:
2027 spec->mixers[0] = ad1981_hp_mixers;
2028 spec->num_init_verbs = 2;
2029 spec->init_verbs[1] = ad1981_hp_init_verbs;
Takashi Iwai695cd4a2011-06-10 14:37:04 +02002030 if (!is_jack_available(codec, 0x0a))
2031 spec->multiout.dig_out_nid = 0;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002032 spec->input_mux = &ad1981_hp_capture_source;
2033
2034 codec->patch_ops.init = ad1981_hp_init;
2035 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
Daniel T Chen01f59662009-12-13 16:22:58 -05002036 /* set the upper-limit for mixer amp to 0dB for avoiding the
2037 * possible damage by overloading
2038 */
2039 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2040 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2041 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2042 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2043 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai18a815d2006-03-01 19:54:39 +01002044 break;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002045 case AD1981_THINKPAD:
2046 spec->mixers[0] = ad1981_thinkpad_mixers;
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002047 spec->input_mux = &ad1981_thinkpad_capture_source;
Daniel T Chenb8e80cf2010-03-30 13:29:28 -04002048 /* set the upper-limit for mixer amp to 0dB for avoiding the
2049 * possible damage by overloading
2050 */
2051 snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
2052 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2053 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2054 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2055 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai01686c5f2006-04-18 12:54:11 +02002056 break;
Tobin Davis18768992007-03-12 22:20:51 +01002057 case AD1981_TOSHIBA:
2058 spec->mixers[0] = ad1981_hp_mixers;
2059 spec->mixers[1] = ad1981_toshiba_mixers;
2060 spec->num_init_verbs = 2;
2061 spec->init_verbs[1] = ad1981_toshiba_init_verbs;
2062 spec->multiout.dig_out_nid = 0;
2063 spec->input_mux = &ad1981_hp_capture_source;
2064 codec->patch_ops.init = ad1981_hp_init;
2065 codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
2066 break;
Takashi Iwai18a815d2006-03-01 19:54:39 +01002067 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01002068
2069 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02002070 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01002071
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02002072 return 0;
2073}
2074
2075
2076/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002077 * AD1988
2078 *
2079 * Output pins and routes
2080 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002081 * Pin Mix Sel DAC (*)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002082 * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
2083 * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
2084 * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
2085 * port-D 0x12 (mute/hp) <- 0x29 <- 04
2086 * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
2087 * port-F 0x16 (mute) <- 0x2a <- 06
2088 * port-G 0x24 (mute) <- 0x27 <- 05
2089 * port-H 0x25 (mute) <- 0x28 <- 0a
2090 * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
2091 *
Takashi Iwaid32410b12005-11-24 16:06:23 +01002092 * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
2093 * (*) 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 +01002094 *
2095 * Input pins and routes
2096 *
2097 * pin boost mix input # / adc input #
2098 * port-A 0x11 -> 0x38 -> mix 2, ADC 0
2099 * port-B 0x14 -> 0x39 -> mix 0, ADC 1
2100 * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
2101 * port-D 0x12 -> 0x3d -> mix 3, ADC 8
2102 * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
2103 * port-F 0x16 -> 0x3b -> mix 5, ADC 3
2104 * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
2105 * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
2106 *
2107 *
2108 * DAC assignment
Takashi Iwaid32410b12005-11-24 16:06:23 +01002109 * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002110 * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002111 *
2112 * Inputs of Analog Mix (0x20)
2113 * 0:Port-B (front mic)
2114 * 1:Port-C/G/H (line-in)
2115 * 2:Port-A
2116 * 3:Port-D (line-in/2)
2117 * 4:Port-E/G/H (mic-in)
2118 * 5:Port-F (mic2-in)
2119 * 6:CD
2120 * 7:Beep
2121 *
2122 * ADC selection
2123 * 0:Port-A
2124 * 1:Port-B (front mic-in)
2125 * 2:Port-C (line-in)
2126 * 3:Port-F (mic2-in)
2127 * 4:Port-E (mic-in)
2128 * 5:CD
2129 * 6:Port-G
2130 * 7:Port-H
2131 * 8:Port-D (line-in/2)
2132 * 9:Mix
2133 *
2134 * Proposed pin assignments by the datasheet
2135 *
2136 * 6-stack
2137 * Port-A front headphone
2138 * B front mic-in
2139 * C rear line-in
2140 * D rear front-out
2141 * E rear mic-in
2142 * F rear surround
2143 * G rear CLFE
2144 * H rear side
2145 *
2146 * 3-stack
2147 * Port-A front headphone
2148 * B front mic
2149 * C rear line-in/surround
2150 * D rear front-out
2151 * E rear mic-in/CLFE
2152 *
2153 * laptop
2154 * Port-A headphone
2155 * B mic-in
2156 * C docking station
2157 * D internal speaker (with EAPD)
2158 * E/F quad mic array
2159 */
2160
2161
2162/* models */
2163enum {
2164 AD1988_6STACK,
2165 AD1988_6STACK_DIG,
2166 AD1988_3STACK,
2167 AD1988_3STACK_DIG,
2168 AD1988_LAPTOP,
2169 AD1988_LAPTOP_DIG,
Takashi Iwaid32410b12005-11-24 16:06:23 +01002170 AD1988_AUTO,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002171 AD1988_MODEL_LAST,
2172};
2173
Takashi Iwaid32410b12005-11-24 16:06:23 +01002174/* reivision id to check workarounds */
2175#define AD1988A_REV2 0x100200
2176
Takashi Iwai1a806f42006-07-03 15:58:16 +02002177#define is_rev2(codec) \
2178 ((codec)->vendor_id == 0x11d41988 && \
2179 (codec)->revision_id == AD1988A_REV2)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002180
2181/*
2182 * mixers
2183 */
2184
Takashi Iwai498f5b12011-05-02 11:33:15 +02002185static const hda_nid_t ad1988_6stack_dac_nids[4] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002186 0x04, 0x06, 0x05, 0x0a
2187};
2188
Takashi Iwai498f5b12011-05-02 11:33:15 +02002189static const hda_nid_t ad1988_3stack_dac_nids[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002190 0x04, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002191};
2192
2193/* for AD1988A revision-2, DAC2-4 are swapped */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002194static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002195 0x04, 0x05, 0x0a, 0x06
2196};
2197
Takashi Iwai498f5b12011-05-02 11:33:15 +02002198static const hda_nid_t ad1988_alt_dac_nid[1] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002199 0x03
2200};
2201
Takashi Iwai498f5b12011-05-02 11:33:15 +02002202static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002203 0x04, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002204};
2205
Takashi Iwai498f5b12011-05-02 11:33:15 +02002206static const hda_nid_t ad1988_adc_nids[3] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002207 0x08, 0x09, 0x0f
2208};
2209
Takashi Iwai498f5b12011-05-02 11:33:15 +02002210static const hda_nid_t ad1988_capsrc_nids[3] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002211 0x0c, 0x0d, 0x0e
2212};
2213
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002214#define AD1988_SPDIF_OUT 0x02
2215#define AD1988_SPDIF_OUT_HDMI 0x0b
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002216#define AD1988_SPDIF_IN 0x07
2217
Takashi Iwai498f5b12011-05-02 11:33:15 +02002218static const hda_nid_t ad1989b_slave_dig_outs[] = {
Takashi Iwai3a08e302009-02-13 11:37:08 +01002219 AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002220};
2221
Takashi Iwai498f5b12011-05-02 11:33:15 +02002222static const struct hda_input_mux ad1988_6stack_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002223 .num_items = 5,
2224 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002225 { "Front Mic", 0x1 }, /* port-B */
2226 { "Line", 0x2 }, /* port-C */
2227 { "Mic", 0x4 }, /* port-E */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002228 { "CD", 0x5 },
2229 { "Mix", 0x9 },
2230 },
2231};
2232
Takashi Iwai498f5b12011-05-02 11:33:15 +02002233static const struct hda_input_mux ad1988_laptop_capture_source = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002234 .num_items = 3,
2235 .items = {
Takashi Iwaifb304ce2008-02-25 15:32:01 +01002236 { "Mic/Line", 0x1 }, /* port-B */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002237 { "CD", 0x5 },
2238 { "Mix", 0x9 },
2239 },
2240};
2241
2242/*
2243 */
2244static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
2245 struct snd_ctl_elem_info *uinfo)
2246{
2247 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2248 struct ad198x_spec *spec = codec->spec;
2249 return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
2250 spec->num_channel_mode);
2251}
2252
2253static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
2254 struct snd_ctl_elem_value *ucontrol)
2255{
2256 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2257 struct ad198x_spec *spec = codec->spec;
2258 return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
2259 spec->num_channel_mode, spec->multiout.max_channels);
2260}
2261
2262static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
2263 struct snd_ctl_elem_value *ucontrol)
2264{
2265 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2266 struct ad198x_spec *spec = codec->spec;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002267 int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
2268 spec->num_channel_mode,
2269 &spec->multiout.max_channels);
Takashi Iwaibd2033f2006-10-10 19:49:31 +02002270 if (err >= 0 && spec->need_dac_fix)
Takashi Iwai2125cad2006-03-27 12:52:22 +02002271 spec->multiout.num_dacs = spec->multiout.max_channels / 2;
Takashi Iwai4e195a72006-07-28 14:47:34 +02002272 return err;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002273}
2274
Raymond Yau34588702011-09-23 19:03:25 +08002275static const struct snd_kcontrol_new ad1988_hp_mixers[] = {
2276 {
2277 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2278 .name = "Independent HP",
2279 .info = ad1988_independent_hp_info,
2280 .get = ad1988_independent_hp_get,
2281 .put = ad1988_independent_hp_put,
2282 },
2283 { } /* end */
2284};
2285
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002286/* 6-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002287static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002288 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2289 HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
2290 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2291 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
2292 HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002293 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002294};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002295
Takashi Iwai498f5b12011-05-02 11:33:15 +02002296static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002297 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2298 HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
2299 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
2300 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
2301 HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002302 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002303};
2304
Takashi Iwai498f5b12011-05-02 11:33:15 +02002305static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002306 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002307 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
2308 HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
2309 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
2310 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
2311 HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
2312 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2313 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2314
2315 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2316 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2317 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2318 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2319 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2320 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2321 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2322 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2323
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002324 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002325 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2326
David Henningsson5f99f862011-01-04 15:24:24 +01002327 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2328 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002329 { } /* end */
2330};
2331
2332/* 3-stack mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002333static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002334 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002335 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002336 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
2337 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002338 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002339};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002340
Takashi Iwai498f5b12011-05-02 11:33:15 +02002341static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002342 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002343 HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
2344 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
2345 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
Takashi Iwai2ece5f422006-07-06 19:16:40 +02002346 { } /* end */
Takashi Iwaid32410b12005-11-24 16:06:23 +01002347};
2348
Takashi Iwai498f5b12011-05-02 11:33:15 +02002349static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002350 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002351 HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
Takashi Iwaid32410b12005-11-24 16:06:23 +01002352 HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
2353 HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
2354 HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002355 HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
2356 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2357
2358 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2359 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2360 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2361 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2362 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2363 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2364 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
2365 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
2366
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002367 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002368 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2369
David Henningsson5f99f862011-01-04 15:24:24 +01002370 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
2371 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002372 {
2373 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2374 .name = "Channel Mode",
2375 .info = ad198x_ch_mode_info,
2376 .get = ad198x_ch_mode_get,
2377 .put = ad198x_ch_mode_put,
2378 },
2379
2380 { } /* end */
2381};
2382
2383/* laptop mode */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002384static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
Raymond Yau356aab72011-08-31 10:30:59 +08002385 HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002386 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
2387 HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
2388 HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
2389
2390 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
2391 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
2392 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
2393 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
2394 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
2395 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
2396
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002397 HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002398 HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
2399
David Henningsson5f99f862011-01-04 15:24:24 +01002400 HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002401
2402 {
2403 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2404 .name = "External Amplifier",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002405 .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
Takashi Iwai18a815d2006-03-01 19:54:39 +01002406 .info = ad198x_eapd_info,
2407 .get = ad198x_eapd_get,
2408 .put = ad198x_eapd_put,
Takashi Iwaiee6e3652009-12-08 17:23:33 +01002409 .private_value = 0x12, /* port-D */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002410 },
2411
2412 { } /* end */
2413};
2414
2415/* capture */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002416static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002417 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
2418 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
2419 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
2420 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
2421 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
2422 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
2423 {
2424 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2425 /* The multiple "Capture Source" controls confuse alsamixer
2426 * So call somewhat different..
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002427 */
2428 /* .name = "Capture Source", */
2429 .name = "Input Source",
2430 .count = 3,
2431 .info = ad198x_mux_enum_info,
2432 .get = ad198x_mux_enum_get,
2433 .put = ad198x_mux_enum_put,
2434 },
2435 { } /* end */
2436};
2437
2438static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
2439 struct snd_ctl_elem_info *uinfo)
2440{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002441 static const char * const texts[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002442 "PCM", "ADC1", "ADC2", "ADC3"
2443 };
2444 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2445 uinfo->count = 1;
2446 uinfo->value.enumerated.items = 4;
2447 if (uinfo->value.enumerated.item >= 4)
2448 uinfo->value.enumerated.item = 3;
2449 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2450 return 0;
2451}
2452
2453static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
2454 struct snd_ctl_elem_value *ucontrol)
2455{
2456 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2457 unsigned int sel;
2458
Takashi Iwaibddcf542007-07-24 18:04:05 +02002459 sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
2460 AC_AMP_GET_INPUT);
2461 if (!(sel & 0x80))
2462 ucontrol->value.enumerated.item[0] = 0;
2463 else {
Takashi Iwai35b26722007-05-05 12:17:17 +02002464 sel = snd_hda_codec_read(codec, 0x0b, 0,
2465 AC_VERB_GET_CONNECT_SEL, 0);
2466 if (sel < 3)
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002467 sel++;
2468 else
2469 sel = 0;
Takashi Iwaibddcf542007-07-24 18:04:05 +02002470 ucontrol->value.enumerated.item[0] = sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002471 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002472 return 0;
2473}
2474
2475static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
2476 struct snd_ctl_elem_value *ucontrol)
2477{
2478 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Takashi Iwai35b26722007-05-05 12:17:17 +02002479 unsigned int val, sel;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002480 int change;
2481
Takashi Iwai35b26722007-05-05 12:17:17 +02002482 val = ucontrol->value.enumerated.item[0];
Takashi Iwai68ea7b22007-11-15 15:54:38 +01002483 if (val > 3)
2484 return -EINVAL;
Takashi Iwai35b26722007-05-05 12:17:17 +02002485 if (!val) {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002486 sel = snd_hda_codec_read(codec, 0x1d, 0,
2487 AC_VERB_GET_AMP_GAIN_MUTE,
2488 AC_AMP_GET_INPUT);
2489 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002490 if (change) {
2491 snd_hda_codec_write_cache(codec, 0x1d, 0,
2492 AC_VERB_SET_AMP_GAIN_MUTE,
2493 AMP_IN_UNMUTE(0));
2494 snd_hda_codec_write_cache(codec, 0x1d, 0,
2495 AC_VERB_SET_AMP_GAIN_MUTE,
2496 AMP_IN_MUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002497 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002498 } else {
Takashi Iwaibddcf542007-07-24 18:04:05 +02002499 sel = snd_hda_codec_read(codec, 0x1d, 0,
2500 AC_VERB_GET_AMP_GAIN_MUTE,
2501 AC_AMP_GET_INPUT | 0x01);
2502 change = sel & 0x80;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002503 if (change) {
2504 snd_hda_codec_write_cache(codec, 0x1d, 0,
2505 AC_VERB_SET_AMP_GAIN_MUTE,
2506 AMP_IN_MUTE(0));
2507 snd_hda_codec_write_cache(codec, 0x1d, 0,
2508 AC_VERB_SET_AMP_GAIN_MUTE,
2509 AMP_IN_UNMUTE(1));
Takashi Iwaibddcf542007-07-24 18:04:05 +02002510 }
Takashi Iwai35b26722007-05-05 12:17:17 +02002511 sel = snd_hda_codec_read(codec, 0x0b, 0,
2512 AC_VERB_GET_CONNECT_SEL, 0) + 1;
2513 change |= sel != val;
Takashi Iwai82beb8f2007-08-10 17:09:26 +02002514 if (change)
2515 snd_hda_codec_write_cache(codec, 0x0b, 0,
2516 AC_VERB_SET_CONNECT_SEL,
2517 val - 1);
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002518 }
2519 return change;
2520}
2521
Takashi Iwai498f5b12011-05-02 11:33:15 +02002522static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002523 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
2524 {
2525 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2526 .name = "IEC958 Playback Source",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002527 .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002528 .info = ad1988_spdif_playback_source_info,
2529 .get = ad1988_spdif_playback_source_get,
2530 .put = ad1988_spdif_playback_source_put,
2531 },
2532 { } /* end */
2533};
2534
Takashi Iwai498f5b12011-05-02 11:33:15 +02002535static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002536 HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
2537 { } /* end */
2538};
2539
Takashi Iwai498f5b12011-05-02 11:33:15 +02002540static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002541 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07002542 HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002543 { } /* end */
2544};
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002545
2546/*
2547 * initialization verbs
2548 */
2549
2550/*
2551 * for 6-stack (+dig)
2552 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002553static const struct hda_verb ad1988_6stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002554 /* Front, Surround, CLFE, side DAC; unmute as default */
2555 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2556 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2557 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2558 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002559 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002560 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002561 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2562 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2563 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2564 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2565 /* Port-D line-out path */
2566 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2567 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2568 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2569 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2570 /* Port-F surround path */
2571 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2572 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2573 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2574 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2575 /* Port-G CLFE path */
2576 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2577 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2578 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2579 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2580 /* Port-H side path */
2581 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2582 {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2583 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2584 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2585 /* Mono out path */
2586 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2587 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2588 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2589 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2590 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2591 /* Port-B front mic-in path */
2592 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2593 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2594 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2595 /* Port-C line-in path */
2596 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2597 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
2598 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2599 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2600 /* Port-E mic-in path */
2601 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2602 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2603 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2604 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
Johannes Stezenbach695005c2007-12-13 17:51:00 +01002605 /* Analog CD Input */
2606 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002607 /* Analog Mix output amp */
2608 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002609
2610 { }
2611};
2612
Takashi Iwai498f5b12011-05-02 11:33:15 +02002613static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
Raymond Yauc66ddf32011-01-17 11:19:03 +01002614 /* Headphone; unmute as default */
2615 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2616 /* Port-A front headphon path */
2617 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
2618 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2619 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2620 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2621 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
Raymond Yauc66ddf32011-01-17 11:19:03 +01002622
2623 { }
2624};
2625
Takashi Iwai498f5b12011-05-02 11:33:15 +02002626static const struct hda_verb ad1988_capture_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002627 /* mute analog mix */
2628 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2629 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2630 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2631 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2632 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2633 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2634 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2635 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2636 /* select ADCs - front-mic */
2637 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2638 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2639 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002640
2641 { }
2642};
2643
Takashi Iwai498f5b12011-05-02 11:33:15 +02002644static const struct hda_verb ad1988_spdif_init_verbs[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002645 /* SPDIF out sel */
2646 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
2647 {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
2648 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaibddcf542007-07-24 18:04:05 +02002649 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002650 /* SPDIF out pin */
2651 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002652
2653 { }
2654};
2655
Takashi Iwai498f5b12011-05-02 11:33:15 +02002656static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01002657 /* unmute SPDIF input pin */
2658 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2659 { }
2660};
2661
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002662/* AD1989 has no ADC -> SPDIF route */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002663static const struct hda_verb ad1989_spdif_init_verbs[] = {
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002664 /* SPDIF-1 out pin */
2665 {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002666 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Robin H. Johnsone8bfc6c2008-09-13 16:55:00 -07002667 /* SPDIF-2/HDMI out pin */
2668 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
2669 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02002670 { }
2671};
2672
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002673/*
2674 * verbs for 3stack (+dig)
2675 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002676static const struct hda_verb ad1988_3stack_ch2_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002677 /* set port-C to line-in */
2678 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2679 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
2680 /* set port-E to mic-in */
2681 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2682 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
2683 { } /* end */
2684};
2685
Takashi Iwai498f5b12011-05-02 11:33:15 +02002686static const struct hda_verb ad1988_3stack_ch6_init[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002687 /* set port-C to surround out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002688 { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002689 { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002690 /* set port-E to CLFE out */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002691 { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
Takashi Iwaid32410b12005-11-24 16:06:23 +01002692 { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002693 { } /* end */
2694};
2695
Takashi Iwai498f5b12011-05-02 11:33:15 +02002696static const struct hda_channel_mode ad1988_3stack_modes[2] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002697 { 2, ad1988_3stack_ch2_init },
2698 { 6, ad1988_3stack_ch6_init },
2699};
2700
Takashi Iwai498f5b12011-05-02 11:33:15 +02002701static const struct hda_verb ad1988_3stack_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002702 /* Front, Surround, CLFE, side DAC; unmute as default */
2703 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2704 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2705 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2706 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002707 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002708 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002709 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2710 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2711 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2712 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2713 /* Port-D line-out path */
2714 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2715 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2716 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2717 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2718 /* Mono out path */
2719 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2720 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2721 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2722 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2723 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2724 /* Port-B front mic-in path */
2725 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2726 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2727 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002728 /* Port-C line-in/surround path - 6ch mode as default */
2729 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2730 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002731 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002732 {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002733 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
Takashi Iwaid32410b12005-11-24 16:06:23 +01002734 /* Port-E mic-in/CLFE path - 6ch mode as default */
2735 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2736 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002737 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01002738 {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002739 {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
2740 /* mute analog mix */
2741 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2742 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2743 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2744 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2745 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2746 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2747 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2748 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2749 /* select ADCs - front-mic */
2750 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2751 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2752 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002753 /* Analog Mix output amp */
2754 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002755 { }
2756};
2757
2758/*
2759 * verbs for laptop mode (+dig)
2760 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02002761static const struct hda_verb ad1988_laptop_hp_on[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002762 /* unmute port-A and mute port-D */
2763 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2764 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2765 { } /* end */
2766};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002767static const struct hda_verb ad1988_laptop_hp_off[] = {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002768 /* mute port-A and unmute port-D */
2769 { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
2770 { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
2771 { } /* end */
2772};
2773
2774#define AD1988_HP_EVENT 0x01
2775
Takashi Iwai498f5b12011-05-02 11:33:15 +02002776static const struct hda_verb ad1988_laptop_init_verbs[] = {
Takashi Iwai2e5b9562005-11-21 16:36:15 +01002777 /* Front, Surround, CLFE, side DAC; unmute as default */
2778 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2779 {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2780 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2781 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002782 /* Port-A front headphon path */
Raymond Yau356aab72011-08-31 10:30:59 +08002783 {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002784 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2785 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2786 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2787 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
2788 /* unsolicited event for pin-sense */
2789 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
2790 /* Port-D line-out path + EAPD */
2791 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2792 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2793 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2794 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2795 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
2796 /* Mono out path */
2797 {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
2798 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2799 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2800 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2801 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
2802 /* Port-B mic-in path */
2803 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
2804 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
2805 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2806 /* Port-C docking station - try to output */
2807 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
2808 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
2809 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2810 {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
2811 /* mute analog mix */
2812 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
2813 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
2814 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
2815 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
2816 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
2817 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
2818 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
2819 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
2820 /* select ADCs - mic */
2821 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
2822 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
2823 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
Takashi Iwaidb3da6c2008-08-11 18:08:54 +02002824 /* Analog Mix output amp */
2825 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002826 { }
2827};
2828
2829static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
2830{
2831 if ((res >> 26) != AD1988_HP_EVENT)
2832 return;
Takashi Iwaid56757a2009-11-18 08:00:14 +01002833 if (snd_hda_jack_detect(codec, 0x11))
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002834 snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
2835 else
2836 snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
2837}
2838
Takashi Iwaicb53c622007-08-10 17:21:45 +02002839#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02002840static const struct hda_amp_list ad1988_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002841 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
2842 { 0x20, HDA_INPUT, 1 }, /* Line */
2843 { 0x20, HDA_INPUT, 4 }, /* Mic */
2844 { 0x20, HDA_INPUT, 6 }, /* CD */
2845 { } /* end */
2846};
2847#endif
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01002848
2849/*
Takashi Iwaid32410b12005-11-24 16:06:23 +01002850 * Automatic parse of I/O pins from the BIOS configuration
2851 */
2852
Takashi Iwaid32410b12005-11-24 16:06:23 +01002853enum {
2854 AD_CTL_WIDGET_VOL,
2855 AD_CTL_WIDGET_MUTE,
2856 AD_CTL_BIND_MUTE,
2857};
Takashi Iwai498f5b12011-05-02 11:33:15 +02002858static const struct snd_kcontrol_new ad1988_control_templates[] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002859 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
2860 HDA_CODEC_MUTE(NULL, 0, 0, 0),
2861 HDA_BIND_MUTE(NULL, 0, 0, 0),
2862};
2863
2864/* add dynamic controls */
2865static int add_control(struct ad198x_spec *spec, int type, const char *name,
2866 unsigned long val)
2867{
2868 struct snd_kcontrol_new *knew;
2869
Takashi Iwai603c4012008-07-30 15:01:44 +02002870 snd_array_init(&spec->kctls, sizeof(*knew), 32);
2871 knew = snd_array_new(&spec->kctls);
2872 if (!knew)
2873 return -ENOMEM;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002874 *knew = ad1988_control_templates[type];
2875 knew->name = kstrdup(name, GFP_KERNEL);
2876 if (! knew->name)
2877 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +01002878 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01002879 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002880 knew->private_value = val;
Takashi Iwaid32410b12005-11-24 16:06:23 +01002881 return 0;
2882}
2883
2884#define AD1988_PIN_CD_NID 0x18
2885#define AD1988_PIN_BEEP_NID 0x10
2886
Takashi Iwai498f5b12011-05-02 11:33:15 +02002887static const hda_nid_t ad1988_mixer_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002888 /* A B C D E F G H */
2889 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
2890};
2891
2892static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
2893{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002894 static const hda_nid_t idx_to_dac[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002895 /* A B C D E F G H */
Raymond Yau356aab72011-08-31 10:30:59 +08002896 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
Takashi Iwaid32410b12005-11-24 16:06:23 +01002897 };
Takashi Iwai498f5b12011-05-02 11:33:15 +02002898 static const hda_nid_t idx_to_dac_rev2[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002899 /* A B C D E F G H */
Raymond Yau356aab72011-08-31 10:30:59 +08002900 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
Takashi Iwaid32410b12005-11-24 16:06:23 +01002901 };
Takashi Iwai1a806f42006-07-03 15:58:16 +02002902 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01002903 return idx_to_dac_rev2[idx];
2904 else
2905 return idx_to_dac[idx];
2906}
2907
Takashi Iwai498f5b12011-05-02 11:33:15 +02002908static const hda_nid_t ad1988_boost_nids[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002909 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
2910};
2911
2912static int ad1988_pin_idx(hda_nid_t nid)
2913{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002914 static const hda_nid_t ad1988_io_pins[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002915 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
2916 };
2917 int i;
2918 for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
2919 if (ad1988_io_pins[i] == nid)
2920 return i;
2921 return 0; /* should be -1 */
2922}
2923
2924static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
2925{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002926 static const int loopback_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002927 2, 0, 1, 3, 4, 5, 1, 4
2928 };
2929 switch (nid) {
2930 case AD1988_PIN_CD_NID:
2931 return 6;
2932 default:
2933 return loopback_idx[ad1988_pin_idx(nid)];
2934 }
2935}
2936
2937static int ad1988_pin_to_adc_idx(hda_nid_t nid)
2938{
Takashi Iwai498f5b12011-05-02 11:33:15 +02002939 static const int adc_idx[8] = {
Takashi Iwaid32410b12005-11-24 16:06:23 +01002940 0, 1, 2, 8, 4, 3, 6, 7
2941 };
2942 switch (nid) {
2943 case AD1988_PIN_CD_NID:
2944 return 5;
2945 default:
2946 return adc_idx[ad1988_pin_idx(nid)];
2947 }
2948}
2949
2950/* fill in the dac_nids table from the parsed pin configuration */
2951static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
2952 const struct auto_pin_cfg *cfg)
2953{
2954 struct ad198x_spec *spec = codec->spec;
2955 int i, idx;
2956
2957 spec->multiout.dac_nids = spec->private_dac_nids;
2958
2959 /* check the pins hardwired to audio widget */
2960 for (i = 0; i < cfg->line_outs; i++) {
2961 idx = ad1988_pin_idx(cfg->line_out_pins[i]);
Takashi Iwaidda14412011-05-02 11:29:30 +02002962 spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx);
Takashi Iwaid32410b12005-11-24 16:06:23 +01002963 }
2964 spec->multiout.num_dacs = cfg->line_outs;
2965 return 0;
2966}
2967
2968/* add playback controls from the parsed DAC table */
2969static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
2970 const struct auto_pin_cfg *cfg)
2971{
2972 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01002973 static const char * const chname[4] = {
2974 "Front", "Surround", NULL /*CLFE*/, "Side"
2975 };
Takashi Iwaid32410b12005-11-24 16:06:23 +01002976 hda_nid_t nid;
2977 int i, err;
2978
2979 for (i = 0; i < cfg->line_outs; i++) {
2980 hda_nid_t dac = spec->multiout.dac_nids[i];
2981 if (! dac)
2982 continue;
2983 nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
2984 if (i == 2) {
2985 /* Center/LFE */
2986 err = add_control(spec, AD_CTL_WIDGET_VOL,
2987 "Center Playback Volume",
2988 HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
2989 if (err < 0)
2990 return err;
2991 err = add_control(spec, AD_CTL_WIDGET_VOL,
2992 "LFE Playback Volume",
2993 HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
2994 if (err < 0)
2995 return err;
2996 err = add_control(spec, AD_CTL_BIND_MUTE,
2997 "Center Playback Switch",
2998 HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
2999 if (err < 0)
3000 return err;
3001 err = add_control(spec, AD_CTL_BIND_MUTE,
3002 "LFE Playback Switch",
3003 HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
3004 if (err < 0)
3005 return err;
3006 } else {
3007 sprintf(name, "%s Playback Volume", chname[i]);
3008 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3009 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
3010 if (err < 0)
3011 return err;
3012 sprintf(name, "%s Playback Switch", chname[i]);
3013 err = add_control(spec, AD_CTL_BIND_MUTE, name,
3014 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
3015 if (err < 0)
3016 return err;
3017 }
3018 }
3019 return 0;
3020}
3021
3022/* add playback controls for speaker and HP outputs */
3023static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
3024 const char *pfx)
3025{
3026 struct ad198x_spec *spec = codec->spec;
3027 hda_nid_t nid;
Takashi Iwai43785ea2008-06-16 15:47:26 +02003028 int i, idx, err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003029 char name[32];
3030
3031 if (! pin)
3032 return 0;
3033
3034 idx = ad1988_pin_idx(pin);
3035 nid = ad1988_idx_to_dac(codec, idx);
Takashi Iwai43785ea2008-06-16 15:47:26 +02003036 /* check whether the corresponding DAC was already taken */
3037 for (i = 0; i < spec->autocfg.line_outs; i++) {
3038 hda_nid_t pin = spec->autocfg.line_out_pins[i];
3039 hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
3040 if (dac == nid)
3041 break;
3042 }
3043 if (i >= spec->autocfg.line_outs) {
3044 /* specify the DAC as the extra output */
3045 if (!spec->multiout.hp_nid)
3046 spec->multiout.hp_nid = nid;
3047 else
3048 spec->multiout.extra_out_nid[0] = nid;
3049 /* control HP volume/switch on the output mixer amp */
3050 sprintf(name, "%s Playback Volume", pfx);
3051 err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3052 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
3053 if (err < 0)
3054 return err;
3055 }
Takashi Iwaid32410b12005-11-24 16:06:23 +01003056 nid = ad1988_mixer_nids[idx];
3057 sprintf(name, "%s Playback Switch", pfx);
3058 if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
3059 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
3060 return err;
3061 return 0;
3062}
3063
3064/* create input playback/capture controls for the given pin */
3065static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
Takashi Iwai9e042e72010-08-30 13:04:44 +02003066 const char *ctlname, int ctlidx, int boost)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003067{
3068 char name[32];
3069 int err, idx;
3070
3071 sprintf(name, "%s Playback Volume", ctlname);
3072 idx = ad1988_pin_to_loopback_idx(pin);
3073 if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
3074 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
3075 return err;
3076 sprintf(name, "%s Playback Switch", ctlname);
3077 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
3078 HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
3079 return err;
3080 if (boost) {
3081 hda_nid_t bnid;
3082 idx = ad1988_pin_idx(pin);
3083 bnid = ad1988_boost_nids[idx];
3084 if (bnid) {
David Henningsson5f99f862011-01-04 15:24:24 +01003085 sprintf(name, "%s Boost Volume", ctlname);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003086 return add_control(spec, AD_CTL_WIDGET_VOL, name,
3087 HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
3088
3089 }
3090 }
3091 return 0;
3092}
3093
3094/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02003095static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
Takashi Iwaid32410b12005-11-24 16:06:23 +01003096 const struct auto_pin_cfg *cfg)
3097{
Takashi Iwai10a20af2010-09-09 16:28:02 +02003098 struct ad198x_spec *spec = codec->spec;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003099 struct hda_input_mux *imux = &spec->private_imux;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003100 int i, err, type, type_idx;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003101
Takashi Iwai9e042e72010-08-30 13:04:44 +02003102 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02003103 const char *label;
Takashi Iwai9e042e72010-08-30 13:04:44 +02003104 type = cfg->inputs[i].type;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003105 label = hda_get_autocfg_input_label(codec, cfg, i);
3106 snd_hda_add_imux_item(imux, label,
3107 ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
3108 &type_idx);
Takashi Iwai9e042e72010-08-30 13:04:44 +02003109 err = new_analog_input(spec, cfg->inputs[i].pin,
Takashi Iwai10a20af2010-09-09 16:28:02 +02003110 label, type_idx,
Takashi Iwai86e29592010-09-09 14:50:17 +02003111 type == AUTO_PIN_MIC);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003112 if (err < 0)
3113 return err;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003114 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02003115 snd_hda_add_imux_item(imux, "Mix", 9, NULL);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003116
3117 if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
3118 "Analog Mix Playback Volume",
3119 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3120 return err;
3121 if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
3122 "Analog Mix Playback Switch",
3123 HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
3124 return err;
3125
3126 return 0;
3127}
3128
3129static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
3130 hda_nid_t nid, int pin_type,
3131 int dac_idx)
3132{
3133 /* set as output */
3134 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
3135 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
3136 switch (nid) {
Raymond Yau356aab72011-08-31 10:30:59 +08003137 case 0x11: /* port-A - DAC 03 */
3138 snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003139 break;
3140 case 0x14: /* port-B - DAC 06 */
3141 snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
3142 break;
3143 case 0x15: /* port-C - DAC 05 */
3144 snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
3145 break;
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003146 case 0x17: /* port-E - DAC 0a */
Takashi Iwaid32410b12005-11-24 16:06:23 +01003147 snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3148 break;
3149 case 0x13: /* mono - DAC 04 */
3150 snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
3151 break;
3152 }
3153}
3154
3155static void ad1988_auto_init_multi_out(struct hda_codec *codec)
3156{
3157 struct ad198x_spec *spec = codec->spec;
3158 int i;
3159
3160 for (i = 0; i < spec->autocfg.line_outs; i++) {
3161 hda_nid_t nid = spec->autocfg.line_out_pins[i];
3162 ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
3163 }
3164}
3165
3166static void ad1988_auto_init_extra_out(struct hda_codec *codec)
3167{
3168 struct ad198x_spec *spec = codec->spec;
3169 hda_nid_t pin;
3170
Takashi Iwai82bc9552006-03-21 11:24:42 +01003171 pin = spec->autocfg.speaker_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003172 if (pin) /* connect to front */
3173 ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003174 pin = spec->autocfg.hp_pins[0];
Takashi Iwaid32410b12005-11-24 16:06:23 +01003175 if (pin) /* connect to front */
3176 ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
3177}
3178
3179static void ad1988_auto_init_analog_input(struct hda_codec *codec)
3180{
3181 struct ad198x_spec *spec = codec->spec;
Takashi Iwai9e042e72010-08-30 13:04:44 +02003182 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003183 int i, idx;
3184
Takashi Iwai9e042e72010-08-30 13:04:44 +02003185 for (i = 0; i < cfg->num_inputs; i++) {
3186 hda_nid_t nid = cfg->inputs[i].pin;
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003187 int type = cfg->inputs[i].type;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003188 switch (nid) {
3189 case 0x15: /* port-C */
3190 snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3191 break;
3192 case 0x17: /* port-E */
3193 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
3194 break;
3195 }
3196 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
Adrian Wilkins5a2d2272011-05-19 21:52:38 +01003197 type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003198 if (nid != AD1988_PIN_CD_NID)
3199 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
3200 AMP_OUT_MUTE);
3201 idx = ad1988_pin_idx(nid);
3202 if (ad1988_boost_nids[idx])
3203 snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
3204 AC_VERB_SET_AMP_GAIN_MUTE,
3205 AMP_OUT_ZERO);
3206 }
3207}
3208
3209/* parse the BIOS configuration and set up the alc_spec */
3210/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
3211static int ad1988_parse_auto_config(struct hda_codec *codec)
3212{
3213 struct ad198x_spec *spec = codec->spec;
3214 int err;
3215
Kailang Yangdf694da2005-12-05 19:42:22 +01003216 if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003217 return err;
3218 if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
3219 return err;
Takashi Iwai82bc9552006-03-21 11:24:42 +01003220 if (! spec->autocfg.line_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003221 return 0; /* can't find valid BIOS pin config */
3222 if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
Takashi Iwai82bc9552006-03-21 11:24:42 +01003223 (err = ad1988_auto_create_extra_out(codec,
3224 spec->autocfg.speaker_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003225 "Speaker")) < 0 ||
Takashi Iwaieb06ed82006-09-20 17:10:27 +02003226 (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
Takashi Iwaid32410b12005-11-24 16:06:23 +01003227 "Headphone")) < 0 ||
Takashi Iwai10a20af2010-09-09 16:28:02 +02003228 (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003229 return err;
3230
3231 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3232
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003233 if (spec->autocfg.dig_outs)
Takashi Iwaid32410b12005-11-24 16:06:23 +01003234 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3235 if (spec->autocfg.dig_in_pin)
3236 spec->dig_in_nid = AD1988_SPDIF_IN;
3237
Takashi Iwai603c4012008-07-30 15:01:44 +02003238 if (spec->kctls.list)
3239 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003240
3241 spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
3242
3243 spec->input_mux = &spec->private_imux;
3244
3245 return 1;
3246}
3247
3248/* init callback for auto-configuration model -- overriding the default init */
3249static int ad1988_auto_init(struct hda_codec *codec)
3250{
3251 ad198x_init(codec);
3252 ad1988_auto_init_multi_out(codec);
3253 ad1988_auto_init_extra_out(codec);
3254 ad1988_auto_init_analog_input(codec);
3255 return 0;
3256}
3257
Takashi Iwaid32410b12005-11-24 16:06:23 +01003258/*
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003259 */
3260
Takashi Iwaiea734962011-01-17 11:29:34 +01003261static const char * const ad1988_models[AD1988_MODEL_LAST] = {
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003262 [AD1988_6STACK] = "6stack",
3263 [AD1988_6STACK_DIG] = "6stack-dig",
3264 [AD1988_3STACK] = "3stack",
3265 [AD1988_3STACK_DIG] = "3stack-dig",
3266 [AD1988_LAPTOP] = "laptop",
3267 [AD1988_LAPTOP_DIG] = "laptop-dig",
3268 [AD1988_AUTO] = "auto",
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003269};
3270
Takashi Iwai498f5b12011-05-02 11:33:15 +02003271static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
Tobin Davis18768992007-03-12 22:20:51 +01003272 SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
Takashi Iwaiac3e3742007-12-17 17:14:18 +01003273 SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
Travis Placeb9e16bc2008-05-21 16:57:20 +02003274 SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
Tony Vroon4e60b4f2011-05-24 22:16:15 +01003275 SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
Robin H. Johnsonf51ff992008-09-13 16:55:01 -07003276 SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003277 {}
3278};
3279
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003280static int patch_ad1988(struct hda_codec *codec)
3281{
3282 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003283 int err, board_config;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003284
3285 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3286 if (spec == NULL)
3287 return -ENOMEM;
3288
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003289 codec->spec = spec;
3290
Takashi Iwai1a806f42006-07-03 15:58:16 +02003291 if (is_rev2(codec))
Takashi Iwaif8c7c7b2005-11-24 16:17:20 +01003292 snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
3293
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003294 board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
Tobin Davisa64c8cd2007-03-12 11:36:00 +01003295 ad1988_models, ad1988_cfg_tbl);
Takashi Iwaif5fcc132006-11-24 17:07:44 +01003296 if (board_config < 0) {
Takashi Iwai9a11f1a2009-07-28 16:01:20 +02003297 printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
3298 codec->chip_name);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003299 board_config = AD1988_AUTO;
3300 }
3301
3302 if (board_config == AD1988_AUTO) {
3303 /* automatic parse from the BIOS config */
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003304 err = ad1988_parse_auto_config(codec);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003305 if (err < 0) {
3306 ad198x_free(codec);
3307 return err;
3308 } else if (! err) {
3309 printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
3310 board_config = AD1988_6STACK;
3311 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003312 }
3313
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003314 err = snd_hda_attach_beep_device(codec, 0x10);
3315 if (err < 0) {
3316 ad198x_free(codec);
3317 return err;
3318 }
3319 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3320
Raymond Yau356aab72011-08-31 10:30:59 +08003321 if (!spec->multiout.hp_nid)
Raymond Yau34588702011-09-23 19:03:25 +08003322 spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003323 switch (board_config) {
3324 case AD1988_6STACK:
3325 case AD1988_6STACK_DIG:
3326 spec->multiout.max_channels = 8;
3327 spec->multiout.num_dacs = 4;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003328 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003329 spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
3330 else
3331 spec->multiout.dac_nids = ad1988_6stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003332 spec->input_mux = &ad1988_6stack_capture_source;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003333 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003334 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003335 spec->mixers[0] = ad1988_6stack_mixers1_rev2;
3336 else
3337 spec->mixers[0] = ad1988_6stack_mixers1;
Raymond Yau28220842011-02-08 19:58:25 +08003338 spec->mixers[1] = ad1988_6stack_mixers2;
3339 spec->num_init_verbs = 1;
3340 spec->init_verbs[0] = ad1988_6stack_init_verbs;
Raymond Yau34588702011-09-23 19:03:25 +08003341 if (board_config == AD1988_6STACK_DIG) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003342 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3343 spec->dig_in_nid = AD1988_SPDIF_IN;
3344 }
3345 break;
3346 case AD1988_3STACK:
3347 case AD1988_3STACK_DIG:
3348 spec->multiout.max_channels = 6;
3349 spec->multiout.num_dacs = 3;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003350 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003351 spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
3352 else
3353 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003354 spec->input_mux = &ad1988_6stack_capture_source;
3355 spec->channel_mode = ad1988_3stack_modes;
3356 spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
Takashi Iwaid32410b12005-11-24 16:06:23 +01003357 spec->num_mixers = 2;
Takashi Iwai1a806f42006-07-03 15:58:16 +02003358 if (is_rev2(codec))
Takashi Iwaid32410b12005-11-24 16:06:23 +01003359 spec->mixers[0] = ad1988_3stack_mixers1_rev2;
3360 else
3361 spec->mixers[0] = ad1988_3stack_mixers1;
3362 spec->mixers[1] = ad1988_3stack_mixers2;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003363 spec->num_init_verbs = 1;
3364 spec->init_verbs[0] = ad1988_3stack_init_verbs;
3365 if (board_config == AD1988_3STACK_DIG)
3366 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3367 break;
3368 case AD1988_LAPTOP:
3369 case AD1988_LAPTOP_DIG:
3370 spec->multiout.max_channels = 2;
3371 spec->multiout.num_dacs = 1;
Takashi Iwaid32410b12005-11-24 16:06:23 +01003372 spec->multiout.dac_nids = ad1988_3stack_dac_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003373 spec->input_mux = &ad1988_laptop_capture_source;
3374 spec->num_mixers = 1;
3375 spec->mixers[0] = ad1988_laptop_mixers;
Takashi Iwaiee6e3652009-12-08 17:23:33 +01003376 spec->inv_eapd = 1; /* inverted EAPD */
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003377 spec->num_init_verbs = 1;
3378 spec->init_verbs[0] = ad1988_laptop_init_verbs;
3379 if (board_config == AD1988_LAPTOP_DIG)
3380 spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
3381 break;
3382 }
3383
Raymond Yau34588702011-09-23 19:03:25 +08003384 if (spec->autocfg.hp_pins[0]) {
3385 spec->mixers[spec->num_mixers++] = ad1988_hp_mixers;
3386 spec->slave_vols = ad1988_6stack_fp_slave_vols;
3387 spec->slave_sws = ad1988_6stack_fp_slave_sws;
3388 spec->alt_dac_nid = ad1988_alt_dac_nid;
3389 spec->stream_analog_alt_playback =
3390 &ad198x_pcm_analog_alt_playback;
3391 }
3392
Takashi Iwaid32410b12005-11-24 16:06:23 +01003393 spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
3394 spec->adc_nids = ad1988_adc_nids;
3395 spec->capsrc_nids = ad1988_capsrc_nids;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003396 spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
3397 spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
3398 if (spec->multiout.dig_out_nid) {
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003399 if (codec->vendor_id >= 0x11d4989a) {
3400 spec->mixers[spec->num_mixers++] =
3401 ad1989_spdif_out_mixers;
3402 spec->init_verbs[spec->num_init_verbs++] =
3403 ad1989_spdif_init_verbs;
Robin H. Johnson9cae0c62008-09-13 16:54:58 -07003404 codec->slave_dig_outs = ad1989b_slave_dig_outs;
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02003405 } else {
3406 spec->mixers[spec->num_mixers++] =
3407 ad1988_spdif_out_mixers;
3408 spec->init_verbs[spec->num_init_verbs++] =
3409 ad1988_spdif_init_verbs;
3410 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003411 }
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003412 if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003413 spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
Jaroslav Kyselafd0b0922010-01-21 14:54:38 +01003414 spec->init_verbs[spec->num_init_verbs++] =
3415 ad1988_spdif_in_init_verbs;
3416 }
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003417
3418 codec->patch_ops = ad198x_patch_ops;
3419 switch (board_config) {
Takashi Iwaid32410b12005-11-24 16:06:23 +01003420 case AD1988_AUTO:
3421 codec->patch_ops.init = ad1988_auto_init;
3422 break;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003423 case AD1988_LAPTOP:
3424 case AD1988_LAPTOP_DIG:
3425 codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
3426 break;
3427 }
Takashi Iwaicb53c622007-08-10 17:21:45 +02003428#ifdef CONFIG_SND_HDA_POWER_SAVE
3429 spec->loopback.amplist = ad1988_loopbacks;
3430#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003431 spec->vmaster_nid = 0x04;
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003432
Takashi Iwai729d55b2009-12-25 22:49:01 +01003433 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003434 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003435
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01003436 return 0;
3437}
3438
3439
3440/*
Takashi Iwai2bac6472007-05-18 18:21:41 +02003441 * AD1884 / AD1984
3442 *
3443 * port-B - front line/mic-in
3444 * port-E - aux in/out
3445 * port-F - aux in/out
3446 * port-C - rear line/mic-in
3447 * port-D - rear line/hp-out
3448 * port-A - front line/hp-out
3449 *
3450 * AD1984 = AD1884 + two digital mic-ins
3451 *
3452 * FIXME:
3453 * For simplicity, we share the single DAC for both HP and line-outs
3454 * right now. The inidividual playbacks could be easily implemented,
3455 * but no build-up framework is given, so far.
3456 */
3457
Takashi Iwai498f5b12011-05-02 11:33:15 +02003458static const hda_nid_t ad1884_dac_nids[1] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003459 0x04,
3460};
3461
Takashi Iwai498f5b12011-05-02 11:33:15 +02003462static const hda_nid_t ad1884_adc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003463 0x08, 0x09,
3464};
3465
Takashi Iwai498f5b12011-05-02 11:33:15 +02003466static const hda_nid_t ad1884_capsrc_nids[2] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003467 0x0c, 0x0d,
3468};
3469
3470#define AD1884_SPDIF_OUT 0x02
3471
Takashi Iwai498f5b12011-05-02 11:33:15 +02003472static const struct hda_input_mux ad1884_capture_source = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003473 .num_items = 4,
3474 .items = {
3475 { "Front Mic", 0x0 },
3476 { "Mic", 0x1 },
3477 { "CD", 0x2 },
3478 { "Mix", 0x3 },
3479 },
3480};
3481
Takashi Iwai498f5b12011-05-02 11:33:15 +02003482static const struct snd_kcontrol_new ad1884_base_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003483 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3484 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3485 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3486 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3487 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3488 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3489 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3490 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3491 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3492 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
3493 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3494 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003495 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3496 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003497 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3498 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3499 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3500 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3501 {
3502 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3503 /* The multiple "Capture Source" controls confuse alsamixer
3504 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003505 */
3506 /* .name = "Capture Source", */
3507 .name = "Input Source",
3508 .count = 2,
3509 .info = ad198x_mux_enum_info,
3510 .get = ad198x_mux_enum_get,
3511 .put = ad198x_mux_enum_put,
3512 },
3513 /* SPDIF controls */
3514 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3515 {
3516 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3517 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3518 /* identical with ad1983 */
3519 .info = ad1983_spdif_route_info,
3520 .get = ad1983_spdif_route_get,
3521 .put = ad1983_spdif_route_put,
3522 },
3523 { } /* end */
3524};
3525
Takashi Iwai498f5b12011-05-02 11:33:15 +02003526static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003527 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
3528 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
3529 HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003530 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003531 HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
Takashi Iwai538c49c2007-06-05 12:13:34 +02003532 HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003533 { } /* end */
3534};
3535
3536/*
3537 * initialization verbs
3538 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003539static const struct hda_verb ad1884_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003540 /* DACs; mute as default */
Takashi Iwai3b194402007-06-04 18:32:23 +02003541 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3542 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003543 /* Port-A (HP) mixer */
3544 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3545 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3546 /* Port-A pin */
3547 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3548 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3549 /* HP selector - select DAC2 */
3550 {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
3551 /* Port-D (Line-out) mixer */
3552 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3553 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3554 /* Port-D pin */
3555 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3556 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3557 /* Mono-out mixer */
3558 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3559 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3560 /* Mono-out pin */
3561 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3562 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3563 /* Mono selector */
3564 {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
3565 /* Port-B (front mic) pin */
3566 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003567 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003568 /* Port-C (rear mic) pin */
3569 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003570 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003571 /* Analog mixer; mute as default */
3572 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3573 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3574 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3575 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3576 /* Analog Mix output amp */
3577 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
3578 /* SPDIF output selector */
3579 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
3580 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
3581 { } /* end */
3582};
3583
Takashi Iwaicb53c622007-08-10 17:21:45 +02003584#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02003585static const struct hda_amp_list ad1884_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02003586 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
3587 { 0x20, HDA_INPUT, 1 }, /* Mic */
3588 { 0x20, HDA_INPUT, 2 }, /* CD */
3589 { 0x20, HDA_INPUT, 4 }, /* Docking */
3590 { } /* end */
3591};
3592#endif
3593
Takashi Iwaiea734962011-01-17 11:29:34 +01003594static const char * const ad1884_slave_vols[] = {
Takashi Iwai2134ea42008-01-10 16:53:55 +01003595 "PCM Playback Volume",
3596 "Mic Playback Volume",
3597 "Mono Playback Volume",
3598 "Front Mic Playback Volume",
3599 "Mic Playback Volume",
3600 "CD Playback Volume",
3601 "Internal Mic Playback Volume",
Akinobu Mitabca68462009-04-06 18:42:42 +09003602 "Docking Mic Playback Volume",
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003603 /* "Beep Playback Volume", */
Takashi Iwai4806ef02008-01-26 09:58:13 +01003604 "IEC958 Playback Volume",
Takashi Iwai2134ea42008-01-10 16:53:55 +01003605 NULL
3606};
3607
Takashi Iwai2bac6472007-05-18 18:21:41 +02003608static int patch_ad1884(struct hda_codec *codec)
3609{
3610 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003611 int err;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003612
3613 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3614 if (spec == NULL)
3615 return -ENOMEM;
3616
Takashi Iwai2bac6472007-05-18 18:21:41 +02003617 codec->spec = spec;
3618
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01003619 err = snd_hda_attach_beep_device(codec, 0x10);
3620 if (err < 0) {
3621 ad198x_free(codec);
3622 return err;
3623 }
3624 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
3625
Takashi Iwai2bac6472007-05-18 18:21:41 +02003626 spec->multiout.max_channels = 2;
3627 spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
3628 spec->multiout.dac_nids = ad1884_dac_nids;
3629 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
3630 spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
3631 spec->adc_nids = ad1884_adc_nids;
3632 spec->capsrc_nids = ad1884_capsrc_nids;
3633 spec->input_mux = &ad1884_capture_source;
3634 spec->num_mixers = 1;
3635 spec->mixers[0] = ad1884_base_mixers;
3636 spec->num_init_verbs = 1;
3637 spec->init_verbs[0] = ad1884_init_verbs;
3638 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003639#ifdef CONFIG_SND_HDA_POWER_SAVE
3640 spec->loopback.amplist = ad1884_loopbacks;
3641#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01003642 spec->vmaster_nid = 0x04;
3643 /* we need to cover all playback volumes */
3644 spec->slave_vols = ad1884_slave_vols;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003645
3646 codec->patch_ops = ad198x_patch_ops;
3647
Takashi Iwai729d55b2009-12-25 22:49:01 +01003648 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02003649 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01003650
Takashi Iwai2bac6472007-05-18 18:21:41 +02003651 return 0;
3652}
3653
3654/*
3655 * Lenovo Thinkpad T61/X61
3656 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003657static const struct hda_input_mux ad1984_thinkpad_capture_source = {
Takashi Iwaib26451c2008-02-26 11:56:35 +01003658 .num_items = 4,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003659 .items = {
3660 { "Mic", 0x0 },
3661 { "Internal Mic", 0x1 },
3662 { "Mix", 0x3 },
Takashi Iwaib26451c2008-02-26 11:56:35 +01003663 { "Docking-Station", 0x4 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003664 },
3665};
3666
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003667
3668/*
3669 * Dell Precision T3400
3670 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003671static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003672 .num_items = 3,
3673 .items = {
3674 { "Front Mic", 0x0 },
3675 { "Line-In", 0x1 },
3676 { "Mix", 0x3 },
3677 },
3678};
3679
3680
Takashi Iwai498f5b12011-05-02 11:33:15 +02003681static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003682 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3683 /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
3684 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3685 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3686 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3687 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3688 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
3689 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003690 HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
3691 HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003692 HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3693 HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003694 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3695 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
3696 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003697 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3698 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3699 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3700 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3701 {
3702 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3703 /* The multiple "Capture Source" controls confuse alsamixer
3704 * So call somewhat different..
Takashi Iwai2bac6472007-05-18 18:21:41 +02003705 */
3706 /* .name = "Capture Source", */
3707 .name = "Input Source",
3708 .count = 2,
3709 .info = ad198x_mux_enum_info,
3710 .get = ad198x_mux_enum_get,
3711 .put = ad198x_mux_enum_put,
3712 },
Jerone Youngebf00c52008-01-07 12:22:18 +01003713 /* SPDIF controls */
3714 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3715 {
3716 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3717 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3718 /* identical with ad1983 */
3719 .info = ad1983_spdif_route_info,
3720 .get = ad1983_spdif_route_get,
3721 .put = ad1983_spdif_route_put,
3722 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02003723 { } /* end */
3724};
3725
3726/* additional verbs */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003727static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003728 /* Port-E (docking station mic) pin */
3729 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
3730 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3731 /* docking mic boost */
Takashi Iwai70040c02009-01-23 14:18:11 +01003732 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003733 /* Analog PC Beeper - allow firmware/ACPI beeps */
3734 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003735 /* Analog mixer - docking mic; mute as default */
3736 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
Takashi Iwaib959d1f2007-06-08 12:25:25 +02003737 /* enable EAPD bit */
3738 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
Takashi Iwai2bac6472007-05-18 18:21:41 +02003739 { } /* end */
3740};
3741
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003742/*
3743 * Dell Precision T3400
3744 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003745static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003746 HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
3747 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3748 HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3749 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3750 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3751 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3752 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3753 HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
3754 HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003755 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
3756 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003757 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3758 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3759 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3760 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3761 {
3762 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3763 /* The multiple "Capture Source" controls confuse alsamixer
3764 * So call somewhat different..
3765 */
3766 /* .name = "Capture Source", */
3767 .name = "Input Source",
3768 .count = 2,
3769 .info = ad198x_mux_enum_info,
3770 .get = ad198x_mux_enum_get,
3771 .put = ad198x_mux_enum_put,
3772 },
3773 { } /* end */
3774};
3775
Takashi Iwai2bac6472007-05-18 18:21:41 +02003776/* Digial MIC ADC NID 0x05 + 0x06 */
3777static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
3778 struct hda_codec *codec,
3779 unsigned int stream_tag,
3780 unsigned int format,
3781 struct snd_pcm_substream *substream)
3782{
3783 snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
3784 stream_tag, 0, format);
3785 return 0;
3786}
3787
3788static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
3789 struct hda_codec *codec,
3790 struct snd_pcm_substream *substream)
3791{
Takashi Iwai888afa12008-03-18 09:57:50 +01003792 snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
Takashi Iwai2bac6472007-05-18 18:21:41 +02003793 return 0;
3794}
3795
Takashi Iwai498f5b12011-05-02 11:33:15 +02003796static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003797 .substreams = 2,
3798 .channels_min = 2,
3799 .channels_max = 2,
3800 .nid = 0x05,
3801 .ops = {
3802 .prepare = ad1984_pcm_dmic_prepare,
3803 .cleanup = ad1984_pcm_dmic_cleanup
3804 },
3805};
3806
3807static int ad1984_build_pcms(struct hda_codec *codec)
3808{
3809 struct ad198x_spec *spec = codec->spec;
3810 struct hda_pcm *info;
3811 int err;
3812
3813 err = ad198x_build_pcms(codec);
3814 if (err < 0)
3815 return err;
3816
3817 info = spec->pcm_rec + codec->num_pcms;
3818 codec->num_pcms++;
3819 info->name = "AD1984 Digital Mic";
3820 info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
3821 return 0;
3822}
3823
3824/* models */
3825enum {
3826 AD1984_BASIC,
3827 AD1984_THINKPAD,
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003828 AD1984_DELL_DESKTOP,
Takashi Iwai2bac6472007-05-18 18:21:41 +02003829 AD1984_MODELS
3830};
3831
Takashi Iwaiea734962011-01-17 11:29:34 +01003832static const char * const ad1984_models[AD1984_MODELS] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003833 [AD1984_BASIC] = "basic",
3834 [AD1984_THINKPAD] = "thinkpad",
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003835 [AD1984_DELL_DESKTOP] = "dell_desktop",
Takashi Iwai2bac6472007-05-18 18:21:41 +02003836};
3837
Takashi Iwai498f5b12011-05-02 11:33:15 +02003838static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
Takashi Iwai2bac6472007-05-18 18:21:41 +02003839 /* Lenovo Thinkpad T61/X61 */
Takashi Iwaidea0a502009-02-09 17:14:52 +01003840 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003841 SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
Luke Yelavich0f9f1ee92010-09-21 17:05:46 +10003842 SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
Takashi Iwai2bac6472007-05-18 18:21:41 +02003843 {}
3844};
3845
3846static int patch_ad1984(struct hda_codec *codec)
3847{
3848 struct ad198x_spec *spec;
3849 int board_config, err;
3850
3851 err = patch_ad1884(codec);
3852 if (err < 0)
3853 return err;
3854 spec = codec->spec;
3855 board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
3856 ad1984_models, ad1984_cfg_tbl);
3857 switch (board_config) {
3858 case AD1984_BASIC:
3859 /* additional digital mics */
3860 spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
3861 codec->patch_ops.build_pcms = ad1984_build_pcms;
3862 break;
3863 case AD1984_THINKPAD:
Jerone Young68c18692010-08-03 01:46:44 -05003864 if (codec->subsystem_id == 0x17aa20fb) {
3865 /* Thinpad X300 does not have the ability to do SPDIF,
3866 or attach to docking station to use SPDIF */
3867 spec->multiout.dig_out_nid = 0;
3868 } else
3869 spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003870 spec->input_mux = &ad1984_thinkpad_capture_source;
3871 spec->mixers[0] = ad1984_thinkpad_mixers;
3872 spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
Jaroslav Kysela0bf0e5a2010-03-26 10:33:18 +01003873 spec->analog_beep = 1;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003874 break;
Douglas Kosovic0aaa22e2008-01-29 15:02:50 +01003875 case AD1984_DELL_DESKTOP:
3876 spec->multiout.dig_out_nid = 0;
3877 spec->input_mux = &ad1984_dell_desktop_capture_source;
3878 spec->mixers[0] = ad1984_dell_desktop_mixers;
3879 break;
Takashi Iwai2bac6472007-05-18 18:21:41 +02003880 }
3881 return 0;
3882}
3883
3884
3885/*
Takashi Iwaic5059252008-02-16 09:43:56 +01003886 * AD1883 / AD1884A / AD1984A / AD1984B
3887 *
3888 * port-B (0x14) - front mic-in
3889 * port-E (0x1c) - rear mic-in
3890 * port-F (0x16) - CD / ext out
3891 * port-C (0x15) - rear line-in
3892 * port-D (0x12) - rear line-out
3893 * port-A (0x11) - front hp-out
3894 *
3895 * AD1984A = AD1884A + digital-mic
3896 * AD1883 = equivalent with AD1984A
3897 * AD1984B = AD1984A + extra SPDIF-out
3898 *
3899 * FIXME:
3900 * We share the single DAC for both HP and line-outs (see AD1884/1984).
3901 */
3902
Takashi Iwai498f5b12011-05-02 11:33:15 +02003903static const hda_nid_t ad1884a_dac_nids[1] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003904 0x03,
3905};
3906
3907#define ad1884a_adc_nids ad1884_adc_nids
3908#define ad1884a_capsrc_nids ad1884_capsrc_nids
3909
3910#define AD1884A_SPDIF_OUT 0x02
3911
Takashi Iwai498f5b12011-05-02 11:33:15 +02003912static const struct hda_input_mux ad1884a_capture_source = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003913 .num_items = 5,
3914 .items = {
3915 { "Front Mic", 0x0 },
3916 { "Mic", 0x4 },
3917 { "Line", 0x1 },
3918 { "CD", 0x2 },
3919 { "Mix", 0x3 },
3920 },
3921};
3922
Takashi Iwai498f5b12011-05-02 11:33:15 +02003923static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003924 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
3925 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
3926 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
3927 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
3928 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
3929 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
3930 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
3931 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
3932 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
3933 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
3934 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
3935 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
3936 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
3937 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
3938 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
3939 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01003940 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
3941 HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
3942 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01003943 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
3944 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
3945 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
3946 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
3947 {
3948 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3949 /* The multiple "Capture Source" controls confuse alsamixer
3950 * So call somewhat different..
3951 */
3952 /* .name = "Capture Source", */
3953 .name = "Input Source",
3954 .count = 2,
3955 .info = ad198x_mux_enum_info,
3956 .get = ad198x_mux_enum_get,
3957 .put = ad198x_mux_enum_put,
3958 },
3959 /* SPDIF controls */
3960 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
3961 {
3962 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3963 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
3964 /* identical with ad1983 */
3965 .info = ad1983_spdif_route_info,
3966 .get = ad1983_spdif_route_get,
3967 .put = ad1983_spdif_route_put,
3968 },
3969 { } /* end */
3970};
3971
3972/*
3973 * initialization verbs
3974 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02003975static const struct hda_verb ad1884a_init_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01003976 /* DACs; unmute as default */
3977 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3978 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
3979 /* Port-A (HP) mixer - route only from analog mixer */
3980 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3981 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3982 /* Port-A pin */
3983 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3984 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3985 /* Port-D (Line-out) mixer - route only from analog mixer */
3986 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3987 {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3988 /* Port-D pin */
3989 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3990 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3991 /* Mono-out mixer - route only from analog mixer */
3992 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3993 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3994 /* Mono-out pin */
3995 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
3996 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
3997 /* Port-B (front mic) pin */
3998 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
Takashi Iwai60e388e2009-01-23 12:37:09 +01003999 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004000 /* Port-C (rear line-in) pin */
4001 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai60e388e2009-01-23 12:37:09 +01004002 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
Takashi Iwaic5059252008-02-16 09:43:56 +01004003 /* Port-E (rear mic) pin */
4004 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4005 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4006 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
4007 /* Port-F (CD) pin */
4008 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4009 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4010 /* Analog mixer; mute as default */
4011 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4012 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4013 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4014 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4015 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
4016 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4017 /* Analog Mix output amp */
4018 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4019 /* capture sources */
4020 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
4021 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4022 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4023 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4024 /* SPDIF output amp */
4025 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4026 { } /* end */
4027};
4028
4029#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02004030static const struct hda_amp_list ad1884a_loopbacks[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004031 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4032 { 0x20, HDA_INPUT, 1 }, /* Mic */
4033 { 0x20, HDA_INPUT, 2 }, /* CD */
4034 { 0x20, HDA_INPUT, 4 }, /* Docking */
4035 { } /* end */
4036};
4037#endif
4038
4039/*
4040 * Laptop model
4041 *
4042 * Port A: Headphone jack
4043 * Port B: MIC jack
4044 * Port C: Internal MIC
4045 * Port D: Dock Line Out (if enabled)
4046 * Port E: Dock Line In (if enabled)
4047 * Port F: Internal speakers
4048 */
4049
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004050static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
4051 struct snd_ctl_elem_value *ucontrol)
4052{
4053 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4054 int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
4055 int mute = (!ucontrol->value.integer.value[0] &&
4056 !ucontrol->value.integer.value[1]);
4057 /* toggle GPIO1 according to the mute state */
4058 snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
4059 mute ? 0x02 : 0x0);
4060 return ret;
4061}
Takashi Iwaic5059252008-02-16 09:43:56 +01004062
Takashi Iwai498f5b12011-05-02 11:33:15 +02004063static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004064 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004065 {
4066 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4067 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004068 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004069 .info = snd_hda_mixer_amp_switch_info,
4070 .get = snd_hda_mixer_amp_switch_get,
4071 .put = ad1884a_mobile_master_sw_put,
4072 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4073 },
Takashi Iwaic5059252008-02-16 09:43:56 +01004074 HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4075 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4076 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4077 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4078 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4079 HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4080 HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4081 HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4082 HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004083 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4084 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4085 HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004086 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4087 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaic5059252008-02-16 09:43:56 +01004088 { } /* end */
4089};
4090
Takashi Iwai498f5b12011-05-02 11:33:15 +02004091static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004092 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
Takashi Iwai099db17e2009-07-02 16:10:23 +02004093 /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4094 {
4095 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4096 .name = "Master Playback Switch",
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004097 .subdevice = HDA_SUBDEV_AMP_FLAG,
Takashi Iwai099db17e2009-07-02 16:10:23 +02004098 .info = snd_hda_mixer_amp_switch_info,
4099 .get = snd_hda_mixer_amp_switch_get,
4100 .put = ad1884a_mobile_master_sw_put,
4101 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4102 },
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004103 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4104 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
Takashi Iwai269ef192008-05-30 15:32:15 +02004105 HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
4106 HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004107 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4108 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004109 { } /* end */
4110};
4111
Takashi Iwaic5059252008-02-16 09:43:56 +01004112/* mute internal speaker if HP is plugged */
4113static void ad1884a_hp_automute(struct hda_codec *codec)
4114{
4115 unsigned int present;
4116
Takashi Iwaid56757a2009-11-18 08:00:14 +01004117 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaic5059252008-02-16 09:43:56 +01004118 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4119 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4120 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4121 present ? 0x00 : 0x02);
4122}
4123
Takashi Iwai269ef192008-05-30 15:32:15 +02004124/* switch to external mic if plugged */
4125static void ad1884a_hp_automic(struct hda_codec *codec)
4126{
4127 unsigned int present;
4128
Takashi Iwaid56757a2009-11-18 08:00:14 +01004129 present = snd_hda_jack_detect(codec, 0x14);
Takashi Iwai269ef192008-05-30 15:32:15 +02004130 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
4131 present ? 0 : 1);
4132}
4133
Takashi Iwaic5059252008-02-16 09:43:56 +01004134#define AD1884A_HP_EVENT 0x37
Takashi Iwai269ef192008-05-30 15:32:15 +02004135#define AD1884A_MIC_EVENT 0x36
Takashi Iwaic5059252008-02-16 09:43:56 +01004136
4137/* unsolicited event for HP jack sensing */
4138static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
4139{
Takashi Iwai269ef192008-05-30 15:32:15 +02004140 switch (res >> 26) {
4141 case AD1884A_HP_EVENT:
4142 ad1884a_hp_automute(codec);
4143 break;
4144 case AD1884A_MIC_EVENT:
4145 ad1884a_hp_automic(codec);
4146 break;
4147 }
Takashi Iwaic5059252008-02-16 09:43:56 +01004148}
4149
4150/* initialize jack-sensing, too */
4151static int ad1884a_hp_init(struct hda_codec *codec)
4152{
4153 ad198x_init(codec);
4154 ad1884a_hp_automute(codec);
Takashi Iwai269ef192008-05-30 15:32:15 +02004155 ad1884a_hp_automic(codec);
Takashi Iwaic5059252008-02-16 09:43:56 +01004156 return 0;
4157}
4158
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004159/* mute internal speaker if HP or docking HP is plugged */
4160static void ad1884a_laptop_automute(struct hda_codec *codec)
4161{
4162 unsigned int present;
4163
Takashi Iwaid56757a2009-11-18 08:00:14 +01004164 present = snd_hda_jack_detect(codec, 0x11);
4165 if (!present)
4166 present = snd_hda_jack_detect(codec, 0x12);
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004167 snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
4168 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4169 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
4170 present ? 0x00 : 0x02);
4171}
4172
4173/* switch to external mic if plugged */
4174static void ad1884a_laptop_automic(struct hda_codec *codec)
4175{
4176 unsigned int idx;
4177
Takashi Iwaid56757a2009-11-18 08:00:14 +01004178 if (snd_hda_jack_detect(codec, 0x14))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004179 idx = 0;
Takashi Iwaid56757a2009-11-18 08:00:14 +01004180 else if (snd_hda_jack_detect(codec, 0x1c))
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004181 idx = 4;
4182 else
4183 idx = 1;
4184 snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
4185}
4186
4187/* unsolicited event for HP jack sensing */
4188static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
4189 unsigned int res)
4190{
4191 switch (res >> 26) {
4192 case AD1884A_HP_EVENT:
4193 ad1884a_laptop_automute(codec);
4194 break;
4195 case AD1884A_MIC_EVENT:
4196 ad1884a_laptop_automic(codec);
4197 break;
4198 }
4199}
4200
4201/* initialize jack-sensing, too */
4202static int ad1884a_laptop_init(struct hda_codec *codec)
4203{
4204 ad198x_init(codec);
4205 ad1884a_laptop_automute(codec);
4206 ad1884a_laptop_automic(codec);
4207 return 0;
4208}
4209
Takashi Iwaic5059252008-02-16 09:43:56 +01004210/* additional verbs for laptop model */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004211static const struct hda_verb ad1884a_laptop_verbs[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004212 /* Port-A (HP) pin - always unmuted */
4213 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4214 /* Port-F (int speaker) mixer - route only from analog mixer */
4215 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4216 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
Wu Fengguang150fe142009-08-19 16:58:59 +08004217 /* Port-F (int speaker) pin */
4218 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwaic5059252008-02-16 09:43:56 +01004219 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
Wu Fengguang150fe142009-08-19 16:58:59 +08004220 /* required for compaq 6530s/6531s speaker output */
4221 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004222 /* Port-C pin - internal mic-in */
4223 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4224 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4225 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
Takashi Iwai2ad81ba2009-09-01 09:09:26 +02004226 /* Port-D (docking line-out) pin - default unmuted */
4227 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
Takashi Iwaic5059252008-02-16 09:43:56 +01004228 /* analog mix */
4229 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4230 /* unsolicited event for pin-sense */
4231 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004232 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
Takashi Iwai269ef192008-05-30 15:32:15 +02004233 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004234 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwaife7e5682009-08-31 08:37:46 +02004235 /* allow to touch GPIO1 (for mute control) */
4236 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4237 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4238 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwaic5059252008-02-16 09:43:56 +01004239 { } /* end */
4240};
4241
Takashi Iwai498f5b12011-05-02 11:33:15 +02004242static const struct hda_verb ad1884a_mobile_verbs[] = {
Takashi Iwai73156132009-04-23 08:24:48 +02004243 /* DACs; unmute as default */
4244 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4245 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4246 /* Port-A (HP) mixer - route only from analog mixer */
4247 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4248 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4249 /* Port-A pin */
4250 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4251 /* Port-A (HP) pin - always unmuted */
4252 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4253 /* Port-B (mic jack) pin */
4254 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4255 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4256 /* Port-C (int mic) pin */
4257 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4258 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4259 /* Port-F (int speaker) mixer - route only from analog mixer */
4260 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4261 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4262 /* Port-F pin */
4263 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4264 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4265 /* Analog mixer; mute as default */
4266 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4267 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4268 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4269 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4270 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4271 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4272 /* Analog Mix output amp */
4273 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4274 /* capture sources */
4275 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4276 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4277 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4278 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4279 /* unsolicited event for pin-sense */
4280 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4281 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
Takashi Iwai099db17e2009-07-02 16:10:23 +02004282 /* allow to touch GPIO1 (for mute control) */
4283 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4284 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4285 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
Takashi Iwai73156132009-04-23 08:24:48 +02004286 { } /* end */
4287};
4288
Takashi Iwaic5059252008-02-16 09:43:56 +01004289/*
Takashi Iwaif0813742008-03-18 12:13:03 +01004290 * Thinkpad X300
4291 * 0x11 - HP
4292 * 0x12 - speaker
4293 * 0x14 - mic-in
4294 * 0x17 - built-in mic
4295 */
4296
Takashi Iwai498f5b12011-05-02 11:33:15 +02004297static const struct hda_verb ad1984a_thinkpad_verbs[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004298 /* HP unmute */
4299 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4300 /* analog mix */
4301 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4302 /* turn on EAPD */
4303 {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4304 /* unsolicited event for pin-sense */
4305 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4306 /* internal mic - dmic */
4307 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
Takashi Iwai05808ec2008-04-23 13:50:08 +02004308 /* set magic COEFs for dmic */
4309 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4310 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
Takashi Iwaif0813742008-03-18 12:13:03 +01004311 { } /* end */
4312};
4313
Takashi Iwai498f5b12011-05-02 11:33:15 +02004314static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004315 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4316 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4317 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4318 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4319 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4320 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004321 HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
4322 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Takashi Iwaif0813742008-03-18 12:13:03 +01004323 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4324 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4325 {
4326 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4327 .name = "Capture Source",
4328 .info = ad198x_mux_enum_info,
4329 .get = ad198x_mux_enum_get,
4330 .put = ad198x_mux_enum_put,
4331 },
4332 { } /* end */
4333};
4334
Takashi Iwai498f5b12011-05-02 11:33:15 +02004335static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
Takashi Iwaif0813742008-03-18 12:13:03 +01004336 .num_items = 3,
4337 .items = {
4338 { "Mic", 0x0 },
4339 { "Internal Mic", 0x5 },
4340 { "Mix", 0x3 },
4341 },
4342};
4343
4344/* mute internal speaker if HP is plugged */
4345static void ad1984a_thinkpad_automute(struct hda_codec *codec)
4346{
4347 unsigned int present;
4348
Takashi Iwaid56757a2009-11-18 08:00:14 +01004349 present = snd_hda_jack_detect(codec, 0x11);
Takashi Iwaif0813742008-03-18 12:13:03 +01004350 snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
4351 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4352}
4353
4354/* unsolicited event for HP jack sensing */
4355static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
4356 unsigned int res)
4357{
4358 if ((res >> 26) != AD1884A_HP_EVENT)
4359 return;
4360 ad1984a_thinkpad_automute(codec);
4361}
4362
4363/* initialize jack-sensing, too */
4364static int ad1984a_thinkpad_init(struct hda_codec *codec)
4365{
4366 ad198x_init(codec);
4367 ad1984a_thinkpad_automute(codec);
4368 return 0;
4369}
4370
4371/*
David Henningsson677cd902011-02-07 15:19:34 +01004372 * Precision R5500
4373 * 0x12 - HP/line-out
4374 * 0x13 - speaker (mono)
4375 * 0x15 - mic-in
4376 */
4377
Takashi Iwai498f5b12011-05-02 11:33:15 +02004378static const struct hda_verb ad1984a_precision_verbs[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004379 /* Unmute main output path */
4380 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4381 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
4382 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
4383 /* Analog mixer; mute as default */
4384 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4385 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4386 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4387 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4388 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4389 /* Select mic as input */
4390 {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
4391 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
4392 /* Configure as mic */
4393 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4394 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
4395 /* HP unmute */
4396 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4397 /* turn on EAPD */
4398 {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
4399 /* unsolicited event for pin-sense */
4400 {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4401 { } /* end */
4402};
4403
Takashi Iwai498f5b12011-05-02 11:33:15 +02004404static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004405 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4406 HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
4407 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4408 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4409 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4410 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4411 HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
4412 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4413 HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
4414 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4415 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4416 { } /* end */
4417};
4418
4419
4420/* mute internal speaker if HP is plugged */
4421static void ad1984a_precision_automute(struct hda_codec *codec)
4422{
4423 unsigned int present;
4424
4425 present = snd_hda_jack_detect(codec, 0x12);
4426 snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
4427 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
4428}
4429
4430
4431/* unsolicited event for HP jack sensing */
4432static void ad1984a_precision_unsol_event(struct hda_codec *codec,
4433 unsigned int res)
4434{
4435 if ((res >> 26) != AD1884A_HP_EVENT)
4436 return;
4437 ad1984a_precision_automute(codec);
4438}
4439
4440/* initialize jack-sensing, too */
4441static int ad1984a_precision_init(struct hda_codec *codec)
4442{
4443 ad198x_init(codec);
4444 ad1984a_precision_automute(codec);
4445 return 0;
4446}
4447
4448
4449/*
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004450 * HP Touchsmart
4451 * port-A (0x11) - front hp-out
4452 * port-B (0x14) - unused
4453 * port-C (0x15) - unused
4454 * port-D (0x12) - rear line out
4455 * port-E (0x1c) - front mic-in
4456 * port-F (0x16) - Internal speakers
4457 * digital-mic (0x17) - Internal mic
4458 */
4459
Takashi Iwai498f5b12011-05-02 11:33:15 +02004460static const struct hda_verb ad1984a_touchsmart_verbs[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004461 /* DACs; unmute as default */
4462 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4463 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
4464 /* Port-A (HP) mixer - route only from analog mixer */
4465 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4466 {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4467 /* Port-A pin */
4468 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4469 /* Port-A (HP) pin - always unmuted */
4470 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4471 /* Port-E (int speaker) mixer - route only from analog mixer */
4472 {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
4473 /* Port-E pin */
4474 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4475 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4476 {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4477 /* Port-F (int speaker) mixer - route only from analog mixer */
4478 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4479 {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4480 /* Port-F pin */
4481 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4482 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4483 /* Analog mixer; mute as default */
4484 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4485 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4486 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4487 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4488 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4489 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4490 /* Analog Mix output amp */
4491 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4492 /* capture sources */
4493 /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
4494 {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4495 {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
4496 {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4497 /* unsolicited event for pin-sense */
4498 {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
4499 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
4500 /* allow to touch GPIO1 (for mute control) */
4501 {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
4502 {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
4503 {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
4504 /* internal mic - dmic */
4505 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4506 /* set magic COEFs for dmic */
4507 {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
4508 {0x01, AC_VERB_SET_PROC_COEF, 0x08},
4509 { } /* end */
4510};
4511
Takashi Iwai498f5b12011-05-02 11:33:15 +02004512static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004513 HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
4514/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
4515 {
4516 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +01004517 .subdevice = HDA_SUBDEV_AMP_FLAG,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004518 .name = "Master Playback Switch",
4519 .info = snd_hda_mixer_amp_switch_info,
4520 .get = snd_hda_mixer_amp_switch_get,
4521 .put = ad1884a_mobile_master_sw_put,
4522 .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
4523 },
4524 HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
4525 HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
4526 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4527 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004528 HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
4529 HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004530 { } /* end */
4531};
4532
4533/* switch to external mic if plugged */
4534static void ad1984a_touchsmart_automic(struct hda_codec *codec)
4535{
Takashi Iwaid56757a2009-11-18 08:00:14 +01004536 if (snd_hda_jack_detect(codec, 0x1c))
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004537 snd_hda_codec_write(codec, 0x0c, 0,
4538 AC_VERB_SET_CONNECT_SEL, 0x4);
Takashi Iwaid56757a2009-11-18 08:00:14 +01004539 else
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004540 snd_hda_codec_write(codec, 0x0c, 0,
4541 AC_VERB_SET_CONNECT_SEL, 0x5);
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004542}
4543
4544
4545/* unsolicited event for HP jack sensing */
4546static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
4547 unsigned int res)
4548{
4549 switch (res >> 26) {
4550 case AD1884A_HP_EVENT:
4551 ad1884a_hp_automute(codec);
4552 break;
4553 case AD1884A_MIC_EVENT:
4554 ad1984a_touchsmart_automic(codec);
4555 break;
4556 }
4557}
4558
4559/* initialize jack-sensing, too */
4560static int ad1984a_touchsmart_init(struct hda_codec *codec)
4561{
4562 ad198x_init(codec);
4563 ad1884a_hp_automute(codec);
4564 ad1984a_touchsmart_automic(codec);
4565 return 0;
4566}
4567
4568
4569/*
Takashi Iwaic5059252008-02-16 09:43:56 +01004570 */
4571
4572enum {
4573 AD1884A_DESKTOP,
4574 AD1884A_LAPTOP,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004575 AD1884A_MOBILE,
Takashi Iwaif0813742008-03-18 12:13:03 +01004576 AD1884A_THINKPAD,
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004577 AD1984A_TOUCHSMART,
David Henningsson677cd902011-02-07 15:19:34 +01004578 AD1984A_PRECISION,
Takashi Iwaic5059252008-02-16 09:43:56 +01004579 AD1884A_MODELS
4580};
4581
Takashi Iwaiea734962011-01-17 11:29:34 +01004582static const char * const ad1884a_models[AD1884A_MODELS] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01004583 [AD1884A_DESKTOP] = "desktop",
4584 [AD1884A_LAPTOP] = "laptop",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004585 [AD1884A_MOBILE] = "mobile",
Takashi Iwaif0813742008-03-18 12:13:03 +01004586 [AD1884A_THINKPAD] = "thinkpad",
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004587 [AD1984A_TOUCHSMART] = "touchsmart",
David Henningsson677cd902011-02-07 15:19:34 +01004588 [AD1984A_PRECISION] = "precision",
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004589};
4590
Takashi Iwai498f5b12011-05-02 11:33:15 +02004591static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
David Henningsson677cd902011-02-07 15:19:34 +01004592 SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004593 SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
Takashi Iwaid5337de2009-01-07 11:41:57 +01004594 SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
Takashi Iwai5695ff42008-10-28 15:39:26 +01004595 SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
Takashi Iwaic2312752009-02-16 15:20:41 +01004596 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
Takashi Iwaiff848472009-07-01 18:08:01 +02004597 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai873dc782009-02-25 18:12:13 +01004598 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
4599 SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
Takashi Iwai286f5872009-08-27 14:37:51 +02004600 SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
Takashi Iwaif0813742008-03-18 12:13:03 +01004601 SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004602 SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004603 {}
Takashi Iwaic5059252008-02-16 09:43:56 +01004604};
4605
4606static int patch_ad1884a(struct hda_codec *codec)
4607{
4608 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004609 int err, board_config;
Takashi Iwaic5059252008-02-16 09:43:56 +01004610
4611 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4612 if (spec == NULL)
4613 return -ENOMEM;
4614
Takashi Iwaic5059252008-02-16 09:43:56 +01004615 codec->spec = spec;
4616
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004617 err = snd_hda_attach_beep_device(codec, 0x10);
4618 if (err < 0) {
4619 ad198x_free(codec);
4620 return err;
4621 }
4622 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
4623
Takashi Iwaic5059252008-02-16 09:43:56 +01004624 spec->multiout.max_channels = 2;
4625 spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
4626 spec->multiout.dac_nids = ad1884a_dac_nids;
4627 spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
4628 spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
4629 spec->adc_nids = ad1884a_adc_nids;
4630 spec->capsrc_nids = ad1884a_capsrc_nids;
4631 spec->input_mux = &ad1884a_capture_source;
4632 spec->num_mixers = 1;
4633 spec->mixers[0] = ad1884a_base_mixers;
4634 spec->num_init_verbs = 1;
4635 spec->init_verbs[0] = ad1884a_init_verbs;
4636 spec->spdif_route = 0;
4637#ifdef CONFIG_SND_HDA_POWER_SAVE
4638 spec->loopback.amplist = ad1884a_loopbacks;
4639#endif
4640 codec->patch_ops = ad198x_patch_ops;
4641
4642 /* override some parameters */
4643 board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004644 ad1884a_models,
4645 ad1884a_cfg_tbl);
Takashi Iwaic5059252008-02-16 09:43:56 +01004646 switch (board_config) {
4647 case AD1884A_LAPTOP:
4648 spec->mixers[0] = ad1884a_laptop_mixers;
4649 spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
4650 spec->multiout.dig_out_nid = 0;
Takashi Iwai17bbaa62009-08-30 12:15:59 +02004651 codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
4652 codec->patch_ops.init = ad1884a_laptop_init;
Takashi Iwai4dc1f872009-04-16 14:19:19 +02004653 /* set the upper-limit for mixer amp to 0dB for avoiding the
4654 * possible damage by overloading
4655 */
4656 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4657 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4658 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4659 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4660 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaic5059252008-02-16 09:43:56 +01004661 break;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004662 case AD1884A_MOBILE:
4663 spec->mixers[0] = ad1884a_mobile_mixers;
Takashi Iwai73156132009-04-23 08:24:48 +02004664 spec->init_verbs[0] = ad1884a_mobile_verbs;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004665 spec->multiout.dig_out_nid = 0;
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004666 codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
4667 codec->patch_ops.init = ad1884a_hp_init;
Takashi Iwai13c989b2009-02-23 11:33:34 +01004668 /* set the upper-limit for mixer amp to 0dB for avoiding the
4669 * possible damage by overloading
4670 */
4671 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4672 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4673 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4674 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4675 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwaib40b04a2008-02-16 09:44:56 +01004676 break;
Takashi Iwaif0813742008-03-18 12:13:03 +01004677 case AD1884A_THINKPAD:
4678 spec->mixers[0] = ad1984a_thinkpad_mixers;
4679 spec->init_verbs[spec->num_init_verbs++] =
4680 ad1984a_thinkpad_verbs;
4681 spec->multiout.dig_out_nid = 0;
4682 spec->input_mux = &ad1984a_thinkpad_capture_source;
4683 codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
4684 codec->patch_ops.init = ad1984a_thinkpad_init;
4685 break;
David Henningsson677cd902011-02-07 15:19:34 +01004686 case AD1984A_PRECISION:
4687 spec->mixers[0] = ad1984a_precision_mixers;
4688 spec->init_verbs[spec->num_init_verbs++] =
4689 ad1984a_precision_verbs;
4690 spec->multiout.dig_out_nid = 0;
4691 codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
4692 codec->patch_ops.init = ad1984a_precision_init;
4693 break;
Miguel de Barrosa72cb4b2009-09-27 22:11:21 +02004694 case AD1984A_TOUCHSMART:
4695 spec->mixers[0] = ad1984a_touchsmart_mixers;
4696 spec->init_verbs[0] = ad1984a_touchsmart_verbs;
4697 spec->multiout.dig_out_nid = 0;
4698 codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
4699 codec->patch_ops.init = ad1984a_touchsmart_init;
4700 /* set the upper-limit for mixer amp to 0dB for avoiding the
4701 * possible damage by overloading
4702 */
4703 snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
4704 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4705 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4706 (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4707 (1 << AC_AMPCAP_MUTE_SHIFT));
4708 break;
Takashi Iwaic5059252008-02-16 09:43:56 +01004709 }
4710
Takashi Iwai729d55b2009-12-25 22:49:01 +01004711 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02004712 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01004713
Takashi Iwaic5059252008-02-16 09:43:56 +01004714 return 0;
4715}
4716
4717
4718/*
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004719 * AD1882 / AD1882A
Takashi Iwai0ac85512007-06-20 15:46:13 +02004720 *
4721 * port-A - front hp-out
4722 * port-B - front mic-in
4723 * port-C - rear line-in, shared surr-out (3stack)
4724 * port-D - rear line-out
4725 * port-E - rear mic-in, shared clfe-out (3stack)
4726 * port-F - rear surr-out (6stack)
4727 * port-G - rear clfe-out (6stack)
4728 */
4729
Takashi Iwai498f5b12011-05-02 11:33:15 +02004730static const hda_nid_t ad1882_dac_nids[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004731 0x04, 0x03, 0x05
4732};
4733
Takashi Iwai498f5b12011-05-02 11:33:15 +02004734static const hda_nid_t ad1882_adc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004735 0x08, 0x09,
4736};
4737
Takashi Iwai498f5b12011-05-02 11:33:15 +02004738static const hda_nid_t ad1882_capsrc_nids[2] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004739 0x0c, 0x0d,
4740};
4741
4742#define AD1882_SPDIF_OUT 0x02
4743
4744/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004745static const struct hda_input_mux ad1882_capture_source = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004746 .num_items = 5,
4747 .items = {
4748 { "Front Mic", 0x1 },
4749 { "Mic", 0x4 },
4750 { "Line", 0x2 },
4751 { "CD", 0x3 },
4752 { "Mix", 0x7 },
4753 },
4754};
4755
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004756/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004757static const struct hda_input_mux ad1882a_capture_source = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004758 .num_items = 5,
4759 .items = {
4760 { "Front Mic", 0x1 },
4761 { "Mic", 0x4},
4762 { "Line", 0x2 },
4763 { "Digital Mic", 0x06 },
4764 { "Mix", 0x7 },
4765 },
4766};
4767
Takashi Iwai498f5b12011-05-02 11:33:15 +02004768static const struct snd_kcontrol_new ad1882_base_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004769 HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
4770 HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
4771 HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
4772 HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
4773 HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
4774 HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
4775 HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
4776 HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004777
David Henningsson5f99f862011-01-04 15:24:24 +01004778 HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
4779 HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
4780 HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
Takashi Iwai0ac85512007-06-20 15:46:13 +02004781 HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
4782 HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
4783 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
4784 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
4785 {
4786 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4787 /* The multiple "Capture Source" controls confuse alsamixer
4788 * So call somewhat different..
Takashi Iwai0ac85512007-06-20 15:46:13 +02004789 */
4790 /* .name = "Capture Source", */
4791 .name = "Input Source",
4792 .count = 2,
4793 .info = ad198x_mux_enum_info,
4794 .get = ad198x_mux_enum_get,
4795 .put = ad198x_mux_enum_put,
4796 },
4797 /* SPDIF controls */
4798 HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
4799 {
4800 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4801 .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
4802 /* identical with ad1983 */
4803 .info = ad1983_spdif_route_info,
4804 .get = ad1983_spdif_route_get,
4805 .put = ad1983_spdif_route_put,
4806 },
4807 { } /* end */
4808};
4809
Takashi Iwai498f5b12011-05-02 11:33:15 +02004810static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004811 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4812 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4813 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
4814 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
4815 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
4816 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
4817 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4818 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004819 { } /* end */
4820};
4821
Takashi Iwai498f5b12011-05-02 11:33:15 +02004822static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004823 HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
4824 HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
4825 HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
4826 HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
4827 HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
4828 HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
4829 HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
4830 HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
David Henningsson5f99f862011-01-04 15:24:24 +01004831 HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02004832 { } /* end */
4833};
4834
Takashi Iwai498f5b12011-05-02 11:33:15 +02004835static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004836 HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
4837 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
4838 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
4839 {
4840 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4841 .name = "Channel Mode",
4842 .info = ad198x_ch_mode_info,
4843 .get = ad198x_ch_mode_get,
4844 .put = ad198x_ch_mode_put,
4845 },
4846 { } /* end */
4847};
4848
Takashi Iwai498f5b12011-05-02 11:33:15 +02004849static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004850 HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
4851 HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
4852 HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
4853 { } /* end */
4854};
4855
Takashi Iwai498f5b12011-05-02 11:33:15 +02004856static const struct hda_verb ad1882_ch2_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004857 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4858 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4859 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4860 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4861 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4862 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4863 { } /* end */
4864};
4865
Takashi Iwai498f5b12011-05-02 11:33:15 +02004866static const struct hda_verb ad1882_ch4_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004867 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4868 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4869 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4870 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4871 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4872 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4873 { } /* end */
4874};
4875
Takashi Iwai498f5b12011-05-02 11:33:15 +02004876static const struct hda_verb ad1882_ch6_init[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004877 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4878 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4879 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4880 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4881 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4882 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4883 { } /* end */
4884};
4885
Takashi Iwai498f5b12011-05-02 11:33:15 +02004886static const struct hda_channel_mode ad1882_modes[3] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004887 { 2, ad1882_ch2_init },
4888 { 4, ad1882_ch4_init },
4889 { 6, ad1882_ch6_init },
4890};
4891
4892/*
4893 * initialization verbs
4894 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02004895static const struct hda_verb ad1882_init_verbs[] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004896 /* DACs; mute as default */
4897 {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4898 {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4899 {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
4900 /* Port-A (HP) mixer */
4901 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4902 {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4903 /* Port-A pin */
4904 {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4905 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4906 /* HP selector - select DAC2 */
4907 {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
4908 /* Port-D (Line-out) mixer */
4909 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4910 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4911 /* Port-D pin */
4912 {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4913 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4914 /* Mono-out mixer */
4915 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4916 {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4917 /* Mono-out pin */
4918 {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
4919 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4920 /* Port-B (front mic) pin */
4921 {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4922 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4923 {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4924 /* Port-C (line-in) pin */
4925 {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
4926 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4927 {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4928 /* Port-C mixer - mute as input */
4929 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4930 {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4931 /* Port-E (mic-in) pin */
4932 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
4933 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4934 {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
4935 /* Port-E mixer - mute as input */
4936 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4937 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4938 /* Port-F (surround) */
4939 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4940 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4941 /* Port-G (CLFE) */
4942 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
4943 {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
4944 /* Analog mixer; mute as default */
4945 /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
4946 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4947 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4948 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4949 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4950 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4951 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4952 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
4953 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
4954 /* Analog Mix output amp */
4955 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
4956 /* SPDIF output selector */
4957 {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4958 {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
4959 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
4960 { } /* end */
4961};
4962
Takashi Iwaicb53c622007-08-10 17:21:45 +02004963#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai498f5b12011-05-02 11:33:15 +02004964static const struct hda_amp_list ad1882_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02004965 { 0x20, HDA_INPUT, 0 }, /* Front Mic */
4966 { 0x20, HDA_INPUT, 1 }, /* Mic */
4967 { 0x20, HDA_INPUT, 4 }, /* Line */
4968 { 0x20, HDA_INPUT, 6 }, /* CD */
4969 { } /* end */
4970};
4971#endif
4972
Takashi Iwai0ac85512007-06-20 15:46:13 +02004973/* models */
4974enum {
4975 AD1882_3STACK,
4976 AD1882_6STACK,
4977 AD1882_MODELS
4978};
4979
Takashi Iwaiea734962011-01-17 11:29:34 +01004980static const char * const ad1882_models[AD1986A_MODELS] = {
Takashi Iwai0ac85512007-06-20 15:46:13 +02004981 [AD1882_3STACK] = "3stack",
4982 [AD1882_6STACK] = "6stack",
4983};
4984
4985
4986static int patch_ad1882(struct hda_codec *codec)
4987{
4988 struct ad198x_spec *spec;
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004989 int err, board_config;
Takashi Iwai0ac85512007-06-20 15:46:13 +02004990
4991 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4992 if (spec == NULL)
4993 return -ENOMEM;
4994
Takashi Iwai0ac85512007-06-20 15:46:13 +02004995 codec->spec = spec;
4996
Takashi Iwaic5a4bcd2009-02-06 17:22:05 +01004997 err = snd_hda_attach_beep_device(codec, 0x10);
4998 if (err < 0) {
4999 ad198x_free(codec);
5000 return err;
5001 }
5002 set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
5003
Takashi Iwai0ac85512007-06-20 15:46:13 +02005004 spec->multiout.max_channels = 6;
5005 spec->multiout.num_dacs = 3;
5006 spec->multiout.dac_nids = ad1882_dac_nids;
5007 spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
5008 spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
5009 spec->adc_nids = ad1882_adc_nids;
5010 spec->capsrc_nids = ad1882_capsrc_nids;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005011 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005012 spec->input_mux = &ad1882_capture_source;
5013 else
5014 spec->input_mux = &ad1882a_capture_source;
5015 spec->num_mixers = 2;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005016 spec->mixers[0] = ad1882_base_mixers;
Clemens Fruhwirthc247ed62009-01-07 11:43:48 +01005017 if (codec->vendor_id == 0x11d41882)
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005018 spec->mixers[1] = ad1882_loopback_mixers;
5019 else
5020 spec->mixers[1] = ad1882a_loopback_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005021 spec->num_init_verbs = 1;
5022 spec->init_verbs[0] = ad1882_init_verbs;
5023 spec->spdif_route = 0;
Takashi Iwaicb53c622007-08-10 17:21:45 +02005024#ifdef CONFIG_SND_HDA_POWER_SAVE
5025 spec->loopback.amplist = ad1882_loopbacks;
5026#endif
Takashi Iwai2134ea42008-01-10 16:53:55 +01005027 spec->vmaster_nid = 0x04;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005028
5029 codec->patch_ops = ad198x_patch_ops;
5030
5031 /* override some parameters */
5032 board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
5033 ad1882_models, NULL);
5034 switch (board_config) {
5035 default:
5036 case AD1882_3STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005037 spec->num_mixers = 3;
5038 spec->mixers[2] = ad1882_3stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005039 spec->channel_mode = ad1882_modes;
5040 spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
5041 spec->need_dac_fix = 1;
5042 spec->multiout.max_channels = 2;
5043 spec->multiout.num_dacs = 1;
5044 break;
5045 case AD1882_6STACK:
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005046 spec->num_mixers = 3;
5047 spec->mixers[2] = ad1882_6stack_mixers;
Takashi Iwai0ac85512007-06-20 15:46:13 +02005048 break;
5049 }
Takashi Iwai729d55b2009-12-25 22:49:01 +01005050
5051 codec->no_trigger_sense = 1;
Takashi Iwai0e7adbe2010-10-25 10:37:11 +02005052 codec->no_sticky_stream = 1;
Takashi Iwai729d55b2009-12-25 22:49:01 +01005053
Takashi Iwai0ac85512007-06-20 15:46:13 +02005054 return 0;
5055}
5056
5057
5058/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07005059 * patch entries
5060 */
Takashi Iwai498f5b12011-05-02 11:33:15 +02005061static const struct hda_codec_preset snd_hda_preset_analog[] = {
Takashi Iwaic5059252008-02-16 09:43:56 +01005062 { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
Takashi Iwai0ac85512007-06-20 15:46:13 +02005063 { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005064 { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005065 { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
Takashi Iwaic5059252008-02-16 09:43:56 +01005066 { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
5067 { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
Takashi Iwai4a3fdf32005-04-14 13:35:51 +02005068 { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
5069 { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
Takashi Iwai2bac6472007-05-18 18:21:41 +02005070 { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005071 { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
Takashi Iwaifd66e0d2005-11-17 15:31:34 +01005072 { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
Takashi Iwai71b2ccc2006-04-21 16:09:31 +02005073 { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
Takashi Iwai9e44c6e2008-08-18 13:53:07 +02005074 { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
Takashi Iwai3adb8ab2008-04-15 18:46:42 +02005075 { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
5076 { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07005077 {} /* terminator */
5078};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01005079
5080MODULE_ALIAS("snd-hda-codec-id:11d4*");
5081
5082MODULE_LICENSE("GPL");
5083MODULE_DESCRIPTION("Analog Devices HD-audio codec");
5084
5085static struct hda_codec_preset_list analog_list = {
5086 .preset = snd_hda_preset_analog,
5087 .owner = THIS_MODULE,
5088};
5089
5090static int __init patch_analog_init(void)
5091{
5092 return snd_hda_add_codec_preset(&analog_list);
5093}
5094
5095static void __exit patch_analog_exit(void)
5096{
5097 snd_hda_delete_codec_preset(&analog_list);
5098}
5099
5100module_init(patch_analog_init)
5101module_exit(patch_analog_exit)