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.  It keeps the long array pointer in
private_value, which represents the private_value of each widget.
All widgets have to be the same type.

diff -r 80cfb0604e70 pci/hda/hda_codec.c
--- a/pci/hda/hda_codec.c	Wed May 09 14:08:53 2007 +0200
+++ b/pci/hda/hda_codec.c	Wed May 09 16:47:58 2007 +0200
@@ -1012,6 +1012,78 @@ int snd_hda_mixer_bind_switch_put(struct
 	kcontrol->private_value = pval;
 	mutex_unlock(&codec->spdif_mutex);
 	return err < 0 ? err : change;
+}
+
+/*
+ * 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->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->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;
+	int i, err = 0, change = 0;
+
+	c = (struct hda_bind_ctls *)kcontrol->private_value;
+	mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+	for (i = 0; i < c->num_binds; i++) {
+		kcontrol->private_value = c->values[i];
+		err = c->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->tlv(kcontrol, op_flag, size, tlv);
+	kcontrol->private_value = (long)c;
+	mutex_unlock(&codec->spdif_mutex);
+	return err;
 }
 
 /*
diff -r 80cfb0604e70 pci/hda/hda_local.h
--- a/pci/hda/hda_local.h	Wed May 09 14:08:53 2007 +0200
+++ b/pci/hda/hda_local.h	Wed May 09 16:49:31 2007 +0200
@@ -91,6 +91,25 @@ int snd_hda_mixer_bind_switch_get(struct
 int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
 
+/* more generic bound controls */
+#define HDA_MAX_BIND_CTLS	8
+struct hda_bind_ctls {
+	snd_kcontrol_info_t *info;
+	snd_kcontrol_get_t *get;
+	snd_kcontrol_put_t *put;
+	snd_kcontrol_tlv_rw_t *tlv;
+	int num_binds;
+	long values[HDA_MAX_BIND_CTLS];
+};
+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);
+
 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 -r 80cfb0604e70 pci/hda/patch_analog.c
--- a/pci/hda/patch_analog.c	Wed May 09 14:08:53 2007 +0200
+++ b/pci/hda/patch_analog.c	Wed May 09 16:49:59 2007 +0200
@@ -429,93 +429,58 @@ static struct hda_input_mux ad1986a_capt
 	},
 };
 
-/*
- * 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;
-
-	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_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,
+	.num_binds = 3,
+	.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),
+	},
+};
+
+static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
+	.info = snd_hda_mixer_amp_switch_info,
+	.get = snd_hda_mixer_amp_switch_get,
+	.put = snd_hda_mixer_amp_switch_put,
+	.num_binds = 3,
+	.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),
+	},
+};
 
 /*
  * mixers
  */
 static struct snd_kcontrol_new ad1986a_mixers[] = {
+	/*
+	 * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
+	 */
 	{
 		.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)
+		.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) &ad1986a_bind_pcm_vol,
 	},
 	{
 		.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)
+		.info = snd_hda_mixer_bind_ctls_info,
+		.get =  snd_hda_mixer_bind_ctls_get,
+		.put = snd_hda_mixer_bind_ctls_put,
+		.private_value = (long) &ad1986a_bind_pcm_sw,
 	},
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -603,41 +568,28 @@ static struct snd_kcontrol_new ad1986a_l
 /* 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;
-
-	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_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,
+	.num_binds = 2,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+	},
+};
+
+static struct hda_bind_ctls ad1986a_laptop_master_sw = {
+	.info = snd_hda_mixer_amp_switch_info,
+	.get = snd_hda_mixer_amp_switch_get,
+	.put = snd_hda_mixer_amp_switch_put,
+	.num_binds = 2,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+	},
+};
 
 static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
 	.num_items = 3,
@@ -652,19 +604,22 @@ static struct snd_kcontrol_new ad1986a_l
 	{
 		.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),
+		.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) &ad1986a_laptop_master_vol,
 	},
 	{
 		.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),
+		.info = snd_hda_mixer_bind_ctls_info,
+		.get =  snd_hda_mixer_bind_ctls_get,
+		.put = snd_hda_mixer_bind_ctls_put,
+		.private_value = (long) &ad1986a_laptop_master_sw,
 	},
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
@@ -863,7 +818,6 @@ static int patch_ad1986a(struct hda_code
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 6;
@@ -1071,7 +1025,6 @@ static int patch_ad1983(struct hda_codec
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -1473,7 +1426,6 @@ static int patch_ad1981(struct hda_codec
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -2662,7 +2614,6 @@ static int patch_ad1988(struct hda_codec
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	if (is_rev2(codec))
