aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-06-28 17:25:57 +0200
committerTakashi Iwai <tiwai@suse.de>2013-06-28 17:26:27 +0200
commitb53215eddce7b69170c83282a8ebec95e6d7a59f (patch)
tree5a628b1437c2d7e2561a244f7abe79cc3ddd3f36
parent6bace726387332b2d4d9dd563e8b0fc82dbce952 (diff)
downloadhda-emu-b53215eddce7b69170c83282a8ebec95e6d7a59f.tar.gz
Add snd_ctl_sync_vmaster()
Taken from the upstream change (commit 1ca2f2ec).
-rw-r--r--include/sound/control.h3
-rw-r--r--snd-vmaster.c80
2 files changed, 58 insertions, 25 deletions
diff --git a/include/sound/control.h b/include/sound/control.h
index 346ec79..47b92c5 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -160,7 +160,8 @@ snd_ctl_add_slave_uncached(struct snd_kcontrol *master,
int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kctl,
void (*hook)(void *, int),
void *private_data);
-void snd_ctl_sync_vmaster_hook(struct snd_kcontrol *kctl);
+void snd_ctl_sync_vmaster(struct snd_kcontrol *kctl, bool hook_only);
+#define snd_ctl_sync_vmaster_hook(kctl) snd_ctl_sync_vmaster(kctl, true)
/*
* Helper functions for jack-detection controls
diff --git a/snd-vmaster.c b/snd-vmaster.c
index 8b984d5..5df8dc2 100644
--- a/snd-vmaster.c
+++ b/snd-vmaster.c
@@ -44,7 +44,7 @@ struct link_master {
/*
* link slave - this contains a slave control element
*
- * It fakes the control callbacks with additional attenuation by the
+ * It fakes the control callbacsk with additional attenuation by the
* master control. A slave may have either one or two channels.
*/
@@ -213,7 +213,10 @@ static int slave_put(struct snd_kcontrol *kcontrol,
}
if (!changed)
return 0;
- return slave_put_val(slave, ucontrol);
+ err = slave_put_val(slave, ucontrol);
+ if (err < 0)
+ return err;
+ return 1;
}
static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
@@ -307,20 +310,10 @@ static int master_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int master_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int sync_slaves(struct link_master *master, int old_val, int new_val)
{
- struct link_master *master = snd_kcontrol_chip(kcontrol);
struct link_slave *slave;
struct snd_ctl_elem_value *uval;
- int err, old_val;
-
- err = master_init(master);
- if (err < 0)
- return err;
- old_val = master->val;
- if (ucontrol->value.integer.value[0] == old_val)
- return 0;
uval = kmalloc(sizeof(*uval), GFP_KERNEL);
if (!uval)
@@ -329,11 +322,33 @@ static int master_put(struct snd_kcontrol *kcontrol,
master->val = old_val;
uval->id = slave->slave.id;
slave_get_val(slave, uval);
- master->val = ucontrol->value.integer.value[0];
+ master->val = new_val;
slave_put_val(slave, uval);
}
kfree(uval);
- if (master->hook && !err)
+ return 0;
+}
+
+static int master_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int err, new_val, old_val;
+ bool first_init;
+
+ err = master_init(master);
+ if (err < 0)
+ return err;
+ first_init = err;
+ old_val = master->val;
+ new_val = ucontrol->value.integer.value[0];
+ if (new_val == old_val)
+ return 0;
+
+ err = sync_slaves(master, old_val, new_val);
+ if (err < 0)
+ return err;
+ if (master->hook && first_init)
master->hook(master->hook_private_data, master->val);
return 1;
}
@@ -343,7 +358,7 @@ static void master_free(struct snd_kcontrol *kcontrol)
struct link_master *master = snd_kcontrol_chip(kcontrol);
struct link_slave *slave, *n;
- /* free all slave links and restore the original slave kctls */
+ /* free all slave links and retore the original slave kctls */
list_for_each_entry_safe(slave, n, &master->slaves, list) {
struct snd_kcontrol *sctl = slave->kctl;
struct list_head olist = sctl->list;
@@ -363,7 +378,6 @@ static void master_free(struct snd_kcontrol *kcontrol)
* @tlv: optional TLV int array for dB information
*
* Creates a virtual master control with the given name string.
- * Returns the created control element, or NULL for errors (ENOMEM).
*
* After creating a vmaster element, you can add the slave controls
* via snd_ctl_add_slave() or snd_ctl_add_slave_uncached().
@@ -372,6 +386,8 @@ static void master_free(struct snd_kcontrol *kcontrol)
* for dB scale of the master control. It should be a single element
* with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
* #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
+ *
+ * Return: The created control element, or %NULL for errors (ENOMEM).
*/
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
const unsigned int *tlv)
@@ -419,9 +435,12 @@ EXPORT_SYMBOL(snd_ctl_make_virtual_master);
* snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
* @kcontrol: vmaster kctl element
* @hook: the hook function
+ * @private_data: the private_data pointer to be saved
*
* Adds the given hook to the vmaster control element so that it's called
* at each time when the value is changed.
+ *
+ * Return: Zero.
*/
int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
void (*hook)(void *private_data, int),
@@ -435,20 +454,33 @@ int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
/**
- * snd_ctl_sync_vmaster_hook - Sync the vmaster hook
+ * snd_ctl_sync_vmaster - Sync the vmaster slaves and hook
* @kcontrol: vmaster kctl element
+ * @hook_only: sync only the hook
*
- * Call the hook function to synchronize with the current value of the given
- * vmaster element. NOP when NULL is passed to @kcontrol or the hook doesn't
- * exist.
+ * Forcibly call the put callback of each slave and call the hook function
+ * to synchronize with the current value of the given vmaster element.
+ * NOP when NULL is passed to @kcontrol.
*/
-void snd_ctl_sync_vmaster_hook(struct snd_kcontrol *kcontrol)
+void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
{
struct link_master *master;
+ bool first_init = false;
+
if (!kcontrol)
return;
master = snd_kcontrol_chip(kcontrol);
- if (master->hook)
+ if (!hook_only) {
+ int err = master_init(master);
+ if (err < 0)
+ return;
+ first_init = err;
+ err = sync_slaves(master, master->val, master->val);
+ if (err < 0)
+ return;
+ }
+
+ if (master->hook && !first_init)
master->hook(master->hook_private_data, master->val);
}
-EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster_hook);
+EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);