2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2007-06-20 21:46:13 +08:00
|
|
|
* HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
|
|
|
|
* AD1986A, AD1988
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2007-05-19 00:21:41 +08:00
|
|
|
* Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* This driver is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This driver is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/pci.h>
|
2011-07-16 00:38:28 +08:00
|
|
|
#include <linux/module.h>
|
2006-01-16 23:34:20 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <sound/core.h>
|
|
|
|
#include "hda_codec.h"
|
|
|
|
#include "hda_local.h"
|
2012-05-07 23:42:31 +08:00
|
|
|
#include "hda_auto_parser.h"
|
2009-02-07 00:22:05 +08:00
|
|
|
#include "hda_beep.h"
|
2011-10-28 04:12:46 +08:00
|
|
|
#include "hda_jack.h"
|
2012-12-21 22:17:06 +08:00
|
|
|
#include "hda_generic.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#define ENABLE_AD_STATIC_QUIRKS
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
struct ad198x_spec {
|
2012-12-21 22:17:06 +08:00
|
|
|
struct hda_gen_spec gen;
|
|
|
|
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
/* for auto parser */
|
|
|
|
int smux_paths[4];
|
|
|
|
unsigned int cur_smux;
|
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
|
2012-12-21 22:17:06 +08:00
|
|
|
hda_nid_t beep_dev_nid;
|
2013-01-22 23:45:58 +08:00
|
|
|
|
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
|
|
|
const struct snd_kcontrol_new *mixers[6];
|
|
|
|
int num_mixers;
|
2011-02-08 19:58:25 +08:00
|
|
|
const struct hda_verb *init_verbs[6]; /* initialization verbs
|
2005-11-03 01:26:49 +08:00
|
|
|
* don't forget NULL termination!
|
|
|
|
*/
|
|
|
|
unsigned int num_init_verbs;
|
|
|
|
|
|
|
|
/* playback */
|
|
|
|
struct hda_multi_out multiout; /* playback set-up
|
|
|
|
* max_channels, dacs must be set
|
|
|
|
* dig_out_nid and hp_nid are optional
|
|
|
|
*/
|
2005-11-17 22:31:34 +08:00
|
|
|
unsigned int cur_eapd;
|
2006-03-27 18:52:22 +08:00
|
|
|
unsigned int need_dac_fix;
|
2005-11-03 01:26:49 +08:00
|
|
|
|
|
|
|
/* capture */
|
|
|
|
unsigned int num_adc_nids;
|
2011-05-02 17:33:15 +08:00
|
|
|
const hda_nid_t *adc_nids;
|
2005-11-03 01:26:49 +08:00
|
|
|
hda_nid_t dig_in_nid; /* digital-in NID; optional */
|
|
|
|
|
|
|
|
/* capture source */
|
2005-04-14 19:35:51 +08:00
|
|
|
const struct hda_input_mux *input_mux;
|
2011-05-02 17:33:15 +08:00
|
|
|
const hda_nid_t *capsrc_nids;
|
2005-11-03 01:26:49 +08:00
|
|
|
unsigned int cur_mux[3];
|
|
|
|
|
|
|
|
/* channel model */
|
2005-11-17 18:06:29 +08:00
|
|
|
const struct hda_channel_mode *channel_mode;
|
2005-11-03 01:26:49 +08:00
|
|
|
int num_channel_mode;
|
|
|
|
|
|
|
|
/* PCM information */
|
2007-05-19 00:21:41 +08:00
|
|
|
struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
|
2005-11-03 01:26:49 +08:00
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
unsigned int spdif_route;
|
2005-11-24 23:06:23 +08:00
|
|
|
|
2010-03-26 17:33:18 +08:00
|
|
|
unsigned int jack_present: 1;
|
|
|
|
unsigned int inv_jack_detect: 1;/* inverted jack-detection */
|
|
|
|
unsigned int analog_beep: 1; /* analog beep input present */
|
2012-03-10 00:51:10 +08:00
|
|
|
unsigned int avoid_init_slave_vol:1;
|
2007-09-06 20:29:53 +08:00
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
struct hda_loopback_check loopback;
|
|
|
|
#endif
|
2008-01-10 23:53:55 +08:00
|
|
|
/* for virtual master */
|
|
|
|
hda_nid_t vmaster_nid;
|
2011-01-17 18:29:34 +08:00
|
|
|
const char * const *slave_vols;
|
|
|
|
const char * const *slave_sws;
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-04-14 19:35:51 +08:00
|
|
|
/*
|
|
|
|
* input MUX handling (common part)
|
|
|
|
*/
|
2005-11-17 21:57:47 +08:00
|
|
|
static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
|
|
|
|
return snd_hda_input_mux_info(spec->input_mux, uinfo);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:57:47 +08:00
|
|
|
static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2005-11-03 01:26:49 +08:00
|
|
|
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
2005-04-14 19:35:51 +08:00
|
|
|
|
2005-11-03 01:26:49 +08:00
|
|
|
ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
|
2005-04-14 19:35:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:57:47 +08:00
|
|
|
static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2005-11-03 01:26:49 +08:00
|
|
|
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
2005-04-14 19:35:51 +08:00
|
|
|
|
|
|
|
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
|
2005-11-21 23:36:15 +08:00
|
|
|
spec->capsrc_nids[adc_idx],
|
|
|
|
&spec->cur_mux[adc_idx]);
|
2005-04-14 19:35:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialization (common callbacks)
|
|
|
|
*/
|
|
|
|
static int ad198x_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2005-11-03 01:26:49 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < spec->num_init_verbs; i++)
|
|
|
|
snd_hda_sequence_write(codec, spec->init_verbs[i]);
|
2005-04-14 19:35:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-03 21:28:01 +08:00
|
|
|
static const char * const ad_slave_pfxs[] = {
|
|
|
|
"Front", "Surround", "Center", "LFE", "Side",
|
|
|
|
"Headphone", "Mono", "Speaker", "IEC958",
|
2008-01-10 23:53:55 +08:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2012-02-03 21:28:01 +08:00
|
|
|
static const char * const ad1988_6stack_fp_slave_pfxs[] = {
|
|
|
|
"Front", "Surround", "Center", "LFE", "Side", "IEC958",
|
2008-01-10 23:53:55 +08:00
|
|
|
NULL
|
|
|
|
};
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2008-01-10 23:53:55 +08:00
|
|
|
|
2009-11-16 22:35:59 +08:00
|
|
|
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
2009-02-07 00:22:05 +08:00
|
|
|
/* additional beep mixers; the actual parameters are overwritten at build */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad_beep_mixer[] = {
|
2009-02-07 00:22:05 +08:00
|
|
|
HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
|
2009-10-21 20:48:23 +08:00
|
|
|
HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
|
2009-02-07 00:22:05 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad_beep2_mixer[] = {
|
2010-03-26 17:33:18 +08:00
|
|
|
HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
#define set_beep_amp(spec, nid, idx, dir) \
|
|
|
|
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
|
2009-11-16 22:35:59 +08:00
|
|
|
#else
|
|
|
|
#define set_beep_amp(spec, nid, idx, dir) /* NOP */
|
|
|
|
#endif
|
2009-02-07 00:22:05 +08:00
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
|
|
|
static int create_beep_ctls(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
const struct snd_kcontrol_new *knew;
|
|
|
|
|
|
|
|
if (!spec->beep_amp)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
|
|
|
|
for ( ; knew->name; knew++) {
|
|
|
|
int err;
|
|
|
|
struct snd_kcontrol *kctl;
|
|
|
|
kctl = snd_ctl_new1(knew, codec);
|
|
|
|
if (!kctl)
|
|
|
|
return -ENOMEM;
|
|
|
|
kctl->private_value = spec->beep_amp;
|
|
|
|
err = snd_hda_ctl_add(codec, 0, kctl);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define create_beep_ctls(codec) 0
|
|
|
|
#endif
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-04-14 19:35:51 +08:00
|
|
|
static int ad198x_build_controls(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2009-12-08 23:13:32 +08:00
|
|
|
struct snd_kcontrol *kctl;
|
2005-11-03 01:26:49 +08:00
|
|
|
unsigned int i;
|
2005-04-14 19:35:51 +08:00
|
|
|
int err;
|
|
|
|
|
2005-11-03 01:26:49 +08:00
|
|
|
for (i = 0; i < spec->num_mixers; i++) {
|
|
|
|
err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
if (spec->multiout.dig_out_nid) {
|
2011-06-02 01:14:18 +08:00
|
|
|
err = snd_hda_create_spdif_out_ctls(codec,
|
|
|
|
spec->multiout.dig_out_nid,
|
|
|
|
spec->multiout.dig_out_nid);
|
2005-11-03 01:26:49 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2008-02-13 01:37:26 +08:00
|
|
|
err = snd_hda_create_spdif_share_sw(codec,
|
|
|
|
&spec->multiout);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec->multiout.share_spdif = 1;
|
2005-11-03 01:26:49 +08:00
|
|
|
}
|
|
|
|
if (spec->dig_in_nid) {
|
|
|
|
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
2008-01-10 23:53:55 +08:00
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
/* create beep controls if needed */
|
2012-12-21 22:17:06 +08:00
|
|
|
err = create_beep_ctls(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2009-02-07 00:22:05 +08:00
|
|
|
|
2008-01-10 23:53:55 +08:00
|
|
|
/* if we have no master control, let's create it */
|
|
|
|
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
|
2008-02-18 20:05:50 +08:00
|
|
|
unsigned int vmaster_tlv[4];
|
2008-01-10 23:53:55 +08:00
|
|
|
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
|
2008-02-18 20:05:50 +08:00
|
|
|
HDA_OUTPUT, vmaster_tlv);
|
2012-03-10 00:51:10 +08:00
|
|
|
err = __snd_hda_add_vmaster(codec, "Master Playback Volume",
|
2008-02-18 20:05:50 +08:00
|
|
|
vmaster_tlv,
|
2008-01-10 23:53:55 +08:00
|
|
|
(spec->slave_vols ?
|
2012-02-03 21:28:01 +08:00
|
|
|
spec->slave_vols : ad_slave_pfxs),
|
2012-03-10 00:51:10 +08:00
|
|
|
"Playback Volume",
|
2012-03-12 19:25:03 +08:00
|
|
|
!spec->avoid_init_slave_vol, NULL);
|
2008-01-10 23:53:55 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
|
|
|
|
err = snd_hda_add_vmaster(codec, "Master Playback Switch",
|
|
|
|
NULL,
|
|
|
|
(spec->slave_sws ?
|
2012-02-03 21:28:01 +08:00
|
|
|
spec->slave_sws : ad_slave_pfxs),
|
|
|
|
"Playback Switch");
|
2008-01-10 23:53:55 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-12-08 23:13:32 +08:00
|
|
|
/* assign Capture Source enums to NID */
|
|
|
|
kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
|
|
|
|
if (!kctl)
|
|
|
|
kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
|
|
|
|
for (i = 0; kctl && i < kctl->count; i++) {
|
2009-12-23 15:31:59 +08:00
|
|
|
err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
|
2009-12-08 23:13:32 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* assign IEC958 enums to NID */
|
|
|
|
kctl = snd_hda_find_mixer_ctl(codec,
|
|
|
|
SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
|
|
|
|
if (kctl) {
|
|
|
|
err = snd_hda_add_nid(codec, kctl, 0,
|
|
|
|
spec->multiout.dig_out_nid);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
/*
|
|
|
|
* Analog playback callbacks
|
|
|
|
*/
|
|
|
|
static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
2005-11-17 21:57:47 +08:00
|
|
|
struct snd_pcm_substream *substream)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2012-12-21 22:17:06 +08:00
|
|
|
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
|
2008-02-13 01:37:26 +08:00
|
|
|
hinfo);
|
2005-04-14 19:35:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
|
|
|
unsigned int stream_tag,
|
|
|
|
unsigned int format,
|
2005-11-17 21:57:47 +08:00
|
|
|
struct snd_pcm_substream *substream)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
|
|
|
|
format, substream);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
2005-11-17 21:57:47 +08:00
|
|
|
struct snd_pcm_substream *substream)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Digital out
|
|
|
|
*/
|
|
|
|
static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
2005-11-17 21:57:47 +08:00
|
|
|
struct snd_pcm_substream *substream)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
2005-11-17 21:57:47 +08:00
|
|
|
struct snd_pcm_substream *substream)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
|
|
|
}
|
|
|
|
|
2007-04-05 20:51:48 +08:00
|
|
|
static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
|
|
|
unsigned int stream_tag,
|
|
|
|
unsigned int format,
|
|
|
|
struct snd_pcm_substream *substream)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
|
|
|
|
format, substream);
|
|
|
|
}
|
|
|
|
|
2009-02-13 18:32:28 +08:00
|
|
|
static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
|
|
|
struct snd_pcm_substream *substream)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
|
|
|
|
}
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
/*
|
|
|
|
* Analog capture
|
|
|
|
*/
|
|
|
|
static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
|
|
|
unsigned int stream_tag,
|
|
|
|
unsigned int format,
|
2005-11-17 21:57:47 +08:00
|
|
|
struct snd_pcm_substream *substream)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2005-11-03 01:26:49 +08:00
|
|
|
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
|
|
|
|
stream_tag, 0, format);
|
2005-04-14 19:35:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
2005-11-17 21:57:47 +08:00
|
|
|
struct snd_pcm_substream *substream)
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2008-03-18 16:57:50 +08:00
|
|
|
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
|
2005-04-14 19:35:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
|
2005-04-14 19:35:51 +08:00
|
|
|
.substreams = 1,
|
|
|
|
.channels_min = 2,
|
2005-11-03 01:26:49 +08:00
|
|
|
.channels_max = 6, /* changed later */
|
2005-04-14 19:35:51 +08:00
|
|
|
.nid = 0, /* fill later */
|
|
|
|
.ops = {
|
|
|
|
.open = ad198x_playback_pcm_open,
|
|
|
|
.prepare = ad198x_playback_pcm_prepare,
|
2011-09-23 19:03:25 +08:00
|
|
|
.cleanup = ad198x_playback_pcm_cleanup,
|
2005-04-14 19:35:51 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
|
2005-11-03 01:26:49 +08:00
|
|
|
.substreams = 1,
|
2005-04-14 19:35:51 +08:00
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.nid = 0, /* fill later */
|
|
|
|
.ops = {
|
|
|
|
.prepare = ad198x_capture_pcm_prepare,
|
|
|
|
.cleanup = ad198x_capture_pcm_cleanup
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
|
2005-04-14 19:35:51 +08:00
|
|
|
.substreams = 1,
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.nid = 0, /* fill later */
|
|
|
|
.ops = {
|
|
|
|
.open = ad198x_dig_playback_pcm_open,
|
2007-04-05 20:51:48 +08:00
|
|
|
.close = ad198x_dig_playback_pcm_close,
|
2009-02-13 18:32:28 +08:00
|
|
|
.prepare = ad198x_dig_playback_pcm_prepare,
|
|
|
|
.cleanup = ad198x_dig_playback_pcm_cleanup
|
2005-04-14 19:35:51 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
|
2005-11-03 01:26:49 +08:00
|
|
|
.substreams = 1,
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
/* NID is set in alc_build_pcms */
|
|
|
|
};
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
static int ad198x_build_pcms(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
struct hda_pcm *info = spec->pcm_rec;
|
|
|
|
|
|
|
|
codec->num_pcms = 1;
|
|
|
|
codec->pcm_info = info;
|
|
|
|
|
|
|
|
info->name = "AD198x Analog";
|
|
|
|
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
|
|
|
|
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
|
|
|
|
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
|
|
|
|
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
|
2005-11-03 01:26:49 +08:00
|
|
|
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
|
|
|
|
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
|
2005-04-14 19:35:51 +08:00
|
|
|
|
|
|
|
if (spec->multiout.dig_out_nid) {
|
|
|
|
info++;
|
|
|
|
codec->num_pcms++;
|
2012-11-05 19:32:46 +08:00
|
|
|
codec->spdif_status_reset = 1;
|
2005-04-14 19:35:51 +08:00
|
|
|
info->name = "AD198x Digital";
|
[ALSA] hda-intel - Fix PCM device number assignment
In the current scheme, PCM device numbers are assigned incrementally
in the order of codecs. This causes problems when the codec number
is irregular, e.g. codec #0 for HDMI and codec #1 for analog. Then
the HDMI becomes the first PCM, which is picked up as the default
output device. Unfortuantely this doesn't work well with normal
setups.
This patch introduced the fixed device numbers for the PCM types,
namely, analog, SPDIF, HDMI and modem. The PCM devices are assigned
according to the corresponding PCM type. After this patch, HDMI will
be always assigned to PCM #3, SPDIF to PCM #1, and the first analog
to PCM #0, etc.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2008-02-06 21:03:20 +08:00
|
|
|
info->pcm_type = HDA_PCM_TYPE_SPDIF;
|
2005-04-14 19:35:51 +08:00
|
|
|
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
|
|
|
|
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
|
2005-11-03 01:26:49 +08:00
|
|
|
if (spec->dig_in_nid) {
|
|
|
|
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
|
|
|
|
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
|
|
|
|
}
|
2005-04-14 19:35:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2005-04-14 19:35:51 +08:00
|
|
|
|
2009-12-28 07:48:29 +08:00
|
|
|
static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
|
|
|
|
hda_nid_t hp)
|
|
|
|
{
|
2011-06-01 15:09:48 +08:00
|
|
|
if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
|
|
|
|
snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
2012-12-21 22:17:06 +08:00
|
|
|
!codec->inv_eapd ? 0x00 : 0x02);
|
2011-06-01 15:09:48 +08:00
|
|
|
if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
|
|
|
|
snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
2012-12-21 22:17:06 +08:00
|
|
|
!codec->inv_eapd ? 0x00 : 0x02);
|
2009-12-28 07:48:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ad198x_power_eapd(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
/* We currently only handle front, HP */
|
|
|
|
switch (codec->vendor_id) {
|
|
|
|
case 0x11d41882:
|
|
|
|
case 0x11d4882a:
|
|
|
|
case 0x11d41884:
|
|
|
|
case 0x11d41984:
|
|
|
|
case 0x11d41883:
|
|
|
|
case 0x11d4184a:
|
|
|
|
case 0x11d4194a:
|
|
|
|
case 0x11d4194b:
|
2011-06-03 16:05:02 +08:00
|
|
|
case 0x11d41988:
|
|
|
|
case 0x11d4198b:
|
|
|
|
case 0x11d4989a:
|
|
|
|
case 0x11d4989b:
|
2009-12-28 07:48:29 +08:00
|
|
|
ad198x_power_eapd_write(codec, 0x12, 0x11);
|
|
|
|
break;
|
|
|
|
case 0x11d41981:
|
|
|
|
case 0x11d41983:
|
|
|
|
ad198x_power_eapd_write(codec, 0x05, 0x06);
|
|
|
|
break;
|
|
|
|
case 0x11d41986:
|
|
|
|
ad198x_power_eapd_write(codec, 0x1b, 0x1a);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-26 21:18:33 +08:00
|
|
|
static void ad198x_shutup(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
snd_hda_shutup_pins(codec);
|
|
|
|
ad198x_power_eapd(codec);
|
|
|
|
}
|
|
|
|
|
2008-07-30 21:01:44 +08:00
|
|
|
static void ad198x_free(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
|
|
|
|
if (!spec)
|
|
|
|
return;
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
snd_hda_gen_spec_free(&spec->gen);
|
2009-02-07 00:22:05 +08:00
|
|
|
kfree(spec);
|
|
|
|
snd_hda_detach_beep_device(codec);
|
2005-04-14 19:35:51 +08:00
|
|
|
}
|
|
|
|
|
2011-07-26 15:52:50 +08:00
|
|
|
#ifdef CONFIG_PM
|
2012-07-02 21:20:37 +08:00
|
|
|
static int ad198x_suspend(struct hda_codec *codec)
|
2009-12-28 07:48:29 +08:00
|
|
|
{
|
|
|
|
ad198x_shutup(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_codec_ops ad198x_patch_ops = {
|
2005-04-14 19:35:51 +08:00
|
|
|
.build_controls = ad198x_build_controls,
|
|
|
|
.build_pcms = ad198x_build_pcms,
|
|
|
|
.init = ad198x_init,
|
|
|
|
.free = ad198x_free,
|
2011-07-26 15:52:50 +08:00
|
|
|
#ifdef CONFIG_PM
|
2012-08-25 00:38:08 +08:00
|
|
|
.check_power_status = ad198x_check_power_status,
|
2009-12-28 07:48:29 +08:00
|
|
|
.suspend = ad198x_suspend,
|
|
|
|
#endif
|
|
|
|
.reboot_notify = ad198x_shutup,
|
2005-04-14 19:35:51 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-03-02 02:54:39 +08:00
|
|
|
/*
|
|
|
|
* EAPD control
|
2009-12-09 00:23:33 +08:00
|
|
|
* the private value = nid
|
2006-03-02 02:54:39 +08:00
|
|
|
*/
|
2007-07-23 21:42:26 +08:00
|
|
|
#define ad198x_eapd_info snd_ctl_boolean_mono_info
|
2006-03-02 02:54:39 +08:00
|
|
|
|
|
|
|
static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2012-12-21 22:17:06 +08:00
|
|
|
if (codec->inv_eapd)
|
2006-03-02 02:54:39 +08:00
|
|
|
ucontrol->value.integer.value[0] = ! spec->cur_eapd;
|
|
|
|
else
|
|
|
|
ucontrol->value.integer.value[0] = spec->cur_eapd;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
hda_nid_t nid = kcontrol->private_value & 0xff;
|
|
|
|
unsigned int eapd;
|
2007-11-15 22:54:38 +08:00
|
|
|
eapd = !!ucontrol->value.integer.value[0];
|
2012-12-21 22:17:06 +08:00
|
|
|
if (codec->inv_eapd)
|
2006-03-02 02:54:39 +08:00
|
|
|
eapd = !eapd;
|
2007-08-10 23:09:26 +08:00
|
|
|
if (eapd == spec->cur_eapd)
|
2006-03-02 02:54:39 +08:00
|
|
|
return 0;
|
|
|
|
spec->cur_eapd = eapd;
|
2007-08-10 23:09:26 +08:00
|
|
|
snd_hda_codec_write_cache(codec, nid,
|
|
|
|
0, AC_VERB_SET_EAPD_BTLENABLE,
|
|
|
|
eapd ? 0x02 : 0x00);
|
2006-03-02 02:54:39 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-03-13 20:49:49 +08:00
|
|
|
static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo);
|
|
|
|
static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol);
|
|
|
|
static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol);
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2006-03-13 20:49:49 +08:00
|
|
|
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
/*
|
|
|
|
* Automatic parse of I/O pins from the BIOS configuration
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int ad198x_auto_build_controls(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = snd_hda_gen_build_controls(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
err = create_beep_ctls(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct hda_codec_ops ad198x_auto_patch_ops = {
|
|
|
|
.build_controls = ad198x_auto_build_controls,
|
|
|
|
.build_pcms = snd_hda_gen_build_pcms,
|
|
|
|
.init = snd_hda_gen_init,
|
|
|
|
.free = ad198x_free,
|
2013-01-18 14:51:17 +08:00
|
|
|
.unsol_event = snd_hda_jack_unsol_event,
|
2012-12-21 22:17:06 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.check_power_status = snd_hda_gen_check_power_status,
|
|
|
|
.suspend = ad198x_suspend,
|
|
|
|
#endif
|
|
|
|
.reboot_notify = ad198x_shutup,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int ad198x_parse_auto_config(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
struct auto_pin_cfg *cfg = &spec->gen.autocfg;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
codec->spdif_status_reset = 1;
|
|
|
|
codec->no_trigger_sense = 1;
|
|
|
|
codec->no_sticky_stream = 1;
|
|
|
|
|
|
|
|
spec->gen.indep_hp = 1;
|
|
|
|
|
|
|
|
err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
err = snd_hda_gen_parse_auto_config(codec, cfg);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (spec->beep_dev_nid) {
|
|
|
|
err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
codec->patch_ops = ad198x_auto_patch_ops;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
/*
|
|
|
|
* AD1986A specific
|
|
|
|
*/
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-04-17 06:20:36 +08:00
|
|
|
#define AD1986A_SPDIF_OUT 0x02
|
|
|
|
#define AD1986A_FRONT_DAC 0x03
|
|
|
|
#define AD1986A_SURR_DAC 0x04
|
|
|
|
#define AD1986A_CLFE_DAC 0x05
|
|
|
|
#define AD1986A_ADC 0x06
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1986a_dac_nids[3] = {
|
2005-04-17 06:20:36 +08:00
|
|
|
AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
|
|
|
|
};
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
|
|
|
|
static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1986a_capture_source = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.num_items = 7,
|
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
|
|
|
{ "CD", 0x1 },
|
|
|
|
{ "Aux", 0x3 },
|
|
|
|
{ "Line", 0x4 },
|
|
|
|
{ "Mix", 0x5 },
|
|
|
|
{ "Mono", 0x6 },
|
|
|
|
{ "Phone", 0x7 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
|
2007-07-28 01:02:40 +08:00
|
|
|
.ops = &snd_hda_bind_vol,
|
|
|
|
.values = {
|
|
|
|
HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
|
|
|
|
HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
|
|
|
|
HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
|
|
|
|
0
|
|
|
|
},
|
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
|
2007-07-28 01:02:40 +08:00
|
|
|
.ops = &snd_hda_bind_sw,
|
|
|
|
.values = {
|
|
|
|
HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
|
|
|
|
HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
|
|
|
|
HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
|
|
|
|
0
|
|
|
|
},
|
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* mixers
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1986a_mixers[] = {
|
2007-07-28 01:02:40 +08:00
|
|
|
/*
|
|
|
|
* bind volumes/mutes of 3 DACs as a single PCM control for simplicity
|
|
|
|
*/
|
|
|
|
HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
|
|
|
|
HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
|
2005-04-17 06:20:36 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
|
2005-04-17 06:20:36 +08:00
|
|
|
HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
2005-04-14 19:35:51 +08:00
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
2005-04-17 06:20:36 +08:00
|
|
|
},
|
|
|
|
HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2006-03-13 20:49:49 +08:00
|
|
|
/* additional mixers for 3stack mode */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
|
2006-03-13 20:49:49 +08:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Channel Mode",
|
|
|
|
.info = ad198x_ch_mode_info,
|
|
|
|
.get = ad198x_ch_mode_get,
|
|
|
|
.put = ad198x_ch_mode_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* laptop model - 2ch only */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
|
2006-03-13 20:49:49 +08:00
|
|
|
|
2007-08-16 04:20:45 +08:00
|
|
|
/* master controls both pins 0x1a and 0x1b */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
|
2007-08-16 04:20:45 +08:00
|
|
|
.ops = &snd_hda_bind_vol,
|
|
|
|
.values = {
|
|
|
|
HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
|
|
|
|
HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
|
|
|
|
0,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
|
2007-08-16 04:20:45 +08:00
|
|
|
.ops = &snd_hda_bind_sw,
|
|
|
|
.values = {
|
|
|
|
HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
|
|
|
|
HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
|
|
|
|
0,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
|
2006-03-13 20:49:49 +08:00
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
|
2007-08-16 04:20:45 +08:00
|
|
|
HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
|
|
|
|
HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
|
2006-03-13 20:49:49 +08:00
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
|
2009-02-07 00:22:05 +08:00
|
|
|
/*
|
2006-03-13 20:49:49 +08:00
|
|
|
HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
|
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2006-03-17 17:50:49 +08:00
|
|
|
/* laptop-eapd model - 2ch only */
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
|
2006-03-17 17:50:49 +08:00
|
|
|
.num_items = 3,
|
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
|
|
|
{ "Internal Mic", 0x4 },
|
|
|
|
{ "Mix", 0x5 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1986a_automic_capture_source = {
|
2008-02-12 19:11:36 +08:00
|
|
|
.num_items = 2,
|
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
|
|
|
{ "Mix", 0x5 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
|
2008-11-21 09:25:48 +08:00
|
|
|
HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
|
|
|
|
HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
|
2006-03-17 17:50:49 +08:00
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
|
2006-03-17 17:50:49 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "External Amplifier",
|
2009-12-08 23:13:32 +08:00
|
|
|
.subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
|
2006-03-17 17:50:49 +08:00
|
|
|
.info = ad198x_eapd_info,
|
|
|
|
.get = ad198x_eapd_get,
|
|
|
|
.put = ad198x_eapd_put,
|
2009-12-09 00:23:33 +08:00
|
|
|
.private_value = 0x1b, /* port-D */
|
2006-03-17 17:50:49 +08:00
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
|
2009-06-24 20:07:53 +08:00
|
|
|
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2008-02-12 19:11:36 +08:00
|
|
|
/* re-connect the mic boost input according to the jack sensing */
|
|
|
|
static void ad1986a_automic(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int present;
|
2009-11-18 15:00:14 +08:00
|
|
|
present = snd_hda_jack_detect(codec, 0x1f);
|
2008-02-12 19:11:36 +08:00
|
|
|
/* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
|
|
|
|
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
|
2009-11-18 15:00:14 +08:00
|
|
|
present ? 0 : 2);
|
2008-02-12 19:11:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define AD1986A_MIC_EVENT 0x36
|
|
|
|
|
|
|
|
static void ad1986a_automic_unsol_event(struct hda_codec *codec,
|
|
|
|
unsigned int res)
|
|
|
|
{
|
|
|
|
if ((res >> 26) != AD1986A_MIC_EVENT)
|
|
|
|
return;
|
|
|
|
ad1986a_automic(codec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1986a_automic_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1986a_automic(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-06 20:29:53 +08:00
|
|
|
/* laptop-automute - 2ch only */
|
|
|
|
|
|
|
|
static void ad1986a_update_hp(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
unsigned int mute;
|
|
|
|
|
|
|
|
if (spec->jack_present)
|
|
|
|
mute = HDA_AMP_MUTE; /* mute internal speaker */
|
|
|
|
else
|
|
|
|
/* unmute internal speaker if necessary */
|
|
|
|
mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
|
|
|
|
snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
|
|
|
|
HDA_AMP_MUTE, mute);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ad1986a_hp_automute(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
|
2009-06-24 20:10:15 +08:00
|
|
|
if (spec->inv_jack_detect)
|
|
|
|
spec->jack_present = !spec->jack_present;
|
2007-09-06 20:29:53 +08:00
|
|
|
ad1986a_update_hp(codec);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define AD1986A_HP_EVENT 0x37
|
|
|
|
|
|
|
|
static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
|
|
|
|
{
|
|
|
|
if ((res >> 26) != AD1986A_HP_EVENT)
|
|
|
|
return;
|
|
|
|
ad1986a_hp_automute(codec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1986a_hp_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1986a_hp_automute(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bind hp and internal speaker mute (with plug check) */
|
|
|
|
static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
2012-12-14 00:03:30 +08:00
|
|
|
int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
2007-09-06 20:29:53 +08:00
|
|
|
if (change)
|
|
|
|
ad1986a_update_hp(codec);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
|
2007-09-06 20:29:53 +08:00
|
|
|
HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Master Playback Switch",
|
2009-12-10 20:57:01 +08:00
|
|
|
.subdevice = HDA_SUBDEV_AMP_FLAG,
|
2007-09-06 20:29:53 +08:00
|
|
|
.info = snd_hda_mixer_amp_switch_info,
|
|
|
|
.get = snd_hda_mixer_amp_switch_get,
|
|
|
|
.put = ad1986a_hp_master_sw_put,
|
|
|
|
.private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2009-06-24 20:07:53 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* initialization verbs
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_init_verbs[] = {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Front, Surround, CLFE DAC; mute as default */
|
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* Downmix - off */
|
|
|
|
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* HP, Line-Out, Surround, CLFE selectors */
|
|
|
|
{0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Mono selector */
|
|
|
|
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Mic selector: Mic 1/2 pin */
|
|
|
|
{0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Line-in selector: Line-in */
|
|
|
|
{0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Mic 1/2 swap */
|
|
|
|
{0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Record selector: mic */
|
|
|
|
{0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Mic, Phone, CD, Aux, Line-In amp; mute as default */
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* PC beep */
|
|
|
|
{0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
|
|
|
|
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
2005-04-14 19:35:51 +08:00
|
|
|
/* HP Pin */
|
|
|
|
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
|
|
|
|
/* Front, Surround, CLFE Pins */
|
|
|
|
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
/* Mono Pin */
|
|
|
|
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
/* Mic Pin */
|
|
|
|
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
|
|
|
|
/* Line, Aux, CD, Beep-In Pin */
|
|
|
|
{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
|
|
|
|
{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
|
|
|
|
{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
|
|
|
|
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
|
|
|
|
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_ch2_init[] = {
|
2006-03-13 20:49:49 +08:00
|
|
|
/* Surround out -> Line In */
|
2007-04-19 05:03:56 +08:00
|
|
|
{ 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
|
|
|
|
/* Line-in selectors */
|
|
|
|
{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
|
2006-03-13 20:49:49 +08:00
|
|
|
/* CLFE -> Mic in */
|
2007-04-19 05:03:56 +08:00
|
|
|
{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
|
|
|
|
/* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
|
|
|
|
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
|
2006-03-13 20:49:49 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_ch4_init[] = {
|
2006-03-13 20:49:49 +08:00
|
|
|
/* Surround out -> Surround */
|
2007-04-19 05:03:56 +08:00
|
|
|
{ 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
|
|
|
{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
|
2006-03-13 20:49:49 +08:00
|
|
|
/* CLFE -> Mic in */
|
2007-04-19 05:03:56 +08:00
|
|
|
{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
|
|
|
|
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
|
2006-03-13 20:49:49 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_ch6_init[] = {
|
2006-03-13 20:49:49 +08:00
|
|
|
/* Surround out -> Surround out */
|
2007-04-19 05:03:56 +08:00
|
|
|
{ 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
|
|
|
{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
|
2006-03-13 20:49:49 +08:00
|
|
|
/* CLFE -> CLFE */
|
2007-04-19 05:03:56 +08:00
|
|
|
{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
|
|
|
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
|
2006-03-13 20:49:49 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_channel_mode ad1986a_modes[3] = {
|
2006-03-13 20:49:49 +08:00
|
|
|
{ 2, ad1986a_ch2_init },
|
|
|
|
{ 4, ad1986a_ch4_init },
|
|
|
|
{ 6, ad1986a_ch6_init },
|
|
|
|
};
|
|
|
|
|
2006-03-17 17:50:49 +08:00
|
|
|
/* eapd initialization */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_eapd_init_verbs[] = {
|
2007-01-08 18:07:12 +08:00
|
|
|
{0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
|
2006-03-17 17:50:49 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_automic_verbs[] = {
|
2008-02-12 19:11:36 +08:00
|
|
|
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
/*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
|
|
|
|
{0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2007-01-08 18:07:12 +08:00
|
|
|
/* Ultra initialization */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_ultra_init[] = {
|
2007-01-08 18:07:12 +08:00
|
|
|
/* eapd initialization */
|
|
|
|
{ 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
|
|
|
|
/* CLFE -> Mic in */
|
|
|
|
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
|
|
|
|
{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
|
|
|
|
{ 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2007-09-06 20:29:53 +08:00
|
|
|
/* pin sensing on HP jack */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1986a_hp_init_verbs[] = {
|
2007-09-06 20:29:53 +08:00
|
|
|
{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2009-06-24 20:14:34 +08:00
|
|
|
static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
|
|
|
|
unsigned int res)
|
|
|
|
{
|
|
|
|
switch (res >> 26) {
|
|
|
|
case AD1986A_HP_EVENT:
|
|
|
|
ad1986a_hp_automute(codec);
|
|
|
|
break;
|
|
|
|
case AD1986A_MIC_EVENT:
|
|
|
|
ad1986a_automic(codec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1986a_samsung_p50_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1986a_hp_automute(codec);
|
|
|
|
ad1986a_automic(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-06 20:29:53 +08:00
|
|
|
|
2006-03-13 20:49:49 +08:00
|
|
|
/* models */
|
2006-11-25 00:07:44 +08:00
|
|
|
enum {
|
2012-12-21 22:17:06 +08:00
|
|
|
AD1986A_AUTO,
|
2006-11-25 00:07:44 +08:00
|
|
|
AD1986A_6STACK,
|
|
|
|
AD1986A_3STACK,
|
|
|
|
AD1986A_LAPTOP,
|
|
|
|
AD1986A_LAPTOP_EAPD,
|
2007-09-06 20:29:53 +08:00
|
|
|
AD1986A_LAPTOP_AUTOMUTE,
|
2007-01-08 18:07:12 +08:00
|
|
|
AD1986A_ULTRA,
|
2008-11-21 09:25:48 +08:00
|
|
|
AD1986A_SAMSUNG,
|
2009-06-24 20:14:34 +08:00
|
|
|
AD1986A_SAMSUNG_P50,
|
2006-11-25 00:07:44 +08:00
|
|
|
AD1986A_MODELS
|
|
|
|
};
|
|
|
|
|
2011-01-17 18:29:34 +08:00
|
|
|
static const char * const ad1986a_models[AD1986A_MODELS] = {
|
2012-12-21 22:17:06 +08:00
|
|
|
[AD1986A_AUTO] = "auto",
|
2006-11-25 00:07:44 +08:00
|
|
|
[AD1986A_6STACK] = "6stack",
|
|
|
|
[AD1986A_3STACK] = "3stack",
|
|
|
|
[AD1986A_LAPTOP] = "laptop",
|
|
|
|
[AD1986A_LAPTOP_EAPD] = "laptop-eapd",
|
2007-09-06 20:29:53 +08:00
|
|
|
[AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
|
2007-01-08 18:07:12 +08:00
|
|
|
[AD1986A_ULTRA] = "ultra",
|
2008-11-21 09:25:48 +08:00
|
|
|
[AD1986A_SAMSUNG] = "samsung",
|
2009-06-24 20:14:34 +08:00
|
|
|
[AD1986A_SAMSUNG_P50] = "samsung-p50",
|
2006-11-25 00:07:44 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
|
2006-11-25 00:07:44 +08:00
|
|
|
SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
|
2007-12-18 00:14:18 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
|
2006-11-25 00:07:44 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
|
2007-11-05 22:13:51 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
|
2007-04-23 22:41:12 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
|
2006-11-25 00:07:44 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
|
|
|
|
SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
|
2007-12-18 00:14:18 +08:00
|
|
|
SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
|
2010-02-21 00:16:30 +08:00
|
|
|
SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
|
2007-03-13 05:20:51 +08:00
|
|
|
SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
|
2006-11-25 00:07:44 +08:00
|
|
|
SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
|
2009-06-24 20:14:34 +08:00
|
|
|
SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
|
2007-01-08 18:07:12 +08:00
|
|
|
SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
|
2009-02-10 00:14:52 +08:00
|
|
|
SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
|
2007-12-18 00:14:18 +08:00
|
|
|
SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
|
2007-03-13 05:20:51 +08:00
|
|
|
SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
|
2006-11-25 00:07:44 +08:00
|
|
|
SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
|
2007-09-06 20:29:53 +08:00
|
|
|
SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
|
2006-11-25 00:07:44 +08:00
|
|
|
SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
|
2006-03-13 20:49:49 +08:00
|
|
|
{}
|
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_amp_list ad1986a_loopbacks[] = {
|
2007-08-10 23:21:45 +08:00
|
|
|
{ 0x13, HDA_OUTPUT, 0 }, /* Mic */
|
|
|
|
{ 0x14, HDA_OUTPUT, 0 }, /* Phone */
|
|
|
|
{ 0x15, HDA_OUTPUT, 0 }, /* CD */
|
|
|
|
{ 0x16, HDA_OUTPUT, 0 }, /* Aux */
|
|
|
|
{ 0x17, HDA_OUTPUT, 0 }, /* Line */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2008-01-28 19:30:17 +08:00
|
|
|
static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
|
|
|
|
{
|
2009-02-20 21:37:42 +08:00
|
|
|
unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
|
2008-01-28 19:30:17 +08:00
|
|
|
return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2008-01-28 19:30:17 +08:00
|
|
|
|
2012-05-09 20:35:27 +08:00
|
|
|
static int alloc_ad_spec(struct hda_codec *codec)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-04-14 19:35:51 +08:00
|
|
|
struct ad198x_spec *spec;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 20:21:46 +08:00
|
|
|
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
2012-05-09 20:35:27 +08:00
|
|
|
if (!spec)
|
2005-04-14 19:35:51 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
codec->spec = spec;
|
2012-12-21 22:17:06 +08:00
|
|
|
snd_hda_gen_spec_init(&spec->gen);
|
2012-05-09 20:35:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
/*
|
|
|
|
*/
|
|
|
|
static int ad1986a_parse_auto_config(struct hda_codec *codec)
|
|
|
|
{
|
2013-01-22 23:45:58 +08:00
|
|
|
int err;
|
|
|
|
struct ad198x_spec *spec;
|
|
|
|
|
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2012-12-21 22:17:06 +08:00
|
|
|
|
|
|
|
/* AD1986A has the inverted EAPD implementation */
|
|
|
|
codec->inv_eapd = 1;
|
|
|
|
|
2013-01-21 23:40:16 +08:00
|
|
|
spec->gen.mixer_nid = 0x07;
|
2012-12-21 22:17:06 +08:00
|
|
|
spec->beep_dev_nid = 0x19;
|
|
|
|
set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
|
|
|
|
|
|
|
|
/* AD1986A has a hardware problem that it can't share a stream
|
|
|
|
* with multiple output pins. The copy of front to surrounds
|
|
|
|
* causes noisy or silent outputs at a certain timing, e.g.
|
|
|
|
* changing the volume.
|
|
|
|
* So, let's disable the shared stream.
|
|
|
|
*/
|
|
|
|
spec->gen.multiout.no_share_stream = 1;
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
err = ad198x_parse_auto_config(codec);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-12-21 22:17:06 +08:00
|
|
|
}
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2012-05-09 20:35:27 +08:00
|
|
|
static int patch_ad1986a(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec;
|
|
|
|
int err, board_config;
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
|
|
|
|
ad1986a_models,
|
|
|
|
ad1986a_cfg_tbl);
|
|
|
|
if (board_config == AD1986A_AUTO)
|
|
|
|
return ad1986a_parse_auto_config(codec);
|
|
|
|
|
2012-05-09 20:35:27 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2005-04-14 19:35:51 +08:00
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
err = snd_hda_attach_beep_device(codec, 0x19);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->multiout.max_channels = 6;
|
|
|
|
spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
|
|
|
|
spec->multiout.dac_nids = ad1986a_dac_nids;
|
|
|
|
spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
|
2005-11-03 01:26:49 +08:00
|
|
|
spec->num_adc_nids = 1;
|
|
|
|
spec->adc_nids = ad1986a_adc_nids;
|
2006-03-02 03:05:39 +08:00
|
|
|
spec->capsrc_nids = ad1986a_capsrc_nids;
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->input_mux = &ad1986a_capture_source;
|
2005-11-03 01:26:49 +08:00
|
|
|
spec->num_mixers = 1;
|
|
|
|
spec->mixers[0] = ad1986a_mixers;
|
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1986a_init_verbs;
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
spec->loopback.amplist = ad1986a_loopbacks;
|
|
|
|
#endif
|
2008-01-10 23:53:55 +08:00
|
|
|
spec->vmaster_nid = 0x1b;
|
2012-12-21 22:17:06 +08:00
|
|
|
codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
|
2005-04-14 19:35:51 +08:00
|
|
|
|
|
|
|
codec->patch_ops = ad198x_patch_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-03-13 20:49:49 +08:00
|
|
|
/* override some parameters */
|
|
|
|
switch (board_config) {
|
|
|
|
case AD1986A_3STACK:
|
|
|
|
spec->num_mixers = 2;
|
|
|
|
spec->mixers[1] = ad1986a_3st_mixers;
|
2007-04-19 05:03:56 +08:00
|
|
|
spec->num_init_verbs = 2;
|
|
|
|
spec->init_verbs[1] = ad1986a_ch2_init;
|
2006-03-13 20:49:49 +08:00
|
|
|
spec->channel_mode = ad1986a_modes;
|
|
|
|
spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
|
2006-03-27 18:52:22 +08:00
|
|
|
spec->need_dac_fix = 1;
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
2006-03-13 20:49:49 +08:00
|
|
|
break;
|
|
|
|
case AD1986A_LAPTOP:
|
|
|
|
spec->mixers[0] = ad1986a_laptop_mixers;
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
|
|
|
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
|
|
|
break;
|
2006-03-17 17:50:49 +08:00
|
|
|
case AD1986A_LAPTOP_EAPD:
|
2009-06-24 20:07:53 +08:00
|
|
|
spec->num_mixers = 3;
|
|
|
|
spec->mixers[0] = ad1986a_laptop_master_mixers;
|
|
|
|
spec->mixers[1] = ad1986a_laptop_eapd_mixers;
|
|
|
|
spec->mixers[2] = ad1986a_laptop_intmic_mixers;
|
2008-11-21 09:25:48 +08:00
|
|
|
spec->num_init_verbs = 2;
|
|
|
|
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
|
|
|
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
|
|
|
if (!is_jack_available(codec, 0x25))
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
|
|
|
|
break;
|
|
|
|
case AD1986A_SAMSUNG:
|
2009-06-24 20:07:53 +08:00
|
|
|
spec->num_mixers = 2;
|
|
|
|
spec->mixers[0] = ad1986a_laptop_master_mixers;
|
|
|
|
spec->mixers[1] = ad1986a_laptop_eapd_mixers;
|
2008-02-12 19:11:36 +08:00
|
|
|
spec->num_init_verbs = 3;
|
2006-03-17 17:50:49 +08:00
|
|
|
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
|
2008-02-12 19:11:36 +08:00
|
|
|
spec->init_verbs[2] = ad1986a_automic_verbs;
|
2006-03-17 17:50:49 +08:00
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
|
|
|
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
2008-01-28 19:30:17 +08:00
|
|
|
if (!is_jack_available(codec, 0x25))
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
2008-02-12 19:11:36 +08:00
|
|
|
spec->input_mux = &ad1986a_automic_capture_source;
|
|
|
|
codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1986a_automic_init;
|
2006-03-17 17:50:49 +08:00
|
|
|
break;
|
2009-06-24 20:14:34 +08:00
|
|
|
case AD1986A_SAMSUNG_P50:
|
|
|
|
spec->num_mixers = 2;
|
|
|
|
spec->mixers[0] = ad1986a_automute_master_mixers;
|
|
|
|
spec->mixers[1] = ad1986a_laptop_eapd_mixers;
|
|
|
|
spec->num_init_verbs = 4;
|
|
|
|
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
|
|
|
|
spec->init_verbs[2] = ad1986a_automic_verbs;
|
|
|
|
spec->init_verbs[3] = ad1986a_hp_init_verbs;
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
|
|
|
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
|
|
|
if (!is_jack_available(codec, 0x25))
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
spec->input_mux = &ad1986a_automic_capture_source;
|
|
|
|
codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1986a_samsung_p50_init;
|
|
|
|
break;
|
2007-09-06 20:29:53 +08:00
|
|
|
case AD1986A_LAPTOP_AUTOMUTE:
|
2009-06-24 20:07:53 +08:00
|
|
|
spec->num_mixers = 3;
|
|
|
|
spec->mixers[0] = ad1986a_automute_master_mixers;
|
|
|
|
spec->mixers[1] = ad1986a_laptop_eapd_mixers;
|
|
|
|
spec->mixers[2] = ad1986a_laptop_intmic_mixers;
|
2007-09-06 20:29:53 +08:00
|
|
|
spec->num_init_verbs = 3;
|
|
|
|
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
|
|
|
|
spec->init_verbs[2] = ad1986a_hp_init_verbs;
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
|
|
|
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
2008-01-28 19:30:17 +08:00
|
|
|
if (!is_jack_available(codec, 0x25))
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
2007-09-06 20:29:53 +08:00
|
|
|
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
|
|
|
|
codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1986a_hp_init;
|
2009-06-24 20:10:15 +08:00
|
|
|
/* Lenovo N100 seems to report the reversed bit
|
|
|
|
* for HP jack-sensing
|
|
|
|
*/
|
|
|
|
spec->inv_jack_detect = 1;
|
2007-09-06 20:29:53 +08:00
|
|
|
break;
|
2007-01-08 18:07:12 +08:00
|
|
|
case AD1986A_ULTRA:
|
|
|
|
spec->mixers[0] = ad1986a_laptop_eapd_mixers;
|
|
|
|
spec->num_init_verbs = 2;
|
|
|
|
spec->init_verbs[1] = ad1986a_ultra_init;
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
|
|
|
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
break;
|
2006-03-13 20:49:49 +08:00
|
|
|
}
|
|
|
|
|
2007-10-26 18:35:56 +08:00
|
|
|
/* AD1986A has a hardware problem that it can't share a stream
|
|
|
|
* with multiple output pins. The copy of front to surrounds
|
|
|
|
* causes noisy or silent outputs at a certain timing, e.g.
|
|
|
|
* changing the volume.
|
|
|
|
* So, let's disable the shared stream.
|
|
|
|
*/
|
|
|
|
spec->multiout.no_share_stream = 1;
|
|
|
|
|
2009-12-26 05:49:01 +08:00
|
|
|
codec->no_trigger_sense = 1;
|
2010-10-25 16:37:11 +08:00
|
|
|
codec->no_sticky_stream = 1;
|
2009-12-26 05:49:01 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1986a ad1986a_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
2005-04-14 19:35:51 +08:00
|
|
|
* AD1983 specific
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-04-14 19:35:51 +08:00
|
|
|
#define AD1983_SPDIF_OUT 0x02
|
|
|
|
#define AD1983_DAC 0x03
|
|
|
|
#define AD1983_ADC 0x04
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
|
|
|
|
static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
|
|
|
|
static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
|
2005-04-14 19:35:51 +08:00
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1983_capture_source = {
|
2005-04-14 19:35:51 +08:00
|
|
|
.num_items = 4,
|
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
|
|
|
{ "Line", 0x1 },
|
|
|
|
{ "Mix", 0x2 },
|
|
|
|
{ "Mix Mono", 0x3 },
|
|
|
|
},
|
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
2005-04-14 19:35:51 +08:00
|
|
|
* SPDIF playback route
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2005-11-17 21:57:47 +08:00
|
|
|
static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-05-02 17:33:15 +08:00
|
|
|
static const char * const texts[] = { "PCM", "ADC" };
|
2005-04-14 19:35:51 +08:00
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.enumerated.items = 2;
|
|
|
|
if (uinfo->value.enumerated.item > 1)
|
|
|
|
uinfo->value.enumerated.item = 1;
|
|
|
|
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-11-17 21:57:47 +08:00
|
|
|
static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-04-14 19:35:51 +08:00
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
ucontrol->value.enumerated.item[0] = spec->spdif_route;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:57:47 +08:00
|
|
|
static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-04-14 19:35:51 +08:00
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
|
2007-11-15 22:54:38 +08:00
|
|
|
if (ucontrol->value.enumerated.item[0] > 1)
|
|
|
|
return -EINVAL;
|
2005-04-14 19:35:51 +08:00
|
|
|
if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
|
|
|
|
spec->spdif_route = ucontrol->value.enumerated.item[0];
|
2007-08-10 23:09:26 +08:00
|
|
|
snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
|
|
|
|
AC_VERB_SET_CONNECT_SEL,
|
|
|
|
spec->spdif_route);
|
2005-04-14 19:35:51 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1983_mixers[] = {
|
2005-04-14 19:35:51 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
|
2005-04-14 19:35:51 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
2005-04-17 06:20:36 +08:00
|
|
|
},
|
2005-04-14 19:35:51 +08:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
2006-06-13 17:57:22 +08:00
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
2005-04-14 19:35:51 +08:00
|
|
|
.info = ad1983_spdif_route_info,
|
|
|
|
.get = ad1983_spdif_route_get,
|
|
|
|
.put = ad1983_spdif_route_put,
|
2005-04-17 06:20:36 +08:00
|
|
|
},
|
2005-04-14 19:35:51 +08:00
|
|
|
{ } /* end */
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1983_init_verbs[] = {
|
2005-04-14 19:35:51 +08:00
|
|
|
/* Front, HP, Mono; mute as default */
|
|
|
|
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* Beep, PCM, Mic, Line-In: mute */
|
|
|
|
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* Front, HP selectors; from Mix */
|
|
|
|
{0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
|
|
|
|
{0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
|
|
|
|
/* Mono selector; from Mix */
|
|
|
|
{0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
|
|
|
|
/* Mic selector; Mic */
|
|
|
|
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Line-in selector: Line-in */
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Mic boost: 0dB */
|
|
|
|
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
|
|
|
|
/* Record selector: mic */
|
|
|
|
{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* SPDIF route: PCM */
|
|
|
|
{0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Front Pin */
|
|
|
|
{0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
/* HP Pin */
|
|
|
|
{0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
|
|
|
|
/* Mono Pin */
|
|
|
|
{0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
/* Mic Pin */
|
|
|
|
{0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
|
|
|
|
/* Line Pin */
|
|
|
|
{0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
|
|
|
|
{ } /* end */
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_amp_list ad1983_loopbacks[] = {
|
2007-08-10 23:21:45 +08:00
|
|
|
{ 0x12, HDA_OUTPUT, 0 }, /* Mic */
|
|
|
|
{ 0x13, HDA_OUTPUT, 0 }, /* Line */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
#endif
|
2005-11-03 01:26:49 +08:00
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
/* models */
|
|
|
|
enum {
|
|
|
|
AD1983_AUTO,
|
|
|
|
AD1983_BASIC,
|
|
|
|
AD1983_MODELS
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char * const ad1983_models[AD1983_MODELS] = {
|
|
|
|
[AD1983_AUTO] = "auto",
|
|
|
|
[AD1983_BASIC] = "basic",
|
|
|
|
};
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
/*
|
|
|
|
* SPDIF mux control for AD1983 auto-parser
|
|
|
|
*/
|
|
|
|
static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
static const char * const texts2[] = { "PCM", "ADC" };
|
|
|
|
static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
|
|
|
|
hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
|
|
|
|
int num_conns = snd_hda_get_num_conns(codec, dig_out);
|
|
|
|
|
|
|
|
if (num_conns == 2)
|
|
|
|
return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
|
|
|
|
else if (num_conns == 3)
|
|
|
|
return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
|
|
|
|
ucontrol->value.enumerated.item[0] = spec->cur_smux;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
unsigned int val = ucontrol->value.enumerated.item[0];
|
|
|
|
hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
|
|
|
|
int num_conns = snd_hda_get_num_conns(codec, dig_out);
|
|
|
|
|
|
|
|
if (val >= num_conns)
|
|
|
|
return -EINVAL;
|
|
|
|
if (spec->cur_smux == val)
|
|
|
|
return 0;
|
|
|
|
spec->cur_smux = val;
|
|
|
|
snd_hda_codec_write_cache(codec, dig_out, 0,
|
|
|
|
AC_VERB_SET_CONNECT_SEL, val);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct snd_kcontrol_new ad1983_auto_smux_mixer = {
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "IEC958 Playback Source",
|
|
|
|
.info = ad1983_auto_smux_enum_info,
|
|
|
|
.get = ad1983_auto_smux_enum_get,
|
|
|
|
.put = ad1983_auto_smux_enum_put,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
|
|
|
|
int num_conns;
|
|
|
|
|
|
|
|
if (!dig_out)
|
|
|
|
return 0;
|
|
|
|
num_conns = snd_hda_get_num_conns(codec, dig_out);
|
|
|
|
if (num_conns != 2 && num_conns != 3)
|
|
|
|
return 0;
|
|
|
|
if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
|
|
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
static int ad1983_parse_auto_config(struct hda_codec *codec)
|
|
|
|
{
|
2013-01-22 23:45:58 +08:00
|
|
|
struct ad198x_spec *spec;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
int err;
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
spec->beep_dev_nid = 0x10;
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad198x_parse_auto_config(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad1983_add_spdif_mux_ctl(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
return 0;
|
2013-01-22 23:45:58 +08:00
|
|
|
|
|
|
|
error:
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
2012-12-21 22:17:06 +08:00
|
|
|
}
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-04-14 19:35:51 +08:00
|
|
|
static int patch_ad1983(struct hda_codec *codec)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-04-14 19:35:51 +08:00
|
|
|
struct ad198x_spec *spec;
|
2012-12-21 22:17:06 +08:00
|
|
|
int board_config;
|
2009-02-07 00:22:05 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
board_config = snd_hda_check_board_config(codec, AD1983_MODELS,
|
|
|
|
ad1983_models, NULL);
|
|
|
|
if (board_config == AD1983_AUTO)
|
|
|
|
return ad1983_parse_auto_config(codec);
|
|
|
|
|
2012-05-09 20:35:27 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2005-04-14 19:35:51 +08:00
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
err = snd_hda_attach_beep_device(codec, 0x10);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
|
|
|
|
spec->multiout.dac_nids = ad1983_dac_nids;
|
|
|
|
spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
|
2005-11-03 01:26:49 +08:00
|
|
|
spec->num_adc_nids = 1;
|
|
|
|
spec->adc_nids = ad1983_adc_nids;
|
2006-03-02 02:54:39 +08:00
|
|
|
spec->capsrc_nids = ad1983_capsrc_nids;
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->input_mux = &ad1983_capture_source;
|
2005-11-03 01:26:49 +08:00
|
|
|
spec->num_mixers = 1;
|
|
|
|
spec->mixers[0] = ad1983_mixers;
|
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1983_init_verbs;
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->spdif_route = 0;
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
spec->loopback.amplist = ad1983_loopbacks;
|
|
|
|
#endif
|
2008-01-10 23:53:55 +08:00
|
|
|
spec->vmaster_nid = 0x05;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
codec->patch_ops = ad198x_patch_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-26 05:49:01 +08:00
|
|
|
codec->no_trigger_sense = 1;
|
2010-10-25 16:37:11 +08:00
|
|
|
codec->no_sticky_stream = 1;
|
2009-12-26 05:49:01 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1983 ad1983_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
/*
|
|
|
|
* AD1981 HD specific
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-04-14 19:35:51 +08:00
|
|
|
#define AD1981_SPDIF_OUT 0x02
|
|
|
|
#define AD1981_DAC 0x03
|
|
|
|
#define AD1981_ADC 0x04
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
|
|
|
|
static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
|
|
|
|
static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
|
2005-04-14 19:35:51 +08:00
|
|
|
|
|
|
|
/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1981_capture_source = {
|
2005-04-14 19:35:51 +08:00
|
|
|
.num_items = 7,
|
|
|
|
.items = {
|
|
|
|
{ "Front Mic", 0x0 },
|
|
|
|
{ "Line", 0x1 },
|
|
|
|
{ "Mix", 0x2 },
|
|
|
|
{ "Mix Mono", 0x3 },
|
|
|
|
{ "CD", 0x4 },
|
|
|
|
{ "Mic", 0x6 },
|
|
|
|
{ "Aux", 0x7 },
|
|
|
|
},
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1981_mixers[] = {
|
2005-04-14 19:35:51 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
|
2005-04-14 19:35:51 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
/* identical with AD1983 */
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
2006-06-13 17:57:22 +08:00
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
2005-04-14 19:35:51 +08:00
|
|
|
.info = ad1983_spdif_route_info,
|
|
|
|
.get = ad1983_spdif_route_get,
|
|
|
|
.put = ad1983_spdif_route_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1981_init_verbs[] = {
|
2005-04-14 19:35:51 +08:00
|
|
|
/* Front, HP, Mono; mute as default */
|
|
|
|
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
|
|
|
|
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* Front, HP selectors; from Mix */
|
|
|
|
{0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
|
|
|
|
{0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
|
|
|
|
/* Mono selector; from Mix */
|
|
|
|
{0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
|
|
|
|
/* Mic Mixer; select Front Mic */
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
|
|
|
|
{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* Mic boost: 0dB */
|
2009-01-23 19:33:54 +08:00
|
|
|
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
2005-04-14 19:35:51 +08:00
|
|
|
/* Record selector: Front mic */
|
|
|
|
{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
/* SPDIF route: PCM */
|
|
|
|
{0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Front Pin */
|
|
|
|
{0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
/* HP Pin */
|
|
|
|
{0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
|
|
|
|
/* Mono Pin */
|
|
|
|
{0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
|
|
|
|
/* Front & Rear Mic Pins */
|
|
|
|
{0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
|
|
|
|
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
|
|
|
|
/* Line Pin */
|
|
|
|
{0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
|
|
|
|
/* Digital Beep */
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
|
|
|
|
/* Line-Out as Input: disabled */
|
|
|
|
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_amp_list ad1981_loopbacks[] = {
|
2007-08-10 23:21:45 +08:00
|
|
|
{ 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
|
|
|
|
{ 0x13, HDA_OUTPUT, 0 }, /* Line */
|
|
|
|
{ 0x1b, HDA_OUTPUT, 0 }, /* Aux */
|
|
|
|
{ 0x1c, HDA_OUTPUT, 0 }, /* Mic */
|
|
|
|
{ 0x1d, HDA_OUTPUT, 0 }, /* CD */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2006-03-02 02:54:39 +08:00
|
|
|
/*
|
|
|
|
* Patch for HP nx6320
|
|
|
|
*
|
2007-03-13 05:20:51 +08:00
|
|
|
* nx6320 uses EAPD in the reverse way - EAPD-on means the internal
|
2006-03-02 02:54:39 +08:00
|
|
|
* speaker output enabled _and_ mute-LED off.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define AD1981_HP_EVENT 0x37
|
|
|
|
#define AD1981_MIC_EVENT 0x38
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1981_hp_init_verbs[] = {
|
2006-03-02 02:54:39 +08:00
|
|
|
{0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
|
|
|
|
/* pin sensing on HP and Mic jacks */
|
|
|
|
{0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
|
|
|
|
{0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* turn on/off EAPD (+ mute HP) as a master switch */
|
|
|
|
static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
|
|
|
|
if (! ad198x_eapd_put(kcontrol, ucontrol))
|
|
|
|
return 0;
|
2008-02-11 22:54:34 +08:00
|
|
|
/* change speaker pin appropriately */
|
2012-04-20 18:34:50 +08:00
|
|
|
snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
|
2006-03-02 02:54:39 +08:00
|
|
|
/* toggle HP mute appropriately */
|
2007-08-10 23:11:07 +08:00
|
|
|
snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
|
|
|
|
HDA_AMP_MUTE,
|
|
|
|
spec->cur_eapd ? 0 : HDA_AMP_MUTE);
|
2006-03-02 02:54:39 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bind volumes of both NID 0x05 and 0x06 */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
|
2007-08-10 23:12:15 +08:00
|
|
|
.ops = &snd_hda_bind_vol,
|
|
|
|
.values = {
|
|
|
|
HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
|
|
|
|
HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
|
|
|
|
0
|
|
|
|
},
|
|
|
|
};
|
2006-03-02 02:54:39 +08:00
|
|
|
|
|
|
|
/* mute internal speaker if HP is plugged */
|
|
|
|
static void ad1981_hp_automute(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int present;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
present = snd_hda_jack_detect(codec, 0x06);
|
2007-08-10 23:11:07 +08:00
|
|
|
snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
|
|
|
|
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
2006-03-02 02:54:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* toggle input of built-in and mic jack appropriately */
|
|
|
|
static void ad1981_hp_automic(struct hda_codec *codec)
|
|
|
|
{
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb mic_jack_on[] = {
|
2006-03-02 02:54:39 +08:00
|
|
|
{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
|
|
|
|
{}
|
|
|
|
};
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb mic_jack_off[] = {
|
2006-03-02 02:54:39 +08:00
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
|
|
|
|
{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
unsigned int present;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
present = snd_hda_jack_detect(codec, 0x08);
|
2006-03-02 02:54:39 +08:00
|
|
|
if (present)
|
|
|
|
snd_hda_sequence_write(codec, mic_jack_on);
|
|
|
|
else
|
|
|
|
snd_hda_sequence_write(codec, mic_jack_off);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unsolicited event for HP jack sensing */
|
|
|
|
static void ad1981_hp_unsol_event(struct hda_codec *codec,
|
|
|
|
unsigned int res)
|
|
|
|
{
|
|
|
|
res >>= 26;
|
|
|
|
switch (res) {
|
|
|
|
case AD1981_HP_EVENT:
|
|
|
|
ad1981_hp_automute(codec);
|
|
|
|
break;
|
|
|
|
case AD1981_MIC_EVENT:
|
|
|
|
ad1981_hp_automic(codec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1981_hp_capture_source = {
|
2006-03-02 02:54:39 +08:00
|
|
|
.num_items = 3,
|
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
2012-09-19 18:19:47 +08:00
|
|
|
{ "Dock Mic", 0x1 },
|
2006-03-02 02:54:39 +08:00
|
|
|
{ "Mix", 0x2 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
|
2007-08-10 23:12:15 +08:00
|
|
|
HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
|
2006-03-02 02:54:39 +08:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
2009-12-08 23:13:32 +08:00
|
|
|
.subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
|
2006-03-02 02:54:39 +08:00
|
|
|
.name = "Master Playback Switch",
|
|
|
|
.info = ad198x_eapd_info,
|
|
|
|
.get = ad198x_eapd_get,
|
|
|
|
.put = ad1981_hp_master_sw_put,
|
|
|
|
.private_value = 0x05,
|
|
|
|
},
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
#if 0
|
|
|
|
/* FIXME: analog mic/line loopback doesn't work with my tests...
|
|
|
|
* (although recording is OK)
|
|
|
|
*/
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
2012-09-19 18:19:47 +08:00
|
|
|
HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
2006-03-02 02:54:39 +08:00
|
|
|
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
|
|
|
|
/* FIXME: does this laptop have analog CD connection? */
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
|
|
|
|
#endif
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
|
2006-03-02 02:54:39 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* initialize jack-sensing, too */
|
|
|
|
static int ad1981_hp_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1981_hp_automute(codec);
|
|
|
|
ad1981_hp_automic(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-13 05:20:51 +08:00
|
|
|
/* configuration for Toshiba Laptops */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1981_toshiba_init_verbs[] = {
|
2007-03-13 05:20:51 +08:00
|
|
|
{0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
|
|
|
|
/* pin sensing on HP and Mic jacks */
|
|
|
|
{0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
|
|
|
|
{0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
|
2007-03-13 05:20:51 +08:00
|
|
|
HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2006-04-18 18:54:11 +08:00
|
|
|
/* configuration for Lenovo Thinkpad T60 */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
|
2006-04-18 18:54:11 +08:00
|
|
|
HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
|
2006-04-18 18:54:11 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
2006-06-13 17:57:22 +08:00
|
|
|
/* identical with AD1983 */
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
|
|
|
.info = ad1983_spdif_route_info,
|
|
|
|
.get = ad1983_spdif_route_get,
|
|
|
|
.put = ad1983_spdif_route_put,
|
|
|
|
},
|
2006-04-18 18:54:11 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1981_thinkpad_capture_source = {
|
2006-04-18 18:54:11 +08:00
|
|
|
.num_items = 3,
|
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
|
|
|
{ "Mix", 0x2 },
|
|
|
|
{ "CD", 0x4 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2006-03-02 02:54:39 +08:00
|
|
|
/* models */
|
2006-11-25 00:07:44 +08:00
|
|
|
enum {
|
2012-12-21 22:17:06 +08:00
|
|
|
AD1981_AUTO,
|
2006-11-25 00:07:44 +08:00
|
|
|
AD1981_BASIC,
|
|
|
|
AD1981_HP,
|
|
|
|
AD1981_THINKPAD,
|
2007-03-13 05:20:51 +08:00
|
|
|
AD1981_TOSHIBA,
|
2006-11-25 00:07:44 +08:00
|
|
|
AD1981_MODELS
|
|
|
|
};
|
2006-03-02 02:54:39 +08:00
|
|
|
|
2011-01-17 18:29:34 +08:00
|
|
|
static const char * const ad1981_models[AD1981_MODELS] = {
|
2012-12-21 22:17:06 +08:00
|
|
|
[AD1981_AUTO] = "auto",
|
2006-11-25 00:07:44 +08:00
|
|
|
[AD1981_HP] = "hp",
|
|
|
|
[AD1981_THINKPAD] = "thinkpad",
|
|
|
|
[AD1981_BASIC] = "basic",
|
2007-03-13 05:20:51 +08:00
|
|
|
[AD1981_TOSHIBA] = "toshiba"
|
2006-11-25 00:07:44 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
|
2007-12-18 00:14:18 +08:00
|
|
|
SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
|
2008-06-30 22:40:10 +08:00
|
|
|
SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
|
2006-04-18 18:50:40 +08:00
|
|
|
/* All HP models */
|
2009-02-10 00:14:52 +08:00
|
|
|
SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
|
2007-12-18 00:14:18 +08:00
|
|
|
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
|
2006-04-18 18:54:11 +08:00
|
|
|
/* Lenovo Thinkpad T60/X60/Z6xx */
|
2009-02-10 00:14:52 +08:00
|
|
|
SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
|
2007-12-18 00:14:18 +08:00
|
|
|
/* HP nx6320 (reversed SSID, H/W bug) */
|
|
|
|
SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
|
2006-03-02 02:54:39 +08:00
|
|
|
{}
|
|
|
|
};
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
|
2006-03-02 02:54:39 +08:00
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
static int ad1981_parse_auto_config(struct hda_codec *codec)
|
|
|
|
{
|
2013-01-22 23:45:58 +08:00
|
|
|
struct ad198x_spec *spec;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
int err;
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
spec = codec->spec;
|
|
|
|
|
2013-01-21 23:40:16 +08:00
|
|
|
spec->gen.mixer_nid = 0x0e;
|
2012-12-21 22:17:06 +08:00
|
|
|
spec->beep_dev_nid = 0x10;
|
|
|
|
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad198x_parse_auto_config(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad1983_add_spdif_mux_ctl(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
return 0;
|
2013-01-22 23:45:58 +08:00
|
|
|
|
|
|
|
error:
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
2012-12-21 22:17:06 +08:00
|
|
|
}
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-04-14 19:35:51 +08:00
|
|
|
static int patch_ad1981(struct hda_codec *codec)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-04-14 19:35:51 +08:00
|
|
|
struct ad198x_spec *spec;
|
2009-02-07 00:22:05 +08:00
|
|
|
int err, board_config;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
|
|
|
|
ad1981_models,
|
|
|
|
ad1981_cfg_tbl);
|
|
|
|
if (board_config == AD1981_AUTO)
|
|
|
|
return ad1981_parse_auto_config(codec);
|
|
|
|
|
2012-05-09 20:35:27 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
2012-05-09 20:35:27 +08:00
|
|
|
spec = codec->spec;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
err = snd_hda_attach_beep_device(codec, 0x10);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
|
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
|
|
|
|
spec->multiout.dac_nids = ad1981_dac_nids;
|
|
|
|
spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
|
2005-11-03 01:26:49 +08:00
|
|
|
spec->num_adc_nids = 1;
|
|
|
|
spec->adc_nids = ad1981_adc_nids;
|
2006-03-02 02:54:39 +08:00
|
|
|
spec->capsrc_nids = ad1981_capsrc_nids;
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->input_mux = &ad1981_capture_source;
|
2005-11-03 01:26:49 +08:00
|
|
|
spec->num_mixers = 1;
|
|
|
|
spec->mixers[0] = ad1981_mixers;
|
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1981_init_verbs;
|
2005-04-14 19:35:51 +08:00
|
|
|
spec->spdif_route = 0;
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
spec->loopback.amplist = ad1981_loopbacks;
|
|
|
|
#endif
|
2008-01-10 23:53:55 +08:00
|
|
|
spec->vmaster_nid = 0x05;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
codec->patch_ops = ad198x_patch_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-03-02 02:54:39 +08:00
|
|
|
/* override some parameters */
|
|
|
|
switch (board_config) {
|
|
|
|
case AD1981_HP:
|
|
|
|
spec->mixers[0] = ad1981_hp_mixers;
|
|
|
|
spec->num_init_verbs = 2;
|
|
|
|
spec->init_verbs[1] = ad1981_hp_init_verbs;
|
2011-06-10 20:37:04 +08:00
|
|
|
if (!is_jack_available(codec, 0x0a))
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
2006-03-02 02:54:39 +08:00
|
|
|
spec->input_mux = &ad1981_hp_capture_source;
|
|
|
|
|
|
|
|
codec->patch_ops.init = ad1981_hp_init;
|
|
|
|
codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
|
2009-12-14 05:22:58 +08:00
|
|
|
/* set the upper-limit for mixer amp to 0dB for avoiding the
|
|
|
|
* possible damage by overloading
|
|
|
|
*/
|
|
|
|
snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
|
|
|
|
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
|
|
|
|
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
|
|
|
|
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
|
|
|
|
(1 << AC_AMPCAP_MUTE_SHIFT));
|
2006-03-02 02:54:39 +08:00
|
|
|
break;
|
2006-04-18 18:54:11 +08:00
|
|
|
case AD1981_THINKPAD:
|
|
|
|
spec->mixers[0] = ad1981_thinkpad_mixers;
|
|
|
|
spec->input_mux = &ad1981_thinkpad_capture_source;
|
2010-03-31 01:29:28 +08:00
|
|
|
/* set the upper-limit for mixer amp to 0dB for avoiding the
|
|
|
|
* possible damage by overloading
|
|
|
|
*/
|
|
|
|
snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
|
|
|
|
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
|
|
|
|
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
|
|
|
|
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
|
|
|
|
(1 << AC_AMPCAP_MUTE_SHIFT));
|
2006-04-18 18:54:11 +08:00
|
|
|
break;
|
2007-03-13 05:20:51 +08:00
|
|
|
case AD1981_TOSHIBA:
|
|
|
|
spec->mixers[0] = ad1981_hp_mixers;
|
|
|
|
spec->mixers[1] = ad1981_toshiba_mixers;
|
|
|
|
spec->num_init_verbs = 2;
|
|
|
|
spec->init_verbs[1] = ad1981_toshiba_init_verbs;
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
spec->input_mux = &ad1981_hp_capture_source;
|
|
|
|
codec->patch_ops.init = ad1981_hp_init;
|
|
|
|
codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
|
|
|
|
break;
|
2006-03-02 02:54:39 +08:00
|
|
|
}
|
2009-12-26 05:49:01 +08:00
|
|
|
|
|
|
|
codec->no_trigger_sense = 1;
|
2010-10-25 16:37:11 +08:00
|
|
|
codec->no_sticky_stream = 1;
|
2009-12-26 05:49:01 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1981 ad1981_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-04-14 19:35:51 +08:00
|
|
|
|
2005-11-17 22:31:34 +08:00
|
|
|
/*
|
|
|
|
* AD1988
|
|
|
|
*
|
|
|
|
* Output pins and routes
|
|
|
|
*
|
2005-11-24 23:06:23 +08:00
|
|
|
* Pin Mix Sel DAC (*)
|
2005-11-17 22:31:34 +08:00
|
|
|
* port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
|
|
|
|
* port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
|
|
|
|
* port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
|
|
|
|
* port-D 0x12 (mute/hp) <- 0x29 <- 04
|
|
|
|
* port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
|
|
|
|
* port-F 0x16 (mute) <- 0x2a <- 06
|
|
|
|
* port-G 0x24 (mute) <- 0x27 <- 05
|
|
|
|
* port-H 0x25 (mute) <- 0x28 <- 0a
|
|
|
|
* mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
|
|
|
|
*
|
2005-11-24 23:06:23 +08:00
|
|
|
* DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
|
|
|
|
* (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
|
2005-11-17 22:31:34 +08:00
|
|
|
*
|
|
|
|
* Input pins and routes
|
|
|
|
*
|
|
|
|
* pin boost mix input # / adc input #
|
|
|
|
* port-A 0x11 -> 0x38 -> mix 2, ADC 0
|
|
|
|
* port-B 0x14 -> 0x39 -> mix 0, ADC 1
|
|
|
|
* port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
|
|
|
|
* port-D 0x12 -> 0x3d -> mix 3, ADC 8
|
|
|
|
* port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
|
|
|
|
* port-F 0x16 -> 0x3b -> mix 5, ADC 3
|
|
|
|
* port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
|
|
|
|
* port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* DAC assignment
|
2005-11-24 23:06:23 +08:00
|
|
|
* 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
|
2005-11-24 23:17:20 +08:00
|
|
|
* 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
|
2005-11-17 22:31:34 +08:00
|
|
|
*
|
|
|
|
* Inputs of Analog Mix (0x20)
|
|
|
|
* 0:Port-B (front mic)
|
|
|
|
* 1:Port-C/G/H (line-in)
|
|
|
|
* 2:Port-A
|
|
|
|
* 3:Port-D (line-in/2)
|
|
|
|
* 4:Port-E/G/H (mic-in)
|
|
|
|
* 5:Port-F (mic2-in)
|
|
|
|
* 6:CD
|
|
|
|
* 7:Beep
|
|
|
|
*
|
|
|
|
* ADC selection
|
|
|
|
* 0:Port-A
|
|
|
|
* 1:Port-B (front mic-in)
|
|
|
|
* 2:Port-C (line-in)
|
|
|
|
* 3:Port-F (mic2-in)
|
|
|
|
* 4:Port-E (mic-in)
|
|
|
|
* 5:CD
|
|
|
|
* 6:Port-G
|
|
|
|
* 7:Port-H
|
|
|
|
* 8:Port-D (line-in/2)
|
|
|
|
* 9:Mix
|
|
|
|
*
|
|
|
|
* Proposed pin assignments by the datasheet
|
|
|
|
*
|
|
|
|
* 6-stack
|
|
|
|
* Port-A front headphone
|
|
|
|
* B front mic-in
|
|
|
|
* C rear line-in
|
|
|
|
* D rear front-out
|
|
|
|
* E rear mic-in
|
|
|
|
* F rear surround
|
|
|
|
* G rear CLFE
|
|
|
|
* H rear side
|
|
|
|
*
|
|
|
|
* 3-stack
|
|
|
|
* Port-A front headphone
|
|
|
|
* B front mic
|
|
|
|
* C rear line-in/surround
|
|
|
|
* D rear front-out
|
|
|
|
* E rear mic-in/CLFE
|
|
|
|
*
|
|
|
|
* laptop
|
|
|
|
* Port-A headphone
|
|
|
|
* B mic-in
|
|
|
|
* C docking station
|
|
|
|
* D internal speaker (with EAPD)
|
|
|
|
* E/F quad mic array
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2005-11-17 22:31:34 +08:00
|
|
|
/* models */
|
|
|
|
enum {
|
2012-12-21 22:17:06 +08:00
|
|
|
AD1988_AUTO,
|
2005-11-17 22:31:34 +08:00
|
|
|
AD1988_6STACK,
|
|
|
|
AD1988_6STACK_DIG,
|
|
|
|
AD1988_3STACK,
|
|
|
|
AD1988_3STACK_DIG,
|
|
|
|
AD1988_LAPTOP,
|
|
|
|
AD1988_LAPTOP_DIG,
|
|
|
|
AD1988_MODEL_LAST,
|
|
|
|
};
|
|
|
|
|
2005-11-24 23:06:23 +08:00
|
|
|
/* reivision id to check workarounds */
|
|
|
|
#define AD1988A_REV2 0x100200
|
|
|
|
|
2006-07-03 21:58:16 +08:00
|
|
|
#define is_rev2(codec) \
|
|
|
|
((codec)->vendor_id == 0x11d41988 && \
|
|
|
|
(codec)->revision_id == AD1988A_REV2)
|
2005-11-17 22:31:34 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* mixers
|
|
|
|
*/
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1988_6stack_dac_nids[4] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
0x04, 0x06, 0x05, 0x0a
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1988_3stack_dac_nids[3] = {
|
2005-11-24 23:17:20 +08:00
|
|
|
0x04, 0x05, 0x0a
|
2005-11-24 23:06:23 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* for AD1988A revision-2, DAC2-4 are swapped */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
|
2005-11-24 23:06:23 +08:00
|
|
|
0x04, 0x05, 0x0a, 0x06
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1988_alt_dac_nid[1] = {
|
2011-01-17 18:19:03 +08:00
|
|
|
0x03
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
|
2005-11-24 23:17:20 +08:00
|
|
|
0x04, 0x0a, 0x06
|
2005-11-24 23:06:23 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1988_adc_nids[3] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
0x08, 0x09, 0x0f
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1988_capsrc_nids[3] = {
|
2005-11-21 23:36:15 +08:00
|
|
|
0x0c, 0x0d, 0x0e
|
|
|
|
};
|
|
|
|
|
2008-09-14 07:54:58 +08:00
|
|
|
#define AD1988_SPDIF_OUT 0x02
|
|
|
|
#define AD1988_SPDIF_OUT_HDMI 0x0b
|
2005-11-17 22:31:34 +08:00
|
|
|
#define AD1988_SPDIF_IN 0x07
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1989b_slave_dig_outs[] = {
|
2009-02-13 18:37:08 +08:00
|
|
|
AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
|
2008-09-14 07:54:58 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1988_6stack_capture_source = {
|
2005-11-17 22:31:34 +08:00
|
|
|
.num_items = 5,
|
|
|
|
.items = {
|
2008-02-25 22:32:01 +08:00
|
|
|
{ "Front Mic", 0x1 }, /* port-B */
|
|
|
|
{ "Line", 0x2 }, /* port-C */
|
|
|
|
{ "Mic", 0x4 }, /* port-E */
|
2005-11-17 22:31:34 +08:00
|
|
|
{ "CD", 0x5 },
|
|
|
|
{ "Mix", 0x9 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1988_laptop_capture_source = {
|
2005-11-17 22:31:34 +08:00
|
|
|
.num_items = 3,
|
|
|
|
.items = {
|
2008-02-25 22:32:01 +08:00
|
|
|
{ "Mic/Line", 0x1 }, /* port-B */
|
2005-11-17 22:31:34 +08:00
|
|
|
{ "CD", 0x5 },
|
|
|
|
{ "Mix", 0x9 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
|
|
|
|
spec->num_channel_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
|
|
|
|
spec->num_channel_mode, spec->multiout.max_channels);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
2006-07-28 20:47:34 +08:00
|
|
|
int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
|
|
|
|
spec->num_channel_mode,
|
|
|
|
&spec->multiout.max_channels);
|
2006-10-11 01:49:31 +08:00
|
|
|
if (err >= 0 && spec->need_dac_fix)
|
2006-03-27 18:52:22 +08:00
|
|
|
spec->multiout.num_dacs = spec->multiout.max_channels / 2;
|
2006-07-28 20:47:34 +08:00
|
|
|
return err;
|
2005-11-17 22:31:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 6-stack mode */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
|
2006-07-07 01:16:40 +08:00
|
|
|
{ } /* end */
|
2005-11-24 23:06:23 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
|
2005-11-24 23:06:23 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
|
2006-07-07 01:16:40 +08:00
|
|
|
{ } /* end */
|
2005-11-24 23:06:23 +08:00
|
|
|
};
|
2005-11-17 22:31:34 +08:00
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
|
2011-08-31 10:30:59 +08:00
|
|
|
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
|
|
|
|
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
|
|
|
|
|
2005-11-21 23:36:15 +08:00
|
|
|
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 3-stack mode */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
2005-11-24 23:06:23 +08:00
|
|
|
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
|
2006-07-07 01:16:40 +08:00
|
|
|
{ } /* end */
|
2005-11-24 23:06:23 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
|
2005-11-24 23:06:23 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
2005-11-24 23:17:20 +08:00
|
|
|
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
|
2006-07-07 01:16:40 +08:00
|
|
|
{ } /* end */
|
2005-11-24 23:06:23 +08:00
|
|
|
};
|
2005-11-17 22:31:34 +08:00
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
|
2011-08-31 10:30:59 +08:00
|
|
|
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
|
2005-11-24 23:06:23 +08:00
|
|
|
HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
|
|
|
|
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
|
|
|
|
|
2005-11-21 23:36:15 +08:00
|
|
|
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Channel Mode",
|
|
|
|
.info = ad198x_ch_mode_info,
|
|
|
|
.get = ad198x_ch_mode_get,
|
|
|
|
.put = ad198x_ch_mode_put,
|
|
|
|
},
|
|
|
|
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* laptop mode */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
|
2011-08-31 10:30:59 +08:00
|
|
|
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
|
|
|
|
HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
|
|
|
|
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
|
|
|
|
|
2005-11-21 23:36:15 +08:00
|
|
|
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
|
2005-11-17 22:31:34 +08:00
|
|
|
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "External Amplifier",
|
2009-12-08 23:13:32 +08:00
|
|
|
.subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
|
2006-03-02 02:54:39 +08:00
|
|
|
.info = ad198x_eapd_info,
|
|
|
|
.get = ad198x_eapd_get,
|
|
|
|
.put = ad198x_eapd_put,
|
2009-12-09 00:23:33 +08:00
|
|
|
.private_value = 0x12, /* port-D */
|
2005-11-17 22:31:34 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* capture */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
/* The multiple "Capture Source" controls confuse alsamixer
|
|
|
|
* So call somewhat different..
|
|
|
|
*/
|
|
|
|
/* .name = "Capture Source", */
|
|
|
|
.name = "Input Source",
|
|
|
|
.count = 3,
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
|
|
|
{
|
2011-05-02 17:33:15 +08:00
|
|
|
static const char * const texts[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
"PCM", "ADC1", "ADC2", "ADC3"
|
|
|
|
};
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.enumerated.items = 4;
|
|
|
|
if (uinfo->value.enumerated.item >= 4)
|
|
|
|
uinfo->value.enumerated.item = 3;
|
|
|
|
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
unsigned int sel;
|
|
|
|
|
2007-07-25 00:04:05 +08:00
|
|
|
sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
|
|
|
|
AC_AMP_GET_INPUT);
|
|
|
|
if (!(sel & 0x80))
|
|
|
|
ucontrol->value.enumerated.item[0] = 0;
|
|
|
|
else {
|
2007-05-05 18:17:17 +08:00
|
|
|
sel = snd_hda_codec_read(codec, 0x0b, 0,
|
|
|
|
AC_VERB_GET_CONNECT_SEL, 0);
|
|
|
|
if (sel < 3)
|
2005-11-17 22:31:34 +08:00
|
|
|
sel++;
|
|
|
|
else
|
|
|
|
sel = 0;
|
2007-07-25 00:04:05 +08:00
|
|
|
ucontrol->value.enumerated.item[0] = sel;
|
2005-11-17 22:31:34 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
2007-05-05 18:17:17 +08:00
|
|
|
unsigned int val, sel;
|
2005-11-17 22:31:34 +08:00
|
|
|
int change;
|
|
|
|
|
2007-05-05 18:17:17 +08:00
|
|
|
val = ucontrol->value.enumerated.item[0];
|
2007-11-15 22:54:38 +08:00
|
|
|
if (val > 3)
|
|
|
|
return -EINVAL;
|
2007-05-05 18:17:17 +08:00
|
|
|
if (!val) {
|
2007-07-25 00:04:05 +08:00
|
|
|
sel = snd_hda_codec_read(codec, 0x1d, 0,
|
|
|
|
AC_VERB_GET_AMP_GAIN_MUTE,
|
|
|
|
AC_AMP_GET_INPUT);
|
|
|
|
change = sel & 0x80;
|
2007-08-10 23:09:26 +08:00
|
|
|
if (change) {
|
|
|
|
snd_hda_codec_write_cache(codec, 0x1d, 0,
|
|
|
|
AC_VERB_SET_AMP_GAIN_MUTE,
|
|
|
|
AMP_IN_UNMUTE(0));
|
|
|
|
snd_hda_codec_write_cache(codec, 0x1d, 0,
|
|
|
|
AC_VERB_SET_AMP_GAIN_MUTE,
|
|
|
|
AMP_IN_MUTE(1));
|
2007-07-25 00:04:05 +08:00
|
|
|
}
|
2005-11-17 22:31:34 +08:00
|
|
|
} else {
|
2007-07-25 00:04:05 +08:00
|
|
|
sel = snd_hda_codec_read(codec, 0x1d, 0,
|
|
|
|
AC_VERB_GET_AMP_GAIN_MUTE,
|
|
|
|
AC_AMP_GET_INPUT | 0x01);
|
|
|
|
change = sel & 0x80;
|
2007-08-10 23:09:26 +08:00
|
|
|
if (change) {
|
|
|
|
snd_hda_codec_write_cache(codec, 0x1d, 0,
|
|
|
|
AC_VERB_SET_AMP_GAIN_MUTE,
|
|
|
|
AMP_IN_MUTE(0));
|
|
|
|
snd_hda_codec_write_cache(codec, 0x1d, 0,
|
|
|
|
AC_VERB_SET_AMP_GAIN_MUTE,
|
|
|
|
AMP_IN_UNMUTE(1));
|
2007-07-25 00:04:05 +08:00
|
|
|
}
|
2007-05-05 18:17:17 +08:00
|
|
|
sel = snd_hda_codec_read(codec, 0x0b, 0,
|
|
|
|
AC_VERB_GET_CONNECT_SEL, 0) + 1;
|
|
|
|
change |= sel != val;
|
2007-08-10 23:09:26 +08:00
|
|
|
if (change)
|
|
|
|
snd_hda_codec_write_cache(codec, 0x0b, 0,
|
|
|
|
AC_VERB_SET_CONNECT_SEL,
|
|
|
|
val - 1);
|
2005-11-17 22:31:34 +08:00
|
|
|
}
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "IEC958 Playback Source",
|
2009-12-08 23:13:32 +08:00
|
|
|
.subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
|
2005-11-17 22:31:34 +08:00
|
|
|
.info = ad1988_spdif_playback_source_info,
|
|
|
|
.get = ad1988_spdif_playback_source_get,
|
|
|
|
.put = ad1988_spdif_playback_source_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
|
2008-04-16 00:46:42 +08:00
|
|
|
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
2008-09-14 07:54:58 +08:00
|
|
|
HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
|
2008-04-16 00:46:42 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
2005-11-17 22:31:34 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* initialization verbs
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* for 6-stack (+dig)
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_6stack_init_verbs[] = {
|
2005-11-21 23:36:15 +08:00
|
|
|
/* Front, Surround, CLFE, side DAC; unmute as default */
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
2005-11-17 22:31:34 +08:00
|
|
|
/* Port-A front headphon path */
|
2011-08-31 10:30:59 +08:00
|
|
|
{0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
|
2005-11-17 22:31:34 +08:00
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
/* Port-D line-out path */
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
/* Port-F surround path */
|
|
|
|
{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
/* Port-G CLFE path */
|
|
|
|
{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
/* Port-H side path */
|
|
|
|
{0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
/* Mono out path */
|
|
|
|
{0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
|
|
|
|
/* Port-B front mic-in path */
|
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
/* Port-C line-in path */
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
|
|
|
{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
{0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* Port-E mic-in path */
|
|
|
|
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
|
2007-12-14 00:51:00 +08:00
|
|
|
/* Analog CD Input */
|
|
|
|
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
2008-08-12 00:08:54 +08:00
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
|
2005-11-17 22:31:34 +08:00
|
|
|
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
|
2011-01-17 18:19:03 +08:00
|
|
|
/* Headphone; unmute as default */
|
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
/* Port-A front headphon path */
|
|
|
|
{0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
|
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_capture_init_verbs[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
/* mute analog mix */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
|
|
|
|
/* select ADCs - front-mic */
|
|
|
|
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_spdif_init_verbs[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
/* SPDIF out sel */
|
|
|
|
{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
|
|
|
|
{0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
|
|
|
|
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
2007-07-25 00:04:05 +08:00
|
|
|
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
2005-11-17 22:31:34 +08:00
|
|
|
/* SPDIF out pin */
|
|
|
|
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
|
|
|
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
|
2010-01-21 21:54:38 +08:00
|
|
|
/* unmute SPDIF input pin */
|
|
|
|
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2008-04-16 00:46:42 +08:00
|
|
|
/* AD1989 has no ADC -> SPDIF route */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1989_spdif_init_verbs[] = {
|
2008-09-14 07:55:00 +08:00
|
|
|
/* SPDIF-1 out pin */
|
|
|
|
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
2008-04-16 00:46:42 +08:00
|
|
|
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
2008-09-14 07:55:00 +08:00
|
|
|
/* SPDIF-2/HDMI out pin */
|
|
|
|
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
|
|
|
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
2008-04-16 00:46:42 +08:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2005-11-17 22:31:34 +08:00
|
|
|
/*
|
|
|
|
* verbs for 3stack (+dig)
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_3stack_ch2_init[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
/* set port-C to line-in */
|
|
|
|
{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
|
|
|
|
{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
|
|
|
|
/* set port-E to mic-in */
|
|
|
|
{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
|
|
|
|
{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_3stack_ch6_init[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
/* set port-C to surround out */
|
|
|
|
{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
2005-11-24 23:06:23 +08:00
|
|
|
{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
|
2005-11-17 22:31:34 +08:00
|
|
|
/* set port-E to CLFE out */
|
|
|
|
{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
2005-11-24 23:06:23 +08:00
|
|
|
{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
|
2005-11-17 22:31:34 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_channel_mode ad1988_3stack_modes[2] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
{ 2, ad1988_3stack_ch2_init },
|
|
|
|
{ 6, ad1988_3stack_ch6_init },
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_3stack_init_verbs[] = {
|
2005-11-21 23:36:15 +08:00
|
|
|
/* Front, Surround, CLFE, side DAC; unmute as default */
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
2005-11-17 22:31:34 +08:00
|
|
|
/* Port-A front headphon path */
|
2011-08-31 10:30:59 +08:00
|
|
|
{0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
|
2005-11-17 22:31:34 +08:00
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
/* Port-D line-out path */
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
/* Mono out path */
|
|
|
|
{0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
|
|
|
|
/* Port-B front mic-in path */
|
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
2005-11-24 23:06:23 +08:00
|
|
|
/* Port-C line-in/surround path - 6ch mode as default */
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
2005-11-17 22:31:34 +08:00
|
|
|
{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
2005-11-24 23:06:23 +08:00
|
|
|
{0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
|
2005-11-17 22:31:34 +08:00
|
|
|
{0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
|
2005-11-24 23:06:23 +08:00
|
|
|
/* Port-E mic-in/CLFE path - 6ch mode as default */
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
2005-11-17 22:31:34 +08:00
|
|
|
{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
2005-11-24 23:17:20 +08:00
|
|
|
{0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
|
2005-11-17 22:31:34 +08:00
|
|
|
{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* mute analog mix */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
|
|
|
|
/* select ADCs - front-mic */
|
|
|
|
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
|
2008-08-12 00:08:54 +08:00
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
|
2005-11-17 22:31:34 +08:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* verbs for laptop mode (+dig)
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_laptop_hp_on[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
/* unmute port-A and mute port-D */
|
|
|
|
{ 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
|
|
|
|
{ 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
|
|
|
|
{ } /* end */
|
|
|
|
};
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_laptop_hp_off[] = {
|
2005-11-17 22:31:34 +08:00
|
|
|
/* mute port-A and unmute port-D */
|
|
|
|
{ 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
|
|
|
|
{ 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define AD1988_HP_EVENT 0x01
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1988_laptop_init_verbs[] = {
|
2005-11-21 23:36:15 +08:00
|
|
|
/* Front, Surround, CLFE, side DAC; unmute as default */
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
2005-11-17 22:31:34 +08:00
|
|
|
/* Port-A front headphon path */
|
2011-08-31 10:30:59 +08:00
|
|
|
{0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
|
2005-11-17 22:31:34 +08:00
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
/* unsolicited event for pin-sense */
|
|
|
|
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
|
|
|
|
/* Port-D line-out path + EAPD */
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
|
|
|
|
/* Mono out path */
|
|
|
|
{0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
|
|
|
|
/* Port-B mic-in path */
|
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
/* Port-C docking station - try to output */
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
{0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
/* mute analog mix */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
|
|
|
|
/* select ADCs - mic */
|
|
|
|
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
|
2008-08-12 00:08:54 +08:00
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
|
2005-11-17 22:31:34 +08:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
|
|
|
|
{
|
|
|
|
if ((res >> 26) != AD1988_HP_EVENT)
|
|
|
|
return;
|
2009-11-18 15:00:14 +08:00
|
|
|
if (snd_hda_jack_detect(codec, 0x11))
|
2005-11-17 22:31:34 +08:00
|
|
|
snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
|
|
|
|
else
|
|
|
|
snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
|
|
|
|
}
|
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_amp_list ad1988_loopbacks[] = {
|
2007-08-10 23:21:45 +08:00
|
|
|
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 1 }, /* Line */
|
|
|
|
{ 0x20, HDA_INPUT, 4 }, /* Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 6 }, /* CD */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
#endif
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2005-11-17 22:31:34 +08:00
|
|
|
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
static const char * const texts[] = {
|
|
|
|
"PCM", "ADC1", "ADC2", "ADC3",
|
|
|
|
};
|
|
|
|
int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
|
|
|
|
if (num_conns > 4)
|
|
|
|
num_conns = 4;
|
|
|
|
return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
|
|
|
|
ucontrol->value.enumerated.item[0] = spec->cur_smux;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
unsigned int val = ucontrol->value.enumerated.item[0];
|
|
|
|
struct nid_path *path;
|
|
|
|
int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
|
|
|
|
|
|
|
|
if (val >= num_conns)
|
|
|
|
return -EINVAL;
|
|
|
|
if (spec->cur_smux == val)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
mutex_lock(&codec->control_mutex);
|
|
|
|
codec->cached_write = 1;
|
|
|
|
path = snd_hda_get_path_from_idx(codec,
|
|
|
|
spec->smux_paths[spec->cur_smux]);
|
|
|
|
if (path)
|
|
|
|
snd_hda_activate_path(codec, path, false, true);
|
|
|
|
path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
|
|
|
|
if (path)
|
|
|
|
snd_hda_activate_path(codec, path, true, true);
|
|
|
|
spec->cur_smux = val;
|
|
|
|
codec->cached_write = 0;
|
|
|
|
mutex_unlock(&codec->control_mutex);
|
|
|
|
snd_hda_codec_flush_cache(codec); /* flush the updates */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "IEC958 Playback Source",
|
|
|
|
.info = ad1988_auto_smux_enum_info,
|
|
|
|
.get = ad1988_auto_smux_enum_get,
|
|
|
|
.put = ad1988_auto_smux_enum_put,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ad1988_auto_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
err = snd_hda_gen_init(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
if (!spec->gen.autocfg.dig_outs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
struct nid_path *path;
|
|
|
|
path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
|
|
|
|
if (path)
|
|
|
|
snd_hda_activate_path(codec, path, path->active, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
int i, num_conns;
|
|
|
|
/* we create four static faked paths, since AD codecs have odd
|
|
|
|
* widget connections regarding the SPDIF out source
|
|
|
|
*/
|
|
|
|
static struct nid_path fake_paths[4] = {
|
|
|
|
{
|
|
|
|
.depth = 3,
|
|
|
|
.path = { 0x02, 0x1d, 0x1b },
|
|
|
|
.idx = { 0, 0, 0 },
|
|
|
|
.multi = { 0, 0, 0 },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.depth = 4,
|
|
|
|
.path = { 0x08, 0x0b, 0x1d, 0x1b },
|
|
|
|
.idx = { 0, 0, 1, 0 },
|
|
|
|
.multi = { 0, 1, 0, 0 },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.depth = 4,
|
|
|
|
.path = { 0x09, 0x0b, 0x1d, 0x1b },
|
|
|
|
.idx = { 0, 1, 1, 0 },
|
|
|
|
.multi = { 0, 1, 0, 0 },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.depth = 4,
|
|
|
|
.path = { 0x0f, 0x0b, 0x1d, 0x1b },
|
|
|
|
.idx = { 0, 2, 1, 0 },
|
|
|
|
.multi = { 0, 1, 0, 0 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/* SPDIF source mux appears to be present only on AD1988A */
|
|
|
|
if (!spec->gen.autocfg.dig_outs ||
|
|
|
|
get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
|
|
|
|
if (num_conns != 3 && num_conns != 4)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < num_conns; i++) {
|
|
|
|
struct nid_path *path = snd_array_new(&spec->gen.paths);
|
|
|
|
if (!path)
|
|
|
|
return -ENOMEM;
|
|
|
|
*path = fake_paths[i];
|
|
|
|
if (!i)
|
|
|
|
path->active = 1;
|
|
|
|
spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
codec->patch_ops.init = ad1988_auto_init;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-24 23:06:23 +08:00
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int ad1988_parse_auto_config(struct hda_codec *codec)
|
|
|
|
{
|
2013-01-22 23:45:58 +08:00
|
|
|
struct ad198x_spec *spec;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
int err;
|
2005-11-24 23:06:23 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
|
|
|
|
2013-01-21 23:40:16 +08:00
|
|
|
spec->gen.mixer_nid = 0x20;
|
2012-12-21 22:17:06 +08:00
|
|
|
spec->beep_dev_nid = 0x10;
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad198x_parse_auto_config(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad1988_add_spdif_mux_ctl(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
return 0;
|
2013-01-22 23:45:58 +08:00
|
|
|
|
|
|
|
error:
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
2005-11-24 23:06:23 +08:00
|
|
|
}
|
|
|
|
|
2005-11-17 22:31:34 +08:00
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2011-01-17 18:29:34 +08:00
|
|
|
static const char * const ad1988_models[AD1988_MODEL_LAST] = {
|
2006-11-25 00:07:44 +08:00
|
|
|
[AD1988_6STACK] = "6stack",
|
|
|
|
[AD1988_6STACK_DIG] = "6stack-dig",
|
|
|
|
[AD1988_3STACK] = "3stack",
|
|
|
|
[AD1988_3STACK_DIG] = "3stack-dig",
|
|
|
|
[AD1988_LAPTOP] = "laptop",
|
|
|
|
[AD1988_LAPTOP_DIG] = "laptop-dig",
|
|
|
|
[AD1988_AUTO] = "auto",
|
2005-11-17 22:31:34 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
|
2007-03-13 05:20:51 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
|
2007-12-18 00:14:18 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
|
2008-05-21 22:57:20 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
|
2011-05-25 05:16:15 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
|
2008-09-14 07:55:01 +08:00
|
|
|
SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
|
2007-03-12 18:36:00 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2005-11-17 22:31:34 +08:00
|
|
|
static int patch_ad1988(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec;
|
2009-02-07 00:22:05 +08:00
|
|
|
int err, board_config;
|
2005-11-17 22:31:34 +08:00
|
|
|
|
2006-11-25 00:07:44 +08:00
|
|
|
board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
|
2007-03-12 18:36:00 +08:00
|
|
|
ad1988_models, ad1988_cfg_tbl);
|
2006-11-25 00:07:44 +08:00
|
|
|
if (board_config < 0) {
|
2009-07-28 22:01:20 +08:00
|
|
|
printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
|
|
|
|
codec->chip_name);
|
2005-11-24 23:06:23 +08:00
|
|
|
board_config = AD1988_AUTO;
|
|
|
|
}
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
if (board_config == AD1988_AUTO)
|
|
|
|
return ad1988_parse_auto_config(codec);
|
|
|
|
|
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2005-11-17 22:31:34 +08:00
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
if (is_rev2(codec))
|
|
|
|
snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
|
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
err = snd_hda_attach_beep_device(codec, 0x10);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
|
|
|
|
2011-08-31 10:30:59 +08:00
|
|
|
if (!spec->multiout.hp_nid)
|
2011-09-23 19:03:25 +08:00
|
|
|
spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
|
2005-11-17 22:31:34 +08:00
|
|
|
switch (board_config) {
|
|
|
|
case AD1988_6STACK:
|
|
|
|
case AD1988_6STACK_DIG:
|
|
|
|
spec->multiout.max_channels = 8;
|
|
|
|
spec->multiout.num_dacs = 4;
|
2006-07-03 21:58:16 +08:00
|
|
|
if (is_rev2(codec))
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
|
|
|
|
else
|
|
|
|
spec->multiout.dac_nids = ad1988_6stack_dac_nids;
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->input_mux = &ad1988_6stack_capture_source;
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->num_mixers = 2;
|
2006-07-03 21:58:16 +08:00
|
|
|
if (is_rev2(codec))
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->mixers[0] = ad1988_6stack_mixers1_rev2;
|
|
|
|
else
|
|
|
|
spec->mixers[0] = ad1988_6stack_mixers1;
|
2011-02-08 19:58:25 +08:00
|
|
|
spec->mixers[1] = ad1988_6stack_mixers2;
|
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1988_6stack_init_verbs;
|
2011-09-23 19:03:25 +08:00
|
|
|
if (board_config == AD1988_6STACK_DIG) {
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
|
|
|
|
spec->dig_in_nid = AD1988_SPDIF_IN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AD1988_3STACK:
|
|
|
|
case AD1988_3STACK_DIG:
|
|
|
|
spec->multiout.max_channels = 6;
|
|
|
|
spec->multiout.num_dacs = 3;
|
2006-07-03 21:58:16 +08:00
|
|
|
if (is_rev2(codec))
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
|
|
|
|
else
|
|
|
|
spec->multiout.dac_nids = ad1988_3stack_dac_nids;
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->input_mux = &ad1988_6stack_capture_source;
|
|
|
|
spec->channel_mode = ad1988_3stack_modes;
|
|
|
|
spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->num_mixers = 2;
|
2006-07-03 21:58:16 +08:00
|
|
|
if (is_rev2(codec))
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->mixers[0] = ad1988_3stack_mixers1_rev2;
|
|
|
|
else
|
|
|
|
spec->mixers[0] = ad1988_3stack_mixers1;
|
|
|
|
spec->mixers[1] = ad1988_3stack_mixers2;
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1988_3stack_init_verbs;
|
|
|
|
if (board_config == AD1988_3STACK_DIG)
|
|
|
|
spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
|
|
|
|
break;
|
|
|
|
case AD1988_LAPTOP:
|
|
|
|
case AD1988_LAPTOP_DIG:
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->multiout.dac_nids = ad1988_3stack_dac_nids;
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->input_mux = &ad1988_laptop_capture_source;
|
|
|
|
spec->num_mixers = 1;
|
|
|
|
spec->mixers[0] = ad1988_laptop_mixers;
|
2012-12-21 22:17:06 +08:00
|
|
|
codec->inv_eapd = 1; /* inverted EAPD */
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1988_laptop_init_verbs;
|
|
|
|
if (board_config == AD1988_LAPTOP_DIG)
|
|
|
|
spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-11-24 23:06:23 +08:00
|
|
|
spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
|
|
|
|
spec->adc_nids = ad1988_adc_nids;
|
|
|
|
spec->capsrc_nids = ad1988_capsrc_nids;
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
|
|
|
|
if (spec->multiout.dig_out_nid) {
|
2008-04-16 00:46:42 +08:00
|
|
|
if (codec->vendor_id >= 0x11d4989a) {
|
|
|
|
spec->mixers[spec->num_mixers++] =
|
|
|
|
ad1989_spdif_out_mixers;
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] =
|
|
|
|
ad1989_spdif_init_verbs;
|
2008-09-14 07:54:58 +08:00
|
|
|
codec->slave_dig_outs = ad1989b_slave_dig_outs;
|
2008-04-16 00:46:42 +08:00
|
|
|
} else {
|
|
|
|
spec->mixers[spec->num_mixers++] =
|
|
|
|
ad1988_spdif_out_mixers;
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] =
|
|
|
|
ad1988_spdif_init_verbs;
|
|
|
|
}
|
2005-11-17 22:31:34 +08:00
|
|
|
}
|
2010-01-21 21:54:38 +08:00
|
|
|
if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
|
2005-11-17 22:31:34 +08:00
|
|
|
spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
|
2010-01-21 21:54:38 +08:00
|
|
|
spec->init_verbs[spec->num_init_verbs++] =
|
|
|
|
ad1988_spdif_in_init_verbs;
|
|
|
|
}
|
2005-11-17 22:31:34 +08:00
|
|
|
|
|
|
|
codec->patch_ops = ad198x_patch_ops;
|
|
|
|
switch (board_config) {
|
|
|
|
case AD1988_LAPTOP:
|
|
|
|
case AD1988_LAPTOP_DIG:
|
|
|
|
codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
|
|
|
|
break;
|
|
|
|
}
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
spec->loopback.amplist = ad1988_loopbacks;
|
|
|
|
#endif
|
2008-01-10 23:53:55 +08:00
|
|
|
spec->vmaster_nid = 0x04;
|
2005-11-17 22:31:34 +08:00
|
|
|
|
2009-12-26 05:49:01 +08:00
|
|
|
codec->no_trigger_sense = 1;
|
2010-10-25 16:37:11 +08:00
|
|
|
codec->no_sticky_stream = 1;
|
2009-12-26 05:49:01 +08:00
|
|
|
|
2005-11-17 22:31:34 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1988 ad1988_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2005-11-17 22:31:34 +08:00
|
|
|
|
|
|
|
|
2007-05-19 00:21:41 +08:00
|
|
|
/*
|
|
|
|
* AD1884 / AD1984
|
|
|
|
*
|
|
|
|
* port-B - front line/mic-in
|
|
|
|
* port-E - aux in/out
|
|
|
|
* port-F - aux in/out
|
|
|
|
* port-C - rear line/mic-in
|
|
|
|
* port-D - rear line/hp-out
|
|
|
|
* port-A - front line/hp-out
|
|
|
|
*
|
|
|
|
* AD1984 = AD1884 + two digital mic-ins
|
|
|
|
*
|
|
|
|
* FIXME:
|
|
|
|
* For simplicity, we share the single DAC for both HP and line-outs
|
|
|
|
* right now. The inidividual playbacks could be easily implemented,
|
|
|
|
* but no build-up framework is given, so far.
|
|
|
|
*/
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1884_dac_nids[1] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
0x04,
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1884_adc_nids[2] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
0x08, 0x09,
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1884_capsrc_nids[2] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
0x0c, 0x0d,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define AD1884_SPDIF_OUT 0x02
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1884_capture_source = {
|
2007-05-19 00:21:41 +08:00
|
|
|
.num_items = 4,
|
|
|
|
.items = {
|
|
|
|
{ "Front Mic", 0x0 },
|
|
|
|
{ "Mic", 0x1 },
|
|
|
|
{ "CD", 0x2 },
|
|
|
|
{ "Mix", 0x3 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1884_base_mixers[] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
|
|
|
/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
|
2007-05-19 00:21:41 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
/* The multiple "Capture Source" controls confuse alsamixer
|
|
|
|
* So call somewhat different..
|
|
|
|
*/
|
|
|
|
/* .name = "Capture Source", */
|
|
|
|
.name = "Input Source",
|
|
|
|
.count = 2,
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
/* SPDIF controls */
|
|
|
|
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
|
|
|
/* identical with ad1983 */
|
|
|
|
.info = ad1983_spdif_route_info,
|
|
|
|
.get = ad1983_spdif_route_get,
|
|
|
|
.put = ad1983_spdif_route_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
|
2007-06-05 18:13:34 +08:00
|
|
|
HDA_INPUT),
|
2007-05-19 00:21:41 +08:00
|
|
|
HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
|
2007-06-05 18:13:34 +08:00
|
|
|
HDA_INPUT),
|
2007-05-19 00:21:41 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialization verbs
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1884_init_verbs[] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
/* DACs; mute as default */
|
2007-06-05 00:32:23 +08:00
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
2007-05-19 00:21:41 +08:00
|
|
|
/* Port-A (HP) mixer */
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-A pin */
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* HP selector - select DAC2 */
|
|
|
|
{0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
/* Port-D (Line-out) mixer */
|
|
|
|
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-D pin */
|
|
|
|
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Mono-out mixer */
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Mono-out pin */
|
|
|
|
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Mono selector */
|
|
|
|
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
/* Port-B (front mic) pin */
|
|
|
|
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
2009-01-23 19:37:09 +08:00
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
2007-05-19 00:21:41 +08:00
|
|
|
/* Port-C (rear mic) pin */
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
2009-01-23 19:37:09 +08:00
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
2007-05-19 00:21:41 +08:00
|
|
|
/* Analog mixer; mute as default */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
|
|
|
|
/* SPDIF output selector */
|
|
|
|
{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
|
|
|
|
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_amp_list ad1884_loopbacks[] = {
|
2007-08-10 23:21:45 +08:00
|
|
|
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 1 }, /* Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 2 }, /* CD */
|
|
|
|
{ 0x20, HDA_INPUT, 4 }, /* Docking */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2011-01-17 18:29:34 +08:00
|
|
|
static const char * const ad1884_slave_vols[] = {
|
2012-02-03 21:28:01 +08:00
|
|
|
"PCM", "Mic", "Mono", "Front Mic", "Mic", "CD",
|
2012-09-19 18:19:47 +08:00
|
|
|
"Internal Mic", "Dock Mic", /* "Beep", */ "IEC958",
|
2008-01-10 23:53:55 +08:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
enum {
|
|
|
|
AD1884_AUTO,
|
|
|
|
AD1884_BASIC,
|
|
|
|
AD1884_MODELS
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char * const ad1884_models[AD1884_MODELS] = {
|
|
|
|
[AD1884_AUTO] = "auto",
|
|
|
|
[AD1884_BASIC] = "basic",
|
|
|
|
};
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2012-12-21 22:17:06 +08:00
|
|
|
|
|
|
|
static int ad1884_parse_auto_config(struct hda_codec *codec)
|
|
|
|
{
|
2013-01-22 23:45:58 +08:00
|
|
|
struct ad198x_spec *spec;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
int err;
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
|
|
|
|
2013-01-21 23:40:16 +08:00
|
|
|
spec->gen.mixer_nid = 0x20;
|
2012-12-21 22:17:06 +08:00
|
|
|
spec->beep_dev_nid = 0x10;
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad198x_parse_auto_config(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad1983_add_spdif_mux_ctl(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
return 0;
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
error:
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
2012-12-21 22:17:06 +08:00
|
|
|
}
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2012-12-21 22:17:06 +08:00
|
|
|
static int patch_ad1884_basic(struct hda_codec *codec)
|
2007-05-19 00:21:41 +08:00
|
|
|
{
|
|
|
|
struct ad198x_spec *spec;
|
2009-02-07 00:22:05 +08:00
|
|
|
int err;
|
2007-05-19 00:21:41 +08:00
|
|
|
|
2012-05-09 20:35:27 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2007-05-19 00:21:41 +08:00
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
err = snd_hda_attach_beep_device(codec, 0x10);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
|
|
|
|
2007-05-19 00:21:41 +08:00
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
|
|
|
|
spec->multiout.dac_nids = ad1884_dac_nids;
|
|
|
|
spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
|
|
|
|
spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
|
|
|
|
spec->adc_nids = ad1884_adc_nids;
|
|
|
|
spec->capsrc_nids = ad1884_capsrc_nids;
|
|
|
|
spec->input_mux = &ad1884_capture_source;
|
|
|
|
spec->num_mixers = 1;
|
|
|
|
spec->mixers[0] = ad1884_base_mixers;
|
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1884_init_verbs;
|
|
|
|
spec->spdif_route = 0;
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
spec->loopback.amplist = ad1884_loopbacks;
|
|
|
|
#endif
|
2008-01-10 23:53:55 +08:00
|
|
|
spec->vmaster_nid = 0x04;
|
|
|
|
/* we need to cover all playback volumes */
|
|
|
|
spec->slave_vols = ad1884_slave_vols;
|
2012-03-10 00:51:10 +08:00
|
|
|
/* slaves may contain input volumes, so we can't raise to 0dB blindly */
|
|
|
|
spec->avoid_init_slave_vol = 1;
|
2007-05-19 00:21:41 +08:00
|
|
|
|
|
|
|
codec->patch_ops = ad198x_patch_ops;
|
|
|
|
|
2009-12-26 05:49:01 +08:00
|
|
|
codec->no_trigger_sense = 1;
|
2010-10-25 16:37:11 +08:00
|
|
|
codec->no_sticky_stream = 1;
|
2009-12-26 05:49:01 +08:00
|
|
|
|
2007-05-19 00:21:41 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
static int patch_ad1884(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
int board_config;
|
|
|
|
|
|
|
|
board_config = snd_hda_check_board_config(codec, AD1884_MODELS,
|
|
|
|
ad1884_models, NULL);
|
|
|
|
if (board_config == AD1884_AUTO)
|
2013-01-22 23:45:58 +08:00
|
|
|
return ad1884_parse_auto_config(codec);
|
2012-12-21 22:17:06 +08:00
|
|
|
else
|
|
|
|
return patch_ad1884_basic(codec);
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1884 ad1884_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
|
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2007-05-19 00:21:41 +08:00
|
|
|
/*
|
|
|
|
* Lenovo Thinkpad T61/X61
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1984_thinkpad_capture_source = {
|
2008-02-26 18:56:35 +08:00
|
|
|
.num_items = 4,
|
2007-05-19 00:21:41 +08:00
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
|
|
|
{ "Internal Mic", 0x1 },
|
|
|
|
{ "Mix", 0x3 },
|
2012-09-19 18:19:47 +08:00
|
|
|
{ "Dock Mic", 0x4 },
|
2007-05-19 00:21:41 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2008-01-29 22:02:50 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Dell Precision T3400
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
|
2008-01-29 22:02:50 +08:00
|
|
|
.num_items = 3,
|
|
|
|
.items = {
|
|
|
|
{ "Front Mic", 0x0 },
|
|
|
|
{ "Line-In", 0x1 },
|
|
|
|
{ "Mix", 0x3 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
|
|
|
/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
2010-03-26 17:33:18 +08:00
|
|
|
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
|
2012-09-19 18:19:47 +08:00
|
|
|
HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
|
2007-05-19 00:21:41 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
/* The multiple "Capture Source" controls confuse alsamixer
|
|
|
|
* So call somewhat different..
|
|
|
|
*/
|
|
|
|
/* .name = "Capture Source", */
|
|
|
|
.name = "Input Source",
|
|
|
|
.count = 2,
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
2008-01-07 19:22:18 +08:00
|
|
|
/* SPDIF controls */
|
|
|
|
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
|
|
|
/* identical with ad1983 */
|
|
|
|
.info = ad1983_spdif_route_info,
|
|
|
|
.get = ad1983_spdif_route_get,
|
|
|
|
.put = ad1983_spdif_route_put,
|
|
|
|
},
|
2007-05-19 00:21:41 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* additional verbs */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
/* Port-E (docking station mic) pin */
|
|
|
|
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* docking mic boost */
|
2009-01-23 21:18:11 +08:00
|
|
|
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
2010-03-26 17:33:18 +08:00
|
|
|
/* Analog PC Beeper - allow firmware/ACPI beeps */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
|
2007-05-19 00:21:41 +08:00
|
|
|
/* Analog mixer - docking mic; mute as default */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
2007-06-08 18:25:25 +08:00
|
|
|
/* enable EAPD bit */
|
|
|
|
{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
|
2007-05-19 00:21:41 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2008-01-29 22:02:50 +08:00
|
|
|
/*
|
|
|
|
* Dell Precision T3400
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
|
2008-01-29 22:02:50 +08:00
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
|
2008-01-29 22:02:50 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
/* The multiple "Capture Source" controls confuse alsamixer
|
|
|
|
* So call somewhat different..
|
|
|
|
*/
|
|
|
|
/* .name = "Capture Source", */
|
|
|
|
.name = "Input Source",
|
|
|
|
.count = 2,
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2007-05-19 00:21:41 +08:00
|
|
|
/* Digial MIC ADC NID 0x05 + 0x06 */
|
|
|
|
static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
|
|
|
unsigned int stream_tag,
|
|
|
|
unsigned int format,
|
|
|
|
struct snd_pcm_substream *substream)
|
|
|
|
{
|
|
|
|
snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
|
|
|
|
stream_tag, 0, format);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
|
|
|
|
struct hda_codec *codec,
|
|
|
|
struct snd_pcm_substream *substream)
|
|
|
|
{
|
2008-03-18 16:57:50 +08:00
|
|
|
snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
|
2007-05-19 00:21:41 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
|
2007-05-19 00:21:41 +08:00
|
|
|
.substreams = 2,
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.nid = 0x05,
|
|
|
|
.ops = {
|
|
|
|
.prepare = ad1984_pcm_dmic_prepare,
|
|
|
|
.cleanup = ad1984_pcm_dmic_cleanup
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ad1984_build_pcms(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec = codec->spec;
|
|
|
|
struct hda_pcm *info;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = ad198x_build_pcms(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
info = spec->pcm_rec + codec->num_pcms;
|
|
|
|
codec->num_pcms++;
|
|
|
|
info->name = "AD1984 Digital Mic";
|
|
|
|
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* models */
|
|
|
|
enum {
|
2012-12-21 22:17:06 +08:00
|
|
|
AD1984_AUTO,
|
2007-05-19 00:21:41 +08:00
|
|
|
AD1984_BASIC,
|
|
|
|
AD1984_THINKPAD,
|
2008-01-29 22:02:50 +08:00
|
|
|
AD1984_DELL_DESKTOP,
|
2007-05-19 00:21:41 +08:00
|
|
|
AD1984_MODELS
|
|
|
|
};
|
|
|
|
|
2011-01-17 18:29:34 +08:00
|
|
|
static const char * const ad1984_models[AD1984_MODELS] = {
|
2012-12-21 22:17:06 +08:00
|
|
|
[AD1984_AUTO] = "auto",
|
2007-05-19 00:21:41 +08:00
|
|
|
[AD1984_BASIC] = "basic",
|
|
|
|
[AD1984_THINKPAD] = "thinkpad",
|
2008-01-29 22:02:50 +08:00
|
|
|
[AD1984_DELL_DESKTOP] = "dell_desktop",
|
2007-05-19 00:21:41 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
|
2007-05-19 00:21:41 +08:00
|
|
|
/* Lenovo Thinkpad T61/X61 */
|
2009-02-10 00:14:52 +08:00
|
|
|
SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
|
2008-01-29 22:02:50 +08:00
|
|
|
SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
|
2010-09-21 15:05:46 +08:00
|
|
|
SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
|
2007-05-19 00:21:41 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static int patch_ad1984(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec;
|
|
|
|
int board_config, err;
|
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
|
|
|
|
ad1984_models, ad1984_cfg_tbl);
|
|
|
|
if (board_config == AD1984_AUTO)
|
2013-01-22 23:45:58 +08:00
|
|
|
return ad1884_parse_auto_config(codec);
|
2012-12-21 22:17:06 +08:00
|
|
|
|
|
|
|
err = patch_ad1884_basic(codec);
|
2007-05-19 00:21:41 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2007-05-19 00:21:41 +08:00
|
|
|
switch (board_config) {
|
|
|
|
case AD1984_BASIC:
|
|
|
|
/* additional digital mics */
|
|
|
|
spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
|
|
|
|
codec->patch_ops.build_pcms = ad1984_build_pcms;
|
|
|
|
break;
|
|
|
|
case AD1984_THINKPAD:
|
2010-08-03 14:46:44 +08:00
|
|
|
if (codec->subsystem_id == 0x17aa20fb) {
|
|
|
|
/* Thinpad X300 does not have the ability to do SPDIF,
|
|
|
|
or attach to docking station to use SPDIF */
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
} else
|
|
|
|
spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
|
2007-05-19 00:21:41 +08:00
|
|
|
spec->input_mux = &ad1984_thinkpad_capture_source;
|
|
|
|
spec->mixers[0] = ad1984_thinkpad_mixers;
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
|
2010-03-26 17:33:18 +08:00
|
|
|
spec->analog_beep = 1;
|
2007-05-19 00:21:41 +08:00
|
|
|
break;
|
2008-01-29 22:02:50 +08:00
|
|
|
case AD1984_DELL_DESKTOP:
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
spec->input_mux = &ad1984_dell_desktop_capture_source;
|
|
|
|
spec->mixers[0] = ad1984_dell_desktop_mixers;
|
|
|
|
break;
|
2007-05-19 00:21:41 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1984 ad1884_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2007-05-19 00:21:41 +08:00
|
|
|
|
|
|
|
|
2008-02-16 16:43:56 +08:00
|
|
|
/*
|
|
|
|
* AD1883 / AD1884A / AD1984A / AD1984B
|
|
|
|
*
|
|
|
|
* port-B (0x14) - front mic-in
|
|
|
|
* port-E (0x1c) - rear mic-in
|
|
|
|
* port-F (0x16) - CD / ext out
|
|
|
|
* port-C (0x15) - rear line-in
|
|
|
|
* port-D (0x12) - rear line-out
|
|
|
|
* port-A (0x11) - front hp-out
|
|
|
|
*
|
|
|
|
* AD1984A = AD1884A + digital-mic
|
|
|
|
* AD1883 = equivalent with AD1984A
|
|
|
|
* AD1984B = AD1984A + extra SPDIF-out
|
|
|
|
*
|
|
|
|
* FIXME:
|
|
|
|
* We share the single DAC for both HP and line-outs (see AD1884/1984).
|
|
|
|
*/
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1884a_dac_nids[1] = {
|
2008-02-16 16:43:56 +08:00
|
|
|
0x03,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define ad1884a_adc_nids ad1884_adc_nids
|
|
|
|
#define ad1884a_capsrc_nids ad1884_capsrc_nids
|
|
|
|
|
|
|
|
#define AD1884A_SPDIF_OUT 0x02
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1884a_capture_source = {
|
2008-02-16 16:43:56 +08:00
|
|
|
.num_items = 5,
|
|
|
|
.items = {
|
|
|
|
{ "Front Mic", 0x0 },
|
|
|
|
{ "Mic", 0x4 },
|
|
|
|
{ "Line", 0x1 },
|
|
|
|
{ "CD", 0x2 },
|
|
|
|
{ "Mix", 0x3 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
|
2008-02-16 16:43:56 +08:00
|
|
|
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
|
2008-02-16 16:43:56 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
/* The multiple "Capture Source" controls confuse alsamixer
|
|
|
|
* So call somewhat different..
|
|
|
|
*/
|
|
|
|
/* .name = "Capture Source", */
|
|
|
|
.name = "Input Source",
|
|
|
|
.count = 2,
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
/* SPDIF controls */
|
|
|
|
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
|
|
|
/* identical with ad1983 */
|
|
|
|
.info = ad1983_spdif_route_info,
|
|
|
|
.get = ad1983_spdif_route_get,
|
|
|
|
.put = ad1983_spdif_route_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialization verbs
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1884a_init_verbs[] = {
|
2008-02-16 16:43:56 +08:00
|
|
|
/* DACs; unmute as default */
|
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
|
|
|
/* Port-A (HP) mixer - route only from analog mixer */
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-A pin */
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Port-D (Line-out) mixer - route only from analog mixer */
|
|
|
|
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-D pin */
|
|
|
|
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Mono-out mixer - route only from analog mixer */
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Mono-out pin */
|
|
|
|
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Port-B (front mic) pin */
|
|
|
|
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
2009-01-23 19:37:09 +08:00
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
2008-02-16 16:43:56 +08:00
|
|
|
/* Port-C (rear line-in) pin */
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
2009-01-23 19:37:09 +08:00
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
2008-02-16 16:43:56 +08:00
|
|
|
/* Port-E (rear mic) pin */
|
|
|
|
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
|
|
|
|
/* Port-F (CD) pin */
|
|
|
|
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
|
|
|
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Analog mixer; mute as default */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* capture sources */
|
|
|
|
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* SPDIF output amp */
|
|
|
|
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_amp_list ad1884a_loopbacks[] = {
|
2008-02-16 16:43:56 +08:00
|
|
|
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 1 }, /* Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 2 }, /* CD */
|
|
|
|
{ 0x20, HDA_INPUT, 4 }, /* Docking */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Laptop model
|
|
|
|
*
|
|
|
|
* Port A: Headphone jack
|
|
|
|
* Port B: MIC jack
|
|
|
|
* Port C: Internal MIC
|
|
|
|
* Port D: Dock Line Out (if enabled)
|
|
|
|
* Port E: Dock Line In (if enabled)
|
|
|
|
* Port F: Internal speakers
|
|
|
|
*/
|
|
|
|
|
2009-08-30 18:15:59 +08:00
|
|
|
static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
|
int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
|
|
|
int mute = (!ucontrol->value.integer.value[0] &&
|
|
|
|
!ucontrol->value.integer.value[1]);
|
|
|
|
/* toggle GPIO1 according to the mute state */
|
|
|
|
snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
|
|
|
mute ? 0x02 : 0x0);
|
|
|
|
return ret;
|
|
|
|
}
|
2008-02-16 16:43:56 +08:00
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
|
2008-02-16 16:43:56 +08:00
|
|
|
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
2009-08-30 18:15:59 +08:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Master Playback Switch",
|
2009-12-10 20:57:01 +08:00
|
|
|
.subdevice = HDA_SUBDEV_AMP_FLAG,
|
2009-08-30 18:15:59 +08:00
|
|
|
.info = snd_hda_mixer_amp_switch_info,
|
|
|
|
.get = snd_hda_mixer_amp_switch_get,
|
|
|
|
.put = ad1884a_mobile_master_sw_put,
|
|
|
|
.private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
|
|
|
|
},
|
2008-02-16 16:43:56 +08:00
|
|
|
HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
|
2008-02-16 16:43:56 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
|
2008-02-16 16:44:56 +08:00
|
|
|
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
2009-07-02 22:10:23 +08:00
|
|
|
/*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Master Playback Switch",
|
2009-12-10 20:57:01 +08:00
|
|
|
.subdevice = HDA_SUBDEV_AMP_FLAG,
|
2009-07-02 22:10:23 +08:00
|
|
|
.info = snd_hda_mixer_amp_switch_info,
|
|
|
|
.get = snd_hda_mixer_amp_switch_get,
|
|
|
|
.put = ad1884a_mobile_master_sw_put,
|
|
|
|
.private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
|
|
|
|
},
|
2008-02-16 16:44:56 +08:00
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
2008-05-30 21:32:15 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
|
2008-02-16 16:44:56 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2008-02-16 16:43:56 +08:00
|
|
|
/* mute internal speaker if HP is plugged */
|
|
|
|
static void ad1884a_hp_automute(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int present;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
present = snd_hda_jack_detect(codec, 0x11);
|
2008-02-16 16:43:56 +08:00
|
|
|
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
|
|
|
|
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
|
|
|
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
|
|
|
present ? 0x00 : 0x02);
|
|
|
|
}
|
|
|
|
|
2008-05-30 21:32:15 +08:00
|
|
|
/* switch to external mic if plugged */
|
|
|
|
static void ad1884a_hp_automic(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int present;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
present = snd_hda_jack_detect(codec, 0x14);
|
2008-05-30 21:32:15 +08:00
|
|
|
snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
|
|
|
|
present ? 0 : 1);
|
|
|
|
}
|
|
|
|
|
2008-02-16 16:43:56 +08:00
|
|
|
#define AD1884A_HP_EVENT 0x37
|
2008-05-30 21:32:15 +08:00
|
|
|
#define AD1884A_MIC_EVENT 0x36
|
2008-02-16 16:43:56 +08:00
|
|
|
|
|
|
|
/* unsolicited event for HP jack sensing */
|
|
|
|
static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
|
|
|
|
{
|
2008-05-30 21:32:15 +08:00
|
|
|
switch (res >> 26) {
|
|
|
|
case AD1884A_HP_EVENT:
|
|
|
|
ad1884a_hp_automute(codec);
|
|
|
|
break;
|
|
|
|
case AD1884A_MIC_EVENT:
|
|
|
|
ad1884a_hp_automic(codec);
|
|
|
|
break;
|
|
|
|
}
|
2008-02-16 16:43:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize jack-sensing, too */
|
|
|
|
static int ad1884a_hp_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1884a_hp_automute(codec);
|
2008-05-30 21:32:15 +08:00
|
|
|
ad1884a_hp_automic(codec);
|
2008-02-16 16:43:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-30 18:15:59 +08:00
|
|
|
/* mute internal speaker if HP or docking HP is plugged */
|
|
|
|
static void ad1884a_laptop_automute(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int present;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
present = snd_hda_jack_detect(codec, 0x11);
|
|
|
|
if (!present)
|
|
|
|
present = snd_hda_jack_detect(codec, 0x12);
|
2009-08-30 18:15:59 +08:00
|
|
|
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
|
|
|
|
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
|
|
|
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
|
|
|
present ? 0x00 : 0x02);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* switch to external mic if plugged */
|
|
|
|
static void ad1884a_laptop_automic(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int idx;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
if (snd_hda_jack_detect(codec, 0x14))
|
2009-08-30 18:15:59 +08:00
|
|
|
idx = 0;
|
2009-11-18 15:00:14 +08:00
|
|
|
else if (snd_hda_jack_detect(codec, 0x1c))
|
2009-08-30 18:15:59 +08:00
|
|
|
idx = 4;
|
|
|
|
else
|
|
|
|
idx = 1;
|
|
|
|
snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unsolicited event for HP jack sensing */
|
|
|
|
static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
|
|
|
|
unsigned int res)
|
|
|
|
{
|
|
|
|
switch (res >> 26) {
|
|
|
|
case AD1884A_HP_EVENT:
|
|
|
|
ad1884a_laptop_automute(codec);
|
|
|
|
break;
|
|
|
|
case AD1884A_MIC_EVENT:
|
|
|
|
ad1884a_laptop_automic(codec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize jack-sensing, too */
|
|
|
|
static int ad1884a_laptop_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1884a_laptop_automute(codec);
|
|
|
|
ad1884a_laptop_automic(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-16 16:43:56 +08:00
|
|
|
/* additional verbs for laptop model */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1884a_laptop_verbs[] = {
|
2008-02-16 16:43:56 +08:00
|
|
|
/* Port-A (HP) pin - always unmuted */
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
/* Port-F (int speaker) mixer - route only from analog mixer */
|
|
|
|
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
2009-08-19 16:58:59 +08:00
|
|
|
/* Port-F (int speaker) pin */
|
|
|
|
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
2008-02-16 16:43:56 +08:00
|
|
|
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
2009-08-19 16:58:59 +08:00
|
|
|
/* required for compaq 6530s/6531s speaker output */
|
|
|
|
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
2008-05-30 21:32:15 +08:00
|
|
|
/* Port-C pin - internal mic-in */
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
|
2009-09-01 15:09:26 +08:00
|
|
|
/* Port-D (docking line-out) pin - default unmuted */
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
2008-02-16 16:43:56 +08:00
|
|
|
/* analog mix */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
/* unsolicited event for pin-sense */
|
|
|
|
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
2009-08-30 18:15:59 +08:00
|
|
|
{0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
2008-05-30 21:32:15 +08:00
|
|
|
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
|
2009-08-30 18:15:59 +08:00
|
|
|
{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
|
2009-08-31 14:37:46 +08:00
|
|
|
/* allow to touch GPIO1 (for mute control) */
|
|
|
|
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
|
|
|
|
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
|
|
|
|
{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
|
2008-02-16 16:43:56 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1884a_mobile_verbs[] = {
|
2009-04-23 14:24:48 +08:00
|
|
|
/* DACs; unmute as default */
|
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
|
|
|
/* Port-A (HP) mixer - route only from analog mixer */
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-A pin */
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
/* Port-A (HP) pin - always unmuted */
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
/* Port-B (mic jack) pin */
|
|
|
|
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
|
|
|
|
/* Port-C (int mic) pin */
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
|
|
|
|
/* Port-F (int speaker) mixer - route only from analog mixer */
|
|
|
|
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-F pin */
|
|
|
|
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Analog mixer; mute as default */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* capture sources */
|
|
|
|
/* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
|
|
|
|
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* unsolicited event for pin-sense */
|
|
|
|
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
|
|
|
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
|
2009-07-02 22:10:23 +08:00
|
|
|
/* allow to touch GPIO1 (for mute control) */
|
|
|
|
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
|
|
|
|
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
|
|
|
|
{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
|
2009-04-23 14:24:48 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2008-03-18 19:13:03 +08:00
|
|
|
/*
|
|
|
|
* Thinkpad X300
|
|
|
|
* 0x11 - HP
|
|
|
|
* 0x12 - speaker
|
|
|
|
* 0x14 - mic-in
|
|
|
|
* 0x17 - built-in mic
|
|
|
|
*/
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1984a_thinkpad_verbs[] = {
|
2008-03-18 19:13:03 +08:00
|
|
|
/* HP unmute */
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
/* analog mix */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
/* turn on EAPD */
|
|
|
|
{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
|
|
|
|
/* unsolicited event for pin-sense */
|
|
|
|
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
|
|
|
/* internal mic - dmic */
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
2008-04-23 19:50:08 +08:00
|
|
|
/* set magic COEFs for dmic */
|
|
|
|
{0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
|
|
|
|
{0x01, AC_VERB_SET_PROC_COEF, 0x08},
|
2008-03-18 19:13:03 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
|
2008-03-18 19:13:03 +08:00
|
|
|
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
|
2008-03-18 19:13:03 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Capture Source",
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
|
2008-03-18 19:13:03 +08:00
|
|
|
.num_items = 3,
|
|
|
|
.items = {
|
|
|
|
{ "Mic", 0x0 },
|
|
|
|
{ "Internal Mic", 0x5 },
|
|
|
|
{ "Mix", 0x3 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/* mute internal speaker if HP is plugged */
|
|
|
|
static void ad1984a_thinkpad_automute(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int present;
|
|
|
|
|
2009-11-18 15:00:14 +08:00
|
|
|
present = snd_hda_jack_detect(codec, 0x11);
|
2008-03-18 19:13:03 +08:00
|
|
|
snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
|
|
|
|
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unsolicited event for HP jack sensing */
|
|
|
|
static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
|
|
|
|
unsigned int res)
|
|
|
|
{
|
|
|
|
if ((res >> 26) != AD1884A_HP_EVENT)
|
|
|
|
return;
|
|
|
|
ad1984a_thinkpad_automute(codec);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize jack-sensing, too */
|
|
|
|
static int ad1984a_thinkpad_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1984a_thinkpad_automute(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-07 22:19:34 +08:00
|
|
|
/*
|
|
|
|
* Precision R5500
|
|
|
|
* 0x12 - HP/line-out
|
|
|
|
* 0x13 - speaker (mono)
|
|
|
|
* 0x15 - mic-in
|
|
|
|
*/
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1984a_precision_verbs[] = {
|
2011-02-07 22:19:34 +08:00
|
|
|
/* Unmute main output path */
|
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
|
|
|
|
/* Analog mixer; mute as default */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
/* Select mic as input */
|
|
|
|
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
|
|
|
|
/* Configure as mic */
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
|
|
|
|
/* HP unmute */
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
/* turn on EAPD */
|
|
|
|
{0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
|
|
|
|
/* unsolicited event for pin-sense */
|
|
|
|
{0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
|
2011-02-07 22:19:34 +08:00
|
|
|
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* mute internal speaker if HP is plugged */
|
|
|
|
static void ad1984a_precision_automute(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
unsigned int present;
|
|
|
|
|
|
|
|
present = snd_hda_jack_detect(codec, 0x12);
|
|
|
|
snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
|
|
|
|
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* unsolicited event for HP jack sensing */
|
|
|
|
static void ad1984a_precision_unsol_event(struct hda_codec *codec,
|
|
|
|
unsigned int res)
|
|
|
|
{
|
|
|
|
if ((res >> 26) != AD1884A_HP_EVENT)
|
|
|
|
return;
|
|
|
|
ad1984a_precision_automute(codec);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize jack-sensing, too */
|
|
|
|
static int ad1984a_precision_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1984a_precision_automute(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-28 04:11:21 +08:00
|
|
|
/*
|
|
|
|
* HP Touchsmart
|
|
|
|
* port-A (0x11) - front hp-out
|
|
|
|
* port-B (0x14) - unused
|
|
|
|
* port-C (0x15) - unused
|
|
|
|
* port-D (0x12) - rear line out
|
|
|
|
* port-E (0x1c) - front mic-in
|
|
|
|
* port-F (0x16) - Internal speakers
|
|
|
|
* digital-mic (0x17) - Internal mic
|
|
|
|
*/
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1984a_touchsmart_verbs[] = {
|
2009-09-28 04:11:21 +08:00
|
|
|
/* DACs; unmute as default */
|
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
|
|
|
/* Port-A (HP) mixer - route only from analog mixer */
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-A pin */
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
/* Port-A (HP) pin - always unmuted */
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
/* Port-E (int speaker) mixer - route only from analog mixer */
|
|
|
|
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
|
|
|
|
/* Port-E pin */
|
|
|
|
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
|
|
|
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
|
|
|
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
/* Port-F (int speaker) mixer - route only from analog mixer */
|
|
|
|
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-F pin */
|
|
|
|
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Analog mixer; mute as default */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* capture sources */
|
|
|
|
/* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
|
|
|
|
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
|
|
|
|
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* unsolicited event for pin-sense */
|
|
|
|
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
|
|
|
{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
|
|
|
|
/* allow to touch GPIO1 (for mute control) */
|
|
|
|
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
|
|
|
|
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
|
|
|
|
{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
|
|
|
|
/* internal mic - dmic */
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
|
|
|
/* set magic COEFs for dmic */
|
|
|
|
{0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
|
|
|
|
{0x01, AC_VERB_SET_PROC_COEF, 0x08},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
|
2009-09-28 04:11:21 +08:00
|
|
|
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
|
|
|
/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
2009-12-10 20:57:01 +08:00
|
|
|
.subdevice = HDA_SUBDEV_AMP_FLAG,
|
2009-09-28 04:11:21 +08:00
|
|
|
.name = "Master Playback Switch",
|
|
|
|
.info = snd_hda_mixer_amp_switch_info,
|
|
|
|
.get = snd_hda_mixer_amp_switch_get,
|
|
|
|
.put = ad1884a_mobile_master_sw_put,
|
|
|
|
.private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
|
|
|
|
},
|
|
|
|
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
|
2009-09-28 04:11:21 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* switch to external mic if plugged */
|
|
|
|
static void ad1984a_touchsmart_automic(struct hda_codec *codec)
|
|
|
|
{
|
2009-11-18 15:00:14 +08:00
|
|
|
if (snd_hda_jack_detect(codec, 0x1c))
|
2009-09-28 04:11:21 +08:00
|
|
|
snd_hda_codec_write(codec, 0x0c, 0,
|
|
|
|
AC_VERB_SET_CONNECT_SEL, 0x4);
|
2009-11-18 15:00:14 +08:00
|
|
|
else
|
2009-09-28 04:11:21 +08:00
|
|
|
snd_hda_codec_write(codec, 0x0c, 0,
|
|
|
|
AC_VERB_SET_CONNECT_SEL, 0x5);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* unsolicited event for HP jack sensing */
|
|
|
|
static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
|
|
|
|
unsigned int res)
|
|
|
|
{
|
|
|
|
switch (res >> 26) {
|
|
|
|
case AD1884A_HP_EVENT:
|
|
|
|
ad1884a_hp_automute(codec);
|
|
|
|
break;
|
|
|
|
case AD1884A_MIC_EVENT:
|
|
|
|
ad1984a_touchsmart_automic(codec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize jack-sensing, too */
|
|
|
|
static int ad1984a_touchsmart_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1884a_hp_automute(codec);
|
|
|
|
ad1984a_touchsmart_automic(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-16 16:43:56 +08:00
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum {
|
2012-12-21 22:17:06 +08:00
|
|
|
AD1884A_AUTO,
|
2008-02-16 16:43:56 +08:00
|
|
|
AD1884A_DESKTOP,
|
|
|
|
AD1884A_LAPTOP,
|
2008-02-16 16:44:56 +08:00
|
|
|
AD1884A_MOBILE,
|
2008-03-18 19:13:03 +08:00
|
|
|
AD1884A_THINKPAD,
|
2009-09-28 04:11:21 +08:00
|
|
|
AD1984A_TOUCHSMART,
|
2011-02-07 22:19:34 +08:00
|
|
|
AD1984A_PRECISION,
|
2008-02-16 16:43:56 +08:00
|
|
|
AD1884A_MODELS
|
|
|
|
};
|
|
|
|
|
2011-01-17 18:29:34 +08:00
|
|
|
static const char * const ad1884a_models[AD1884A_MODELS] = {
|
2012-12-21 22:17:06 +08:00
|
|
|
[AD1884A_AUTO] = "auto",
|
2008-02-16 16:43:56 +08:00
|
|
|
[AD1884A_DESKTOP] = "desktop",
|
|
|
|
[AD1884A_LAPTOP] = "laptop",
|
2008-02-16 16:44:56 +08:00
|
|
|
[AD1884A_MOBILE] = "mobile",
|
2008-03-18 19:13:03 +08:00
|
|
|
[AD1884A_THINKPAD] = "thinkpad",
|
2009-09-28 04:11:21 +08:00
|
|
|
[AD1984A_TOUCHSMART] = "touchsmart",
|
2011-02-07 22:19:34 +08:00
|
|
|
[AD1984A_PRECISION] = "precision",
|
2008-02-16 16:44:56 +08:00
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
|
2011-02-07 22:19:34 +08:00
|
|
|
SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
|
2008-02-16 16:44:56 +08:00
|
|
|
SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
|
2009-01-07 18:41:57 +08:00
|
|
|
SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
|
2008-10-28 22:39:26 +08:00
|
|
|
SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
|
2009-02-16 22:20:41 +08:00
|
|
|
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
|
2009-07-02 00:08:01 +08:00
|
|
|
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
|
2009-02-26 01:12:13 +08:00
|
|
|
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
|
|
|
|
SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
|
2009-08-27 20:37:51 +08:00
|
|
|
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
|
2008-03-18 19:13:03 +08:00
|
|
|
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
|
2009-09-28 04:11:21 +08:00
|
|
|
SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
|
2008-02-16 16:44:56 +08:00
|
|
|
{}
|
2008-02-16 16:43:56 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int patch_ad1884a(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec;
|
2009-02-07 00:22:05 +08:00
|
|
|
int err, board_config;
|
2008-02-16 16:43:56 +08:00
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
|
|
|
|
ad1884a_models,
|
|
|
|
ad1884a_cfg_tbl);
|
|
|
|
if (board_config == AD1884_AUTO)
|
2013-01-22 23:45:58 +08:00
|
|
|
return ad1884_parse_auto_config(codec);
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2012-05-09 20:35:27 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2008-02-16 16:43:56 +08:00
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
err = snd_hda_attach_beep_device(codec, 0x10);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
|
|
|
|
2008-02-16 16:43:56 +08:00
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
|
|
|
|
spec->multiout.dac_nids = ad1884a_dac_nids;
|
|
|
|
spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
|
|
|
|
spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
|
|
|
|
spec->adc_nids = ad1884a_adc_nids;
|
|
|
|
spec->capsrc_nids = ad1884a_capsrc_nids;
|
|
|
|
spec->input_mux = &ad1884a_capture_source;
|
|
|
|
spec->num_mixers = 1;
|
|
|
|
spec->mixers[0] = ad1884a_base_mixers;
|
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1884a_init_verbs;
|
|
|
|
spec->spdif_route = 0;
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2008-02-16 16:43:56 +08:00
|
|
|
spec->loopback.amplist = ad1884a_loopbacks;
|
|
|
|
#endif
|
|
|
|
codec->patch_ops = ad198x_patch_ops;
|
|
|
|
|
|
|
|
/* override some parameters */
|
|
|
|
switch (board_config) {
|
|
|
|
case AD1884A_LAPTOP:
|
|
|
|
spec->mixers[0] = ad1884a_laptop_mixers;
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
2009-08-30 18:15:59 +08:00
|
|
|
codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1884a_laptop_init;
|
2009-04-16 20:19:19 +08:00
|
|
|
/* set the upper-limit for mixer amp to 0dB for avoiding the
|
|
|
|
* possible damage by overloading
|
|
|
|
*/
|
|
|
|
snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
|
|
|
|
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
|
|
|
|
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
|
|
|
|
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
|
|
|
|
(1 << AC_AMPCAP_MUTE_SHIFT));
|
2008-02-16 16:43:56 +08:00
|
|
|
break;
|
2008-02-16 16:44:56 +08:00
|
|
|
case AD1884A_MOBILE:
|
|
|
|
spec->mixers[0] = ad1884a_mobile_mixers;
|
2009-04-23 14:24:48 +08:00
|
|
|
spec->init_verbs[0] = ad1884a_mobile_verbs;
|
2008-02-16 16:44:56 +08:00
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1884a_hp_init;
|
2009-02-23 18:33:34 +08:00
|
|
|
/* set the upper-limit for mixer amp to 0dB for avoiding the
|
|
|
|
* possible damage by overloading
|
|
|
|
*/
|
|
|
|
snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
|
|
|
|
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
|
|
|
|
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
|
|
|
|
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
|
|
|
|
(1 << AC_AMPCAP_MUTE_SHIFT));
|
2008-02-16 16:44:56 +08:00
|
|
|
break;
|
2008-03-18 19:13:03 +08:00
|
|
|
case AD1884A_THINKPAD:
|
|
|
|
spec->mixers[0] = ad1984a_thinkpad_mixers;
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] =
|
|
|
|
ad1984a_thinkpad_verbs;
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
spec->input_mux = &ad1984a_thinkpad_capture_source;
|
|
|
|
codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1984a_thinkpad_init;
|
|
|
|
break;
|
2011-02-07 22:19:34 +08:00
|
|
|
case AD1984A_PRECISION:
|
|
|
|
spec->mixers[0] = ad1984a_precision_mixers;
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] =
|
|
|
|
ad1984a_precision_verbs;
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1984a_precision_init;
|
|
|
|
break;
|
2009-09-28 04:11:21 +08:00
|
|
|
case AD1984A_TOUCHSMART:
|
|
|
|
spec->mixers[0] = ad1984a_touchsmart_mixers;
|
|
|
|
spec->init_verbs[0] = ad1984a_touchsmart_verbs;
|
|
|
|
spec->multiout.dig_out_nid = 0;
|
|
|
|
codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1984a_touchsmart_init;
|
|
|
|
/* set the upper-limit for mixer amp to 0dB for avoiding the
|
|
|
|
* possible damage by overloading
|
|
|
|
*/
|
|
|
|
snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
|
|
|
|
(0x17 << AC_AMPCAP_OFFSET_SHIFT) |
|
|
|
|
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
|
|
|
|
(0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
|
|
|
|
(1 << AC_AMPCAP_MUTE_SHIFT));
|
|
|
|
break;
|
2008-02-16 16:43:56 +08:00
|
|
|
}
|
|
|
|
|
2009-12-26 05:49:01 +08:00
|
|
|
codec->no_trigger_sense = 1;
|
2010-10-25 16:37:11 +08:00
|
|
|
codec->no_sticky_stream = 1;
|
2009-12-26 05:49:01 +08:00
|
|
|
|
2008-02-16 16:43:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1884a ad1884_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2008-02-16 16:43:56 +08:00
|
|
|
|
|
|
|
|
2007-06-20 21:46:13 +08:00
|
|
|
/*
|
2008-08-18 19:53:07 +08:00
|
|
|
* AD1882 / AD1882A
|
2007-06-20 21:46:13 +08:00
|
|
|
*
|
|
|
|
* port-A - front hp-out
|
|
|
|
* port-B - front mic-in
|
|
|
|
* port-C - rear line-in, shared surr-out (3stack)
|
|
|
|
* port-D - rear line-out
|
|
|
|
* port-E - rear mic-in, shared clfe-out (3stack)
|
|
|
|
* port-F - rear surr-out (6stack)
|
|
|
|
* port-G - rear clfe-out (6stack)
|
|
|
|
*/
|
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1882_dac_nids[3] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
0x04, 0x03, 0x05
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1882_adc_nids[2] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
0x08, 0x09,
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const hda_nid_t ad1882_capsrc_nids[2] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
0x0c, 0x0d,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define AD1882_SPDIF_OUT 0x02
|
|
|
|
|
|
|
|
/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1882_capture_source = {
|
2007-06-20 21:46:13 +08:00
|
|
|
.num_items = 5,
|
|
|
|
.items = {
|
|
|
|
{ "Front Mic", 0x1 },
|
|
|
|
{ "Mic", 0x4 },
|
|
|
|
{ "Line", 0x2 },
|
|
|
|
{ "CD", 0x3 },
|
|
|
|
{ "Mix", 0x7 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2008-08-18 19:53:07 +08:00
|
|
|
/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_input_mux ad1882a_capture_source = {
|
2008-08-18 19:53:07 +08:00
|
|
|
.num_items = 5,
|
|
|
|
.items = {
|
|
|
|
{ "Front Mic", 0x1 },
|
|
|
|
{ "Mic", 0x4},
|
|
|
|
{ "Line", 0x2 },
|
|
|
|
{ "Digital Mic", 0x06 },
|
|
|
|
{ "Mix", 0x7 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1882_base_mixers[] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
|
2008-08-18 19:53:07 +08:00
|
|
|
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
|
2007-06-20 21:46:13 +08:00
|
|
|
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
/* The multiple "Capture Source" controls confuse alsamixer
|
|
|
|
* So call somewhat different..
|
|
|
|
*/
|
|
|
|
/* .name = "Capture Source", */
|
|
|
|
.name = "Input Source",
|
|
|
|
.count = 2,
|
|
|
|
.info = ad198x_mux_enum_info,
|
|
|
|
.get = ad198x_mux_enum_get,
|
|
|
|
.put = ad198x_mux_enum_put,
|
|
|
|
},
|
|
|
|
/* SPDIF controls */
|
|
|
|
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
|
|
|
/* identical with ad1983 */
|
|
|
|
.info = ad1983_spdif_route_info,
|
|
|
|
.get = ad1983_spdif_route_get,
|
|
|
|
.put = ad1983_spdif_route_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
|
2008-08-18 19:53:07 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
|
2008-08-18 19:53:07 +08:00
|
|
|
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
|
|
|
|
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
|
|
|
|
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
|
2011-01-04 22:24:24 +08:00
|
|
|
HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
|
2008-08-18 19:53:07 +08:00
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Channel Mode",
|
|
|
|
.info = ad198x_ch_mode_info,
|
|
|
|
.get = ad198x_ch_mode_get,
|
|
|
|
.put = ad198x_ch_mode_put,
|
|
|
|
},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2012-08-13 17:09:35 +08:00
|
|
|
/* simple auto-mute control for AD1882 3-stack board */
|
|
|
|
#define AD1882_HP_EVENT 0x01
|
|
|
|
|
|
|
|
static void ad1882_3stack_automute(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
bool mute = snd_hda_jack_detect(codec, 0x11);
|
|
|
|
snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
|
|
|
|
mute ? 0 : PIN_OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ad1882_3stack_automute_init(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
ad198x_init(codec);
|
|
|
|
ad1882_3stack_automute(codec);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res)
|
|
|
|
{
|
|
|
|
switch (res >> 26) {
|
|
|
|
case AD1882_HP_EVENT:
|
|
|
|
ad1882_3stack_automute(codec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
|
|
|
|
HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1882_ch2_init[] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1882_ch4_init[] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1882_ch6_init[] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_channel_mode ad1882_modes[3] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
{ 2, ad1882_ch2_init },
|
|
|
|
{ 4, ad1882_ch4_init },
|
|
|
|
{ 6, ad1882_ch6_init },
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialization verbs
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_verb ad1882_init_verbs[] = {
|
2007-06-20 21:46:13 +08:00
|
|
|
/* DACs; mute as default */
|
|
|
|
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
|
|
|
/* Port-A (HP) mixer */
|
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-A pin */
|
|
|
|
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* HP selector - select DAC2 */
|
|
|
|
{0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
|
|
|
|
/* Port-D (Line-out) mixer */
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Port-D pin */
|
|
|
|
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Mono-out mixer */
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
|
|
|
|
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
|
|
|
/* Mono-out pin */
|
|
|
|
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
|
|
|
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Port-B (front mic) pin */
|
|
|
|
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
|
|
|
|
/* Port-C (line-in) pin */
|
|
|
|
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
|
|
|
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
|
|
|
|
/* Port-C mixer - mute as input */
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
/* Port-E (mic-in) pin */
|
|
|
|
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
|
|
|
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
|
|
|
|
/* Port-E mixer - mute as input */
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
/* Port-F (surround) */
|
|
|
|
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Port-G (CLFE) */
|
|
|
|
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
|
|
|
{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
|
|
|
/* Analog mixer; mute as default */
|
|
|
|
/* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
|
|
|
|
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
|
|
|
|
/* Analog Mix output amp */
|
|
|
|
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
|
|
|
|
/* SPDIF output selector */
|
|
|
|
{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
|
|
|
{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
|
|
|
|
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2012-08-13 17:09:35 +08:00
|
|
|
static const struct hda_verb ad1882_3stack_automute_verbs[] = {
|
|
|
|
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT},
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_amp_list ad1882_loopbacks[] = {
|
2007-08-10 23:21:45 +08:00
|
|
|
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 1 }, /* Mic */
|
|
|
|
{ 0x20, HDA_INPUT, 4 }, /* Line */
|
|
|
|
{ 0x20, HDA_INPUT, 6 }, /* CD */
|
|
|
|
{ } /* end */
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2007-06-20 21:46:13 +08:00
|
|
|
/* models */
|
|
|
|
enum {
|
2012-12-21 22:17:06 +08:00
|
|
|
AD1882_AUTO,
|
2007-06-20 21:46:13 +08:00
|
|
|
AD1882_3STACK,
|
|
|
|
AD1882_6STACK,
|
2012-08-13 17:09:35 +08:00
|
|
|
AD1882_3STACK_AUTOMUTE,
|
2007-06-20 21:46:13 +08:00
|
|
|
AD1882_MODELS
|
|
|
|
};
|
|
|
|
|
2011-01-17 18:29:34 +08:00
|
|
|
static const char * const ad1882_models[AD1986A_MODELS] = {
|
2012-12-21 22:17:06 +08:00
|
|
|
[AD1882_AUTO] = "auto",
|
2007-06-20 21:46:13 +08:00
|
|
|
[AD1882_3STACK] = "3stack",
|
|
|
|
[AD1882_6STACK] = "6stack",
|
2012-08-13 17:09:35 +08:00
|
|
|
[AD1882_3STACK_AUTOMUTE] = "3stack-automute",
|
2007-06-20 21:46:13 +08:00
|
|
|
};
|
2013-01-22 23:45:58 +08:00
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2007-06-20 21:46:13 +08:00
|
|
|
|
2012-12-21 22:17:06 +08:00
|
|
|
static int ad1882_parse_auto_config(struct hda_codec *codec)
|
|
|
|
{
|
2013-01-22 23:45:58 +08:00
|
|
|
struct ad198x_spec *spec;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
int err;
|
2012-12-21 22:17:06 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
|
|
|
|
2013-01-21 23:40:16 +08:00
|
|
|
spec->gen.mixer_nid = 0x20;
|
2012-12-21 22:17:06 +08:00
|
|
|
spec->beep_dev_nid = 0x10;
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad198x_parse_auto_config(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
err = ad1988_add_spdif_mux_ctl(codec);
|
|
|
|
if (err < 0)
|
2013-01-22 23:45:58 +08:00
|
|
|
goto error;
|
ALSA: hda - Add SPDIF mux control to AD codec auto-parser
AD codecs have strange implementations for choosing the SPDIF-output
mux source: the digital audio out widget may take the sources from
multiple connections, where 0x01 indicates it's a PCM while others
point ADCs. It's obviously invalid in the HD-audio spec POV, but it's
somehow convincing, too. And, to make things more complex, AD1988A
and AD1882 have deeper connection routes that aren't expressed
correctly.
In this patch, the SPDIF mux control is implemented in two ways:
- For easier one like AD1981, AD1983, AD1884 and AD1984, where the
SPDIF audio out widget takes just two or three sources, we can
simply implement via the normal input_mux and connection verb
calls.
- For the complex routes like AD1988A (but not AD1988B) or AD1882, we
prepare "faked" paths represented statically, and switch the paths
using these static ones, instead of parsing the routes from the
widget tree.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2013-01-22 22:31:33 +08:00
|
|
|
return 0;
|
2013-01-22 23:45:58 +08:00
|
|
|
|
|
|
|
error:
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
2012-12-21 22:17:06 +08:00
|
|
|
}
|
2007-06-20 21:46:13 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
#ifdef ENABLE_AD_STATIC_QUIRKS
|
2007-06-20 21:46:13 +08:00
|
|
|
static int patch_ad1882(struct hda_codec *codec)
|
|
|
|
{
|
|
|
|
struct ad198x_spec *spec;
|
2009-02-07 00:22:05 +08:00
|
|
|
int err, board_config;
|
2007-06-20 21:46:13 +08:00
|
|
|
|
2013-01-22 23:45:58 +08:00
|
|
|
board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
|
|
|
|
ad1882_models, NULL);
|
|
|
|
if (board_config == AD1882_AUTO)
|
|
|
|
return ad1882_parse_auto_config(codec);
|
|
|
|
|
2012-05-09 20:35:27 +08:00
|
|
|
err = alloc_ad_spec(codec);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
spec = codec->spec;
|
2007-06-20 21:46:13 +08:00
|
|
|
|
2009-02-07 00:22:05 +08:00
|
|
|
err = snd_hda_attach_beep_device(codec, 0x10);
|
|
|
|
if (err < 0) {
|
|
|
|
ad198x_free(codec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
|
|
|
|
|
2007-06-20 21:46:13 +08:00
|
|
|
spec->multiout.max_channels = 6;
|
|
|
|
spec->multiout.num_dacs = 3;
|
|
|
|
spec->multiout.dac_nids = ad1882_dac_nids;
|
|
|
|
spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
|
|
|
|
spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
|
|
|
|
spec->adc_nids = ad1882_adc_nids;
|
|
|
|
spec->capsrc_nids = ad1882_capsrc_nids;
|
2009-01-07 18:43:48 +08:00
|
|
|
if (codec->vendor_id == 0x11d41882)
|
2008-08-18 19:53:07 +08:00
|
|
|
spec->input_mux = &ad1882_capture_source;
|
|
|
|
else
|
|
|
|
spec->input_mux = &ad1882a_capture_source;
|
|
|
|
spec->num_mixers = 2;
|
2007-06-20 21:46:13 +08:00
|
|
|
spec->mixers[0] = ad1882_base_mixers;
|
2009-01-07 18:43:48 +08:00
|
|
|
if (codec->vendor_id == 0x11d41882)
|
2008-08-18 19:53:07 +08:00
|
|
|
spec->mixers[1] = ad1882_loopback_mixers;
|
|
|
|
else
|
|
|
|
spec->mixers[1] = ad1882a_loopback_mixers;
|
2007-06-20 21:46:13 +08:00
|
|
|
spec->num_init_verbs = 1;
|
|
|
|
spec->init_verbs[0] = ad1882_init_verbs;
|
|
|
|
spec->spdif_route = 0;
|
2012-08-25 00:38:08 +08:00
|
|
|
#ifdef CONFIG_PM
|
2007-08-10 23:21:45 +08:00
|
|
|
spec->loopback.amplist = ad1882_loopbacks;
|
|
|
|
#endif
|
2008-01-10 23:53:55 +08:00
|
|
|
spec->vmaster_nid = 0x04;
|
2007-06-20 21:46:13 +08:00
|
|
|
|
|
|
|
codec->patch_ops = ad198x_patch_ops;
|
|
|
|
|
|
|
|
/* override some parameters */
|
|
|
|
switch (board_config) {
|
|
|
|
default:
|
|
|
|
case AD1882_3STACK:
|
2012-08-13 17:09:35 +08:00
|
|
|
case AD1882_3STACK_AUTOMUTE:
|
2008-08-18 19:53:07 +08:00
|
|
|
spec->num_mixers = 3;
|
|
|
|
spec->mixers[2] = ad1882_3stack_mixers;
|
2007-06-20 21:46:13 +08:00
|
|
|
spec->channel_mode = ad1882_modes;
|
|
|
|
spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
|
|
|
|
spec->need_dac_fix = 1;
|
|
|
|
spec->multiout.max_channels = 2;
|
|
|
|
spec->multiout.num_dacs = 1;
|
2012-08-13 17:09:35 +08:00
|
|
|
if (board_config != AD1882_3STACK) {
|
|
|
|
spec->init_verbs[spec->num_init_verbs++] =
|
|
|
|
ad1882_3stack_automute_verbs;
|
|
|
|
codec->patch_ops.unsol_event = ad1882_3stack_unsol_event;
|
|
|
|
codec->patch_ops.init = ad1882_3stack_automute_init;
|
|
|
|
}
|
2007-06-20 21:46:13 +08:00
|
|
|
break;
|
|
|
|
case AD1882_6STACK:
|
2008-08-18 19:53:07 +08:00
|
|
|
spec->num_mixers = 3;
|
|
|
|
spec->mixers[2] = ad1882_6stack_mixers;
|
2007-06-20 21:46:13 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-12-26 05:49:01 +08:00
|
|
|
|
|
|
|
codec->no_trigger_sense = 1;
|
2010-10-25 16:37:11 +08:00
|
|
|
codec->no_sticky_stream = 1;
|
2009-12-26 05:49:01 +08:00
|
|
|
|
2007-06-20 21:46:13 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-01-22 23:45:58 +08:00
|
|
|
#else /* ENABLE_AD_STATIC_QUIRKS */
|
|
|
|
#define patch_ad1882 ad1882_parse_auto_config
|
|
|
|
#endif /* ENABLE_AD_STATIC_QUIRKS */
|
2007-06-20 21:46:13 +08:00
|
|
|
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* patch entries
|
|
|
|
*/
|
2011-05-02 17:33:15 +08:00
|
|
|
static const struct hda_codec_preset snd_hda_preset_analog[] = {
|
2008-02-16 16:43:56 +08:00
|
|
|
{ .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
|
2007-06-20 21:46:13 +08:00
|
|
|
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
|
2008-02-16 16:43:56 +08:00
|
|
|
{ .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
|
2007-05-19 00:21:41 +08:00
|
|
|
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
|
2008-02-16 16:43:56 +08:00
|
|
|
{ .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
|
|
|
|
{ .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
|
2005-04-14 19:35:51 +08:00
|
|
|
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
|
|
|
|
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
|
2007-05-19 00:21:41 +08:00
|
|
|
{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
|
2005-04-17 06:20:36 +08:00
|
|
|
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
|
2005-11-17 22:31:34 +08:00
|
|
|
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
|
2006-04-21 22:09:31 +08:00
|
|
|
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
|
2008-08-18 19:53:07 +08:00
|
|
|
{ .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
|
2008-04-16 00:46:42 +08:00
|
|
|
{ .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
|
|
|
|
{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
|
2005-04-17 06:20:36 +08:00
|
|
|
{} /* terminator */
|
|
|
|
};
|
2008-11-27 22:47:11 +08:00
|
|
|
|
|
|
|
MODULE_ALIAS("snd-hda-codec-id:11d4*");
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
|
|
|
|
|
|
|
|
static struct hda_codec_preset_list analog_list = {
|
|
|
|
.preset = snd_hda_preset_analog,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init patch_analog_init(void)
|
|
|
|
{
|
|
|
|
return snd_hda_add_codec_preset(&analog_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit patch_analog_exit(void)
|
|
|
|
{
|
|
|
|
snd_hda_delete_codec_preset(&analog_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(patch_analog_init)
|
|
|
|
module_exit(patch_analog_exit)
|