summaryrefslogtreecommitdiff
path: root/sound/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/patch_analog.c266
1 files changed, 261 insertions, 5 deletions
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index a186b3da20b7..5d8328a64207 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -35,6 +35,10 @@
struct ad198x_spec {
struct hda_gen_spec gen;
+ /* for auto parser */
+ int smux_paths[4];
+ unsigned int cur_smux;
+
const struct snd_kcontrol_new *mixers[6];
int num_mixers;
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
@@ -1519,13 +1523,94 @@ static const char * const ad1983_models[AD1983_MODELS] = {
[AD1983_BASIC] = "basic",
};
+/*
+ * 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;
+}
+
static int ad1983_parse_auto_config(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ int err;
spec->beep_dev_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- return ad198x_parse_auto_config(codec);
+ err = ad198x_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+ return 0;
}
static int patch_ad1983(struct hda_codec *codec)
@@ -1950,11 +2035,18 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
static int ad1981_parse_auto_config(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ int err;
spec->gen.mixer_nid = 0x0e;
spec->beep_dev_nid = 0x10;
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
- return ad198x_parse_auto_config(codec);
+ err = ad198x_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+ return 0;
}
static int patch_ad1981(struct hda_codec *codec)
@@ -2820,17 +2912,167 @@ static const struct hda_amp_list ad1988_loopbacks[] = {
};
#endif
+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;
+}
+
/*
*/
static int ad1988_parse_auto_config(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ int err;
spec->gen.mixer_nid = 0x20;
spec->beep_dev_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- return ad198x_parse_auto_config(codec);
+ err = ad198x_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+ return 0;
}
/*
@@ -3174,11 +3416,18 @@ static const char * const ad1884_models[AD1884_MODELS] = {
static int ad1884_parse_auto_config(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ int err;
spec->gen.mixer_nid = 0x20;
spec->beep_dev_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- return ad198x_parse_auto_config(codec);
+ err = ad198x_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+ return 0;
}
static int patch_ad1884_auto(struct hda_codec *codec)
@@ -4635,11 +4884,18 @@ static const char * const ad1882_models[AD1986A_MODELS] = {
static int ad1882_parse_auto_config(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ int err;
spec->gen.mixer_nid = 0x20;
spec->beep_dev_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- return ad198x_parse_auto_config(codec);
+ err = ad198x_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+ return 0;
}
static int patch_ad1882(struct hda_codec *codec)