aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-07-27 19:02:40 +0200
committerJaroslav Kysela <perex@perex.cz>2007-10-16 15:58:11 +0200
commit532d5381793f3c824f8ff68d7067fab8c76bb811 (patch)
tree41a27e08a905c28576167b711ccc2b06ea234a9f
parent2807314d467e7dd929c42050031aabbd28e78f0b (diff)
[ALSA] hda-codec - Add a generic bind-control helper
Added callbacks for a generic bind-control of mixer elements. This can be used for creating a mixer element controlling multiple widgets at the same time. Two macros, HDA_BIND_VOL() and HDA_BIND_SW(), are introduced for creating bind-volume and bind-switch, respectively. It taks the mixer element name and struct hda_bind_ctls pointer, which contains the real control callbacks in ops field and long array for private_value of each bound widget. All widgets have to be the same type (i.e. the same amp capability). Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
-rw-r--r--sound/pci/hda/hda_codec.c87
-rw-r--r--sound/pci/hda/hda_local.h47
-rw-r--r--sound/pci/hda/patch_analog.c177
-rw-r--r--sound/pci/hda/patch_realtek.c28
4 files changed, 184 insertions, 155 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index e7843ffeeb2..36879a93eac 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1006,6 +1006,93 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
}
/*
+ * generic bound volume/swtich controls
+ */
+int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_bind_ctls *c;
+ int err;
+
+ c = (struct hda_bind_ctls *)kcontrol->private_value;
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = *c->values;
+ err = c->ops->info(kcontrol, uinfo);
+ kcontrol->private_value = (long)c;
+ mutex_unlock(&codec->spdif_mutex);
+ return err;
+}
+
+int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_bind_ctls *c;
+ int err;
+
+ c = (struct hda_bind_ctls *)kcontrol->private_value;
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = *c->values;
+ err = c->ops->get(kcontrol, ucontrol);
+ kcontrol->private_value = (long)c;
+ mutex_unlock(&codec->spdif_mutex);
+ return err;
+}
+
+int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_bind_ctls *c;
+ unsigned long *vals;
+ int err = 0, change = 0;
+
+ c = (struct hda_bind_ctls *)kcontrol->private_value;
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ for (vals = c->values; *vals; vals++) {
+ kcontrol->private_value = *vals;
+ err = c->ops->put(kcontrol, ucontrol);
+ if (err < 0)
+ break;
+ change |= err;
+ }
+ kcontrol->private_value = (long)c;
+ mutex_unlock(&codec->spdif_mutex);
+ return err < 0 ? err : change;
+}
+
+int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_bind_ctls *c;
+ int err;
+
+ c = (struct hda_bind_ctls *)kcontrol->private_value;
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = *c->values;
+ err = c->ops->tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = (long)c;
+ mutex_unlock(&codec->spdif_mutex);
+ return err;
+}
+
+struct hda_ctl_ops snd_hda_bind_vol = {
+ .info = snd_hda_mixer_amp_volume_info,
+ .get = snd_hda_mixer_amp_volume_get,
+ .put = snd_hda_mixer_amp_volume_put,
+ .tlv = snd_hda_mixer_amp_tlv
+};
+
+struct hda_ctl_ops snd_hda_bind_sw = {
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = snd_hda_mixer_amp_switch_put,
+ .tlv = snd_hda_mixer_amp_tlv
+};
+
+/*
* SPDIF out controls
*/
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 12428a67eb2..fafcffe6fc7 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -102,6 +102,53 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+/* more generic bound controls */
+struct hda_ctl_ops {
+ snd_kcontrol_info_t *info;
+ snd_kcontrol_get_t *get;
+ snd_kcontrol_put_t *put;
+ snd_kcontrol_tlv_rw_t *tlv;
+};
+
+extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */
+extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */
+
+struct hda_bind_ctls {
+ struct hda_ctl_ops *ops;
+ long values[];
+};
+
+int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv);
+
+#define HDA_BIND_VOL(xname, bindrec) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\
+ .info = snd_hda_mixer_bind_ctls_info,\
+ .get = snd_hda_mixer_bind_ctls_get,\
+ .put = snd_hda_mixer_bind_ctls_put,\
+ .tlv = { .c = snd_hda_mixer_bind_tlv },\
+ .private_value = (long) (bindrec) }
+#define HDA_BIND_SW(xname, bindrec) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+ .name = xname, \
+ .info = snd_hda_mixer_bind_ctls_info,\
+ .get = snd_hda_mixer_bind_ctls_get,\
+ .put = snd_hda_mixer_bind_ctls_put,\
+ .private_value = (long) (bindrec) }
+
+/*
+ * SPDIF I/O
+ */
int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 488724f2e30..cc2e944cc59 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -422,94 +422,36 @@ static struct hda_input_mux ad1986a_capture_source = {
},
};
-/*
- * PCM control
- *
- * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
- */
-
-#define ad1986a_pcm_amp_vol_info snd_hda_mixer_amp_volume_info
-
-static int ad1986a_pcm_amp_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *ad = codec->spec;
-
- mutex_lock(&ad->amp_mutex);
- snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
- mutex_unlock(&ad->amp_mutex);
- return 0;
-}
-static int ad1986a_pcm_amp_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *ad = codec->spec;
- int i, change = 0;
-
- mutex_lock(&ad->amp_mutex);
- for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
- change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
- }
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
- mutex_unlock(&ad->amp_mutex);
- return change;
-}
-
-#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_switch_info
-
-static int ad1986a_pcm_amp_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *ad = codec->spec;
-
- mutex_lock(&ad->amp_mutex);
- snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
- mutex_unlock(&ad->amp_mutex);
- return 0;
-}
-
-static int ad1986a_pcm_amp_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *ad = codec->spec;
- int i, change = 0;
+static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
+ .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
+ },
+};
- mutex_lock(&ad->amp_mutex);
- for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
- change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- }
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
- mutex_unlock(&ad->amp_mutex);
- return change;
-}
+static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
+ .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
+ },
+};
/*
* mixers
*/
static struct snd_kcontrol_new ad1986a_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PCM Playback Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
- .info = ad1986a_pcm_amp_vol_info,
- .get = ad1986a_pcm_amp_vol_get,
- .put = ad1986a_pcm_amp_vol_put,
- .tlv = { .c = snd_hda_mixer_amp_tlv },
- .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PCM Playback Switch",
- .info = ad1986a_pcm_amp_sw_info,
- .get = ad1986a_pcm_amp_sw_get,
- .put = ad1986a_pcm_amp_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
- },
+ /*
+ * 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),
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),
@@ -596,41 +538,23 @@ static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
/* laptop-eapd model - 2ch only */
/* master controls both pins 0x1a and 0x1b */
-static int ad1986a_laptop_master_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
- 0x7f, valp[0] & 0x7f);
- change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
- 0x7f, valp[1] & 0x7f);
- snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
- 0x7f, valp[0] & 0x7f);
- snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
- 0x7f, valp[1] & 0x7f);
- return change;
-}
-
-static int ad1986a_laptop_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
+static struct hda_bind_ctls ad1986a_laptop_master_vol = {
+ .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,
+ },
+};
- change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
- 0x80, valp[0] ? 0 : 0x80);
- change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
- 0x80, valp[1] ? 0 : 0x80);
- snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
- 0x80, valp[0] ? 0 : 0x80);
- snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
- 0x80, valp[1] ? 0 : 0x80);
- return change;
-}
+static struct hda_bind_ctls ad1986a_laptop_master_sw = {
+ .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,
+ },
+};
static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
.num_items = 3,
@@ -642,23 +566,8 @@ static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
};
static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Volume",
- .info = snd_hda_mixer_amp_volume_info,
- .get = snd_hda_mixer_amp_volume_get,
- .put = ad1986a_laptop_master_vol_put,
- .tlv = { .c = snd_hda_mixer_amp_tlv },
- .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1986a_laptop_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- },
+ HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
+ HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
@@ -856,7 +765,6 @@ static int patch_ad1986a(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 6;
@@ -1064,7 +972,6 @@ static int patch_ad1983(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 2;
@@ -1466,7 +1373,6 @@ static int patch_ad1981(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 2;
@@ -2672,7 +2578,6 @@ static int patch_ad1988(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- mutex_init(&spec->amp_mutex);
codec->spec = spec;
if (is_rev2(codec))
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index d839d567f8e..f27e073d22b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -7140,28 +7140,18 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
{ } /* end */
};
-static int alc262_sony_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- unsigned long private_save = kcontrol->private_value;
- int change;
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT);
- change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- kcontrol->private_value = private_save;
- change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- return change;
-}
+static struct hda_bind_ctls alc262_sony_bind_sw = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ 0,
+ },
+};
static struct snd_kcontrol_new alc262_sony_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Front Playback Switch",
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc262_sony_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- },
+ HDA_BIND_SW("Front Playback Switch", &alc262_sony_bind_sw),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),