18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Virtual master and follower controls
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <sound/core.h>
118c2ecf20Sopenharmony_ci#include <sound/control.h>
128c2ecf20Sopenharmony_ci#include <sound/tlv.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * a subset of information returned via ctl info callback
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_cistruct link_ctl_info {
188c2ecf20Sopenharmony_ci	snd_ctl_elem_type_t type; /* value type */
198c2ecf20Sopenharmony_ci	int count;		/* item count */
208c2ecf20Sopenharmony_ci	int min_val, max_val;	/* min, max values */
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * link master - this contains a list of follower controls that are
258c2ecf20Sopenharmony_ci * identical types, i.e. info returns the same value type and value
268c2ecf20Sopenharmony_ci * ranges, but may have different number of counts.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * The master control is so far only mono volume/switch for simplicity.
298c2ecf20Sopenharmony_ci * The same value will be applied to all followers.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistruct link_master {
328c2ecf20Sopenharmony_ci	struct list_head followers;
338c2ecf20Sopenharmony_ci	struct link_ctl_info info;
348c2ecf20Sopenharmony_ci	int val;		/* the master value */
358c2ecf20Sopenharmony_ci	unsigned int tlv[4];
368c2ecf20Sopenharmony_ci	void (*hook)(void *private_data, int);
378c2ecf20Sopenharmony_ci	void *hook_private_data;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * link follower - this contains a follower control element
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * It fakes the control callbacks with additional attenuation by the
448c2ecf20Sopenharmony_ci * master control.  A follower may have either one or two channels.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct link_follower {
488c2ecf20Sopenharmony_ci	struct list_head list;
498c2ecf20Sopenharmony_ci	struct link_master *master;
508c2ecf20Sopenharmony_ci	struct link_ctl_info info;
518c2ecf20Sopenharmony_ci	int vals[2];		/* current values */
528c2ecf20Sopenharmony_ci	unsigned int flags;
538c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl; /* original kcontrol pointer */
548c2ecf20Sopenharmony_ci	struct snd_kcontrol follower; /* the copy of original control entry */
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int follower_update(struct link_follower *follower)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *uctl;
608c2ecf20Sopenharmony_ci	int err, ch;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
638c2ecf20Sopenharmony_ci	if (!uctl)
648c2ecf20Sopenharmony_ci		return -ENOMEM;
658c2ecf20Sopenharmony_ci	uctl->id = follower->follower.id;
668c2ecf20Sopenharmony_ci	err = follower->follower.get(&follower->follower, uctl);
678c2ecf20Sopenharmony_ci	if (err < 0)
688c2ecf20Sopenharmony_ci		goto error;
698c2ecf20Sopenharmony_ci	for (ch = 0; ch < follower->info.count; ch++)
708c2ecf20Sopenharmony_ci		follower->vals[ch] = uctl->value.integer.value[ch];
718c2ecf20Sopenharmony_ci error:
728c2ecf20Sopenharmony_ci	kfree(uctl);
738c2ecf20Sopenharmony_ci	return err < 0 ? err : 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* get the follower ctl info and save the initial values */
778c2ecf20Sopenharmony_cistatic int follower_init(struct link_follower *follower)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info *uinfo;
808c2ecf20Sopenharmony_ci	int err;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (follower->info.count) {
838c2ecf20Sopenharmony_ci		/* already initialized */
848c2ecf20Sopenharmony_ci		if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE)
858c2ecf20Sopenharmony_ci			return follower_update(follower);
868c2ecf20Sopenharmony_ci		return 0;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
908c2ecf20Sopenharmony_ci	if (!uinfo)
918c2ecf20Sopenharmony_ci		return -ENOMEM;
928c2ecf20Sopenharmony_ci	uinfo->id = follower->follower.id;
938c2ecf20Sopenharmony_ci	err = follower->follower.info(&follower->follower, uinfo);
948c2ecf20Sopenharmony_ci	if (err < 0) {
958c2ecf20Sopenharmony_ci		kfree(uinfo);
968c2ecf20Sopenharmony_ci		return err;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	follower->info.type = uinfo->type;
998c2ecf20Sopenharmony_ci	follower->info.count = uinfo->count;
1008c2ecf20Sopenharmony_ci	if (follower->info.count > 2  ||
1018c2ecf20Sopenharmony_ci	    (follower->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
1028c2ecf20Sopenharmony_ci	     follower->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
1038c2ecf20Sopenharmony_ci		pr_err("ALSA: vmaster: invalid follower element\n");
1048c2ecf20Sopenharmony_ci		kfree(uinfo);
1058c2ecf20Sopenharmony_ci		return -EINVAL;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci	follower->info.min_val = uinfo->value.integer.min;
1088c2ecf20Sopenharmony_ci	follower->info.max_val = uinfo->value.integer.max;
1098c2ecf20Sopenharmony_ci	kfree(uinfo);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return follower_update(follower);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* initialize master volume */
1158c2ecf20Sopenharmony_cistatic int master_init(struct link_master *master)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct link_follower *follower;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (master->info.count)
1208c2ecf20Sopenharmony_ci		return 0; /* already initialized */
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	list_for_each_entry(follower, &master->followers, list) {
1238c2ecf20Sopenharmony_ci		int err = follower_init(follower);
1248c2ecf20Sopenharmony_ci		if (err < 0)
1258c2ecf20Sopenharmony_ci			return err;
1268c2ecf20Sopenharmony_ci		master->info = follower->info;
1278c2ecf20Sopenharmony_ci		master->info.count = 1; /* always mono */
1288c2ecf20Sopenharmony_ci		/* set full volume as default (= no attenuation) */
1298c2ecf20Sopenharmony_ci		master->val = master->info.max_val;
1308c2ecf20Sopenharmony_ci		if (master->hook)
1318c2ecf20Sopenharmony_ci			master->hook(master->hook_private_data, master->val);
1328c2ecf20Sopenharmony_ci		return 1;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	return -ENOENT;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int follower_get_val(struct link_follower *follower,
1388c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	int err, ch;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	err = follower_init(follower);
1438c2ecf20Sopenharmony_ci	if (err < 0)
1448c2ecf20Sopenharmony_ci		return err;
1458c2ecf20Sopenharmony_ci	for (ch = 0; ch < follower->info.count; ch++)
1468c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[ch] = follower->vals[ch];
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int follower_put_val(struct link_follower *follower,
1518c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	int err, ch, vol;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	err = master_init(follower->master);
1568c2ecf20Sopenharmony_ci	if (err < 0)
1578c2ecf20Sopenharmony_ci		return err;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	switch (follower->info.type) {
1608c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1618c2ecf20Sopenharmony_ci		for (ch = 0; ch < follower->info.count; ch++)
1628c2ecf20Sopenharmony_ci			ucontrol->value.integer.value[ch] &=
1638c2ecf20Sopenharmony_ci				!!follower->master->val;
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	case SNDRV_CTL_ELEM_TYPE_INTEGER:
1668c2ecf20Sopenharmony_ci		for (ch = 0; ch < follower->info.count; ch++) {
1678c2ecf20Sopenharmony_ci			/* max master volume is supposed to be 0 dB */
1688c2ecf20Sopenharmony_ci			vol = ucontrol->value.integer.value[ch];
1698c2ecf20Sopenharmony_ci			vol += follower->master->val - follower->master->info.max_val;
1708c2ecf20Sopenharmony_ci			if (vol < follower->info.min_val)
1718c2ecf20Sopenharmony_ci				vol = follower->info.min_val;
1728c2ecf20Sopenharmony_ci			else if (vol > follower->info.max_val)
1738c2ecf20Sopenharmony_ci				vol = follower->info.max_val;
1748c2ecf20Sopenharmony_ci			ucontrol->value.integer.value[ch] = vol;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci		break;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	return follower->follower.put(&follower->follower, ucontrol);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/*
1828c2ecf20Sopenharmony_ci * ctl callbacks for followers
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cistatic int follower_info(struct snd_kcontrol *kcontrol,
1858c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_info *uinfo)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
1888c2ecf20Sopenharmony_ci	return follower->follower.info(&follower->follower, uinfo);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int follower_get(struct snd_kcontrol *kcontrol,
1928c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
1958c2ecf20Sopenharmony_ci	return follower_get_val(follower, ucontrol);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int follower_put(struct snd_kcontrol *kcontrol,
1998c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
2028c2ecf20Sopenharmony_ci	int err, ch, changed = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	err = follower_init(follower);
2058c2ecf20Sopenharmony_ci	if (err < 0)
2068c2ecf20Sopenharmony_ci		return err;
2078c2ecf20Sopenharmony_ci	for (ch = 0; ch < follower->info.count; ch++) {
2088c2ecf20Sopenharmony_ci		if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
2098c2ecf20Sopenharmony_ci			changed = 1;
2108c2ecf20Sopenharmony_ci			follower->vals[ch] = ucontrol->value.integer.value[ch];
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	if (!changed)
2148c2ecf20Sopenharmony_ci		return 0;
2158c2ecf20Sopenharmony_ci	err = follower_put_val(follower, ucontrol);
2168c2ecf20Sopenharmony_ci	if (err < 0)
2178c2ecf20Sopenharmony_ci		return err;
2188c2ecf20Sopenharmony_ci	return 1;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int follower_tlv_cmd(struct snd_kcontrol *kcontrol,
2228c2ecf20Sopenharmony_ci			    int op_flag, unsigned int size,
2238c2ecf20Sopenharmony_ci			    unsigned int __user *tlv)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
2268c2ecf20Sopenharmony_ci	/* FIXME: this assumes that the max volume is 0 dB */
2278c2ecf20Sopenharmony_ci	return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic void follower_free(struct snd_kcontrol *kcontrol)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
2338c2ecf20Sopenharmony_ci	if (follower->follower.private_free)
2348c2ecf20Sopenharmony_ci		follower->follower.private_free(&follower->follower);
2358c2ecf20Sopenharmony_ci	if (follower->master)
2368c2ecf20Sopenharmony_ci		list_del(&follower->list);
2378c2ecf20Sopenharmony_ci	kfree(follower);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * Add a follower control to the group with the given master control
2428c2ecf20Sopenharmony_ci *
2438c2ecf20Sopenharmony_ci * All followers must be the same type (returning the same information
2448c2ecf20Sopenharmony_ci * via info callback).  The function doesn't check it, so it's your
2458c2ecf20Sopenharmony_ci * responsibility.
2468c2ecf20Sopenharmony_ci *
2478c2ecf20Sopenharmony_ci * Also, some additional limitations:
2488c2ecf20Sopenharmony_ci * - at most two channels
2498c2ecf20Sopenharmony_ci * - logarithmic volume control (dB level), no linear volume
2508c2ecf20Sopenharmony_ci * - master can only attenuate the volume, no gain
2518c2ecf20Sopenharmony_ci */
2528c2ecf20Sopenharmony_ciint _snd_ctl_add_follower(struct snd_kcontrol *master,
2538c2ecf20Sopenharmony_ci			  struct snd_kcontrol *follower,
2548c2ecf20Sopenharmony_ci			  unsigned int flags)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct link_master *master_link = snd_kcontrol_chip(master);
2578c2ecf20Sopenharmony_ci	struct link_follower *srec;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	srec = kzalloc(struct_size(srec, follower.vd, follower->count),
2608c2ecf20Sopenharmony_ci		       GFP_KERNEL);
2618c2ecf20Sopenharmony_ci	if (!srec)
2628c2ecf20Sopenharmony_ci		return -ENOMEM;
2638c2ecf20Sopenharmony_ci	srec->kctl = follower;
2648c2ecf20Sopenharmony_ci	srec->follower = *follower;
2658c2ecf20Sopenharmony_ci	memcpy(srec->follower.vd, follower->vd, follower->count * sizeof(*follower->vd));
2668c2ecf20Sopenharmony_ci	srec->master = master_link;
2678c2ecf20Sopenharmony_ci	srec->flags = flags;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/* override callbacks */
2708c2ecf20Sopenharmony_ci	follower->info = follower_info;
2718c2ecf20Sopenharmony_ci	follower->get = follower_get;
2728c2ecf20Sopenharmony_ci	follower->put = follower_put;
2738c2ecf20Sopenharmony_ci	if (follower->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
2748c2ecf20Sopenharmony_ci		follower->tlv.c = follower_tlv_cmd;
2758c2ecf20Sopenharmony_ci	follower->private_data = srec;
2768c2ecf20Sopenharmony_ci	follower->private_free = follower_free;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	list_add_tail(&srec->list, &master_link->followers);
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(_snd_ctl_add_follower);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/*
2848c2ecf20Sopenharmony_ci * ctl callbacks for master controls
2858c2ecf20Sopenharmony_ci */
2868c2ecf20Sopenharmony_cistatic int master_info(struct snd_kcontrol *kcontrol,
2878c2ecf20Sopenharmony_ci		      struct snd_ctl_elem_info *uinfo)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct link_master *master = snd_kcontrol_chip(kcontrol);
2908c2ecf20Sopenharmony_ci	int ret;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ret = master_init(master);
2938c2ecf20Sopenharmony_ci	if (ret < 0)
2948c2ecf20Sopenharmony_ci		return ret;
2958c2ecf20Sopenharmony_ci	uinfo->type = master->info.type;
2968c2ecf20Sopenharmony_ci	uinfo->count = master->info.count;
2978c2ecf20Sopenharmony_ci	uinfo->value.integer.min = master->info.min_val;
2988c2ecf20Sopenharmony_ci	uinfo->value.integer.max = master->info.max_val;
2998c2ecf20Sopenharmony_ci	return 0;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int master_get(struct snd_kcontrol *kcontrol,
3038c2ecf20Sopenharmony_ci		      struct snd_ctl_elem_value *ucontrol)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct link_master *master = snd_kcontrol_chip(kcontrol);
3068c2ecf20Sopenharmony_ci	int err = master_init(master);
3078c2ecf20Sopenharmony_ci	if (err < 0)
3088c2ecf20Sopenharmony_ci		return err;
3098c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = master->val;
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int sync_followers(struct link_master *master, int old_val, int new_val)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct link_follower *follower;
3168c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *uval;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	uval = kmalloc(sizeof(*uval), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!uval)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci	list_for_each_entry(follower, &master->followers, list) {
3228c2ecf20Sopenharmony_ci		master->val = old_val;
3238c2ecf20Sopenharmony_ci		uval->id = follower->follower.id;
3248c2ecf20Sopenharmony_ci		follower_get_val(follower, uval);
3258c2ecf20Sopenharmony_ci		master->val = new_val;
3268c2ecf20Sopenharmony_ci		follower_put_val(follower, uval);
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci	kfree(uval);
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int master_put(struct snd_kcontrol *kcontrol,
3338c2ecf20Sopenharmony_ci		      struct snd_ctl_elem_value *ucontrol)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct link_master *master = snd_kcontrol_chip(kcontrol);
3368c2ecf20Sopenharmony_ci	int err, new_val, old_val;
3378c2ecf20Sopenharmony_ci	bool first_init;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	err = master_init(master);
3408c2ecf20Sopenharmony_ci	if (err < 0)
3418c2ecf20Sopenharmony_ci		return err;
3428c2ecf20Sopenharmony_ci	first_init = err;
3438c2ecf20Sopenharmony_ci	old_val = master->val;
3448c2ecf20Sopenharmony_ci	new_val = ucontrol->value.integer.value[0];
3458c2ecf20Sopenharmony_ci	if (new_val == old_val)
3468c2ecf20Sopenharmony_ci		return 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	err = sync_followers(master, old_val, new_val);
3498c2ecf20Sopenharmony_ci	if (err < 0)
3508c2ecf20Sopenharmony_ci		return err;
3518c2ecf20Sopenharmony_ci	if (master->hook && !first_init)
3528c2ecf20Sopenharmony_ci		master->hook(master->hook_private_data, master->val);
3538c2ecf20Sopenharmony_ci	return 1;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void master_free(struct snd_kcontrol *kcontrol)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct link_master *master = snd_kcontrol_chip(kcontrol);
3598c2ecf20Sopenharmony_ci	struct link_follower *follower, *n;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* free all follower links and retore the original follower kctls */
3628c2ecf20Sopenharmony_ci	list_for_each_entry_safe(follower, n, &master->followers, list) {
3638c2ecf20Sopenharmony_ci		struct snd_kcontrol *sctl = follower->kctl;
3648c2ecf20Sopenharmony_ci		struct list_head olist = sctl->list;
3658c2ecf20Sopenharmony_ci		memcpy(sctl, &follower->follower, sizeof(*sctl));
3668c2ecf20Sopenharmony_ci		memcpy(sctl->vd, follower->follower.vd,
3678c2ecf20Sopenharmony_ci		       sctl->count * sizeof(*sctl->vd));
3688c2ecf20Sopenharmony_ci		sctl->list = olist; /* keep the current linked-list */
3698c2ecf20Sopenharmony_ci		kfree(follower);
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci	kfree(master);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/**
3768c2ecf20Sopenharmony_ci * snd_ctl_make_virtual_master - Create a virtual master control
3778c2ecf20Sopenharmony_ci * @name: name string of the control element to create
3788c2ecf20Sopenharmony_ci * @tlv: optional TLV int array for dB information
3798c2ecf20Sopenharmony_ci *
3808c2ecf20Sopenharmony_ci * Creates a virtual master control with the given name string.
3818c2ecf20Sopenharmony_ci *
3828c2ecf20Sopenharmony_ci * After creating a vmaster element, you can add the follower controls
3838c2ecf20Sopenharmony_ci * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached().
3848c2ecf20Sopenharmony_ci *
3858c2ecf20Sopenharmony_ci * The optional argument @tlv can be used to specify the TLV information
3868c2ecf20Sopenharmony_ci * for dB scale of the master control.  It should be a single element
3878c2ecf20Sopenharmony_ci * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
3888c2ecf20Sopenharmony_ci * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
3898c2ecf20Sopenharmony_ci *
3908c2ecf20Sopenharmony_ci * Return: The created control element, or %NULL for errors (ENOMEM).
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_cistruct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
3938c2ecf20Sopenharmony_ci						 const unsigned int *tlv)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct link_master *master;
3968c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
3978c2ecf20Sopenharmony_ci	struct snd_kcontrol_new knew;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	memset(&knew, 0, sizeof(knew));
4008c2ecf20Sopenharmony_ci	knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
4018c2ecf20Sopenharmony_ci	knew.name = name;
4028c2ecf20Sopenharmony_ci	knew.info = master_info;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	master = kzalloc(sizeof(*master), GFP_KERNEL);
4058c2ecf20Sopenharmony_ci	if (!master)
4068c2ecf20Sopenharmony_ci		return NULL;
4078c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&master->followers);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	kctl = snd_ctl_new1(&knew, master);
4108c2ecf20Sopenharmony_ci	if (!kctl) {
4118c2ecf20Sopenharmony_ci		kfree(master);
4128c2ecf20Sopenharmony_ci		return NULL;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	/* override some callbacks */
4158c2ecf20Sopenharmony_ci	kctl->info = master_info;
4168c2ecf20Sopenharmony_ci	kctl->get = master_get;
4178c2ecf20Sopenharmony_ci	kctl->put = master_put;
4188c2ecf20Sopenharmony_ci	kctl->private_free = master_free;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* additional (constant) TLV read */
4218c2ecf20Sopenharmony_ci	if (tlv) {
4228c2ecf20Sopenharmony_ci		unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
4238c2ecf20Sopenharmony_ci		if (type == SNDRV_CTL_TLVT_DB_SCALE ||
4248c2ecf20Sopenharmony_ci		    type == SNDRV_CTL_TLVT_DB_MINMAX ||
4258c2ecf20Sopenharmony_ci		    type == SNDRV_CTL_TLVT_DB_MINMAX_MUTE) {
4268c2ecf20Sopenharmony_ci			kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
4278c2ecf20Sopenharmony_ci			memcpy(master->tlv, tlv, sizeof(master->tlv));
4288c2ecf20Sopenharmony_ci			kctl->tlv.p = master->tlv;
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return kctl;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ctl_make_virtual_master);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/**
4378c2ecf20Sopenharmony_ci * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
4388c2ecf20Sopenharmony_ci * @kcontrol: vmaster kctl element
4398c2ecf20Sopenharmony_ci * @hook: the hook function
4408c2ecf20Sopenharmony_ci * @private_data: the private_data pointer to be saved
4418c2ecf20Sopenharmony_ci *
4428c2ecf20Sopenharmony_ci * Adds the given hook to the vmaster control element so that it's called
4438c2ecf20Sopenharmony_ci * at each time when the value is changed.
4448c2ecf20Sopenharmony_ci *
4458c2ecf20Sopenharmony_ci * Return: Zero.
4468c2ecf20Sopenharmony_ci */
4478c2ecf20Sopenharmony_ciint snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
4488c2ecf20Sopenharmony_ci			     void (*hook)(void *private_data, int),
4498c2ecf20Sopenharmony_ci			     void *private_data)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct link_master *master = snd_kcontrol_chip(kcontrol);
4528c2ecf20Sopenharmony_ci	master->hook = hook;
4538c2ecf20Sopenharmony_ci	master->hook_private_data = private_data;
4548c2ecf20Sopenharmony_ci	return 0;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci/**
4598c2ecf20Sopenharmony_ci * snd_ctl_sync_vmaster - Sync the vmaster followers and hook
4608c2ecf20Sopenharmony_ci * @kcontrol: vmaster kctl element
4618c2ecf20Sopenharmony_ci * @hook_only: sync only the hook
4628c2ecf20Sopenharmony_ci *
4638c2ecf20Sopenharmony_ci * Forcibly call the put callback of each follower and call the hook function
4648c2ecf20Sopenharmony_ci * to synchronize with the current value of the given vmaster element.
4658c2ecf20Sopenharmony_ci * NOP when NULL is passed to @kcontrol.
4668c2ecf20Sopenharmony_ci */
4678c2ecf20Sopenharmony_civoid snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct link_master *master;
4708c2ecf20Sopenharmony_ci	bool first_init = false;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (!kcontrol)
4738c2ecf20Sopenharmony_ci		return;
4748c2ecf20Sopenharmony_ci	master = snd_kcontrol_chip(kcontrol);
4758c2ecf20Sopenharmony_ci	if (!hook_only) {
4768c2ecf20Sopenharmony_ci		int err = master_init(master);
4778c2ecf20Sopenharmony_ci		if (err < 0)
4788c2ecf20Sopenharmony_ci			return;
4798c2ecf20Sopenharmony_ci		first_init = err;
4808c2ecf20Sopenharmony_ci		err = sync_followers(master, master->val, master->val);
4818c2ecf20Sopenharmony_ci		if (err < 0)
4828c2ecf20Sopenharmony_ci			return;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (master->hook && !first_init)
4868c2ecf20Sopenharmony_ci		master->hook(master->hook_private_data, master->val);
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/**
4918c2ecf20Sopenharmony_ci * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower
4928c2ecf20Sopenharmony_ci * @kctl: vmaster kctl element
4938c2ecf20Sopenharmony_ci * @func: function to apply
4948c2ecf20Sopenharmony_ci * @arg: optional function argument
4958c2ecf20Sopenharmony_ci *
4968c2ecf20Sopenharmony_ci * Apply the function @func to each follower kctl of the given vmaster kctl.
4978c2ecf20Sopenharmony_ci * Returns 0 if successful, or a negative error code.
4988c2ecf20Sopenharmony_ci */
4998c2ecf20Sopenharmony_ciint snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
5008c2ecf20Sopenharmony_ci				    int (*func)(struct snd_kcontrol *vfollower,
5018c2ecf20Sopenharmony_ci						struct snd_kcontrol *follower,
5028c2ecf20Sopenharmony_ci						void *arg),
5038c2ecf20Sopenharmony_ci				    void *arg)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct link_master *master;
5068c2ecf20Sopenharmony_ci	struct link_follower *follower;
5078c2ecf20Sopenharmony_ci	int err;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	master = snd_kcontrol_chip(kctl);
5108c2ecf20Sopenharmony_ci	err = master_init(master);
5118c2ecf20Sopenharmony_ci	if (err < 0)
5128c2ecf20Sopenharmony_ci		return err;
5138c2ecf20Sopenharmony_ci	list_for_each_entry(follower, &master->followers, list) {
5148c2ecf20Sopenharmony_ci		err = func(follower->kctl, &follower->follower, arg);
5158c2ecf20Sopenharmony_ci		if (err < 0)
5168c2ecf20Sopenharmony_ci			return err;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	return 0;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);
522