xref: /OK3568_Linux_fs/kernel/sound/core/vmaster.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Virtual master and follower controls
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/slab.h>
9*4882a593Smuzhiyun #include <linux/export.h>
10*4882a593Smuzhiyun #include <sound/core.h>
11*4882a593Smuzhiyun #include <sound/control.h>
12*4882a593Smuzhiyun #include <sound/tlv.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun /*
15*4882a593Smuzhiyun  * a subset of information returned via ctl info callback
16*4882a593Smuzhiyun  */
17*4882a593Smuzhiyun struct link_ctl_info {
18*4882a593Smuzhiyun 	snd_ctl_elem_type_t type; /* value type */
19*4882a593Smuzhiyun 	int count;		/* item count */
20*4882a593Smuzhiyun 	int min_val, max_val;	/* min, max values */
21*4882a593Smuzhiyun };
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /*
24*4882a593Smuzhiyun  * link master - this contains a list of follower controls that are
25*4882a593Smuzhiyun  * identical types, i.e. info returns the same value type and value
26*4882a593Smuzhiyun  * ranges, but may have different number of counts.
27*4882a593Smuzhiyun  *
28*4882a593Smuzhiyun  * The master control is so far only mono volume/switch for simplicity.
29*4882a593Smuzhiyun  * The same value will be applied to all followers.
30*4882a593Smuzhiyun  */
31*4882a593Smuzhiyun struct link_master {
32*4882a593Smuzhiyun 	struct list_head followers;
33*4882a593Smuzhiyun 	struct link_ctl_info info;
34*4882a593Smuzhiyun 	int val;		/* the master value */
35*4882a593Smuzhiyun 	unsigned int tlv[4];
36*4882a593Smuzhiyun 	void (*hook)(void *private_data, int);
37*4882a593Smuzhiyun 	void *hook_private_data;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun /*
41*4882a593Smuzhiyun  * link follower - this contains a follower control element
42*4882a593Smuzhiyun  *
43*4882a593Smuzhiyun  * It fakes the control callbacks with additional attenuation by the
44*4882a593Smuzhiyun  * master control.  A follower may have either one or two channels.
45*4882a593Smuzhiyun  */
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun struct link_follower {
48*4882a593Smuzhiyun 	struct list_head list;
49*4882a593Smuzhiyun 	struct link_master *master;
50*4882a593Smuzhiyun 	struct link_ctl_info info;
51*4882a593Smuzhiyun 	int vals[2];		/* current values */
52*4882a593Smuzhiyun 	unsigned int flags;
53*4882a593Smuzhiyun 	struct snd_kcontrol *kctl; /* original kcontrol pointer */
54*4882a593Smuzhiyun 	struct snd_kcontrol follower; /* the copy of original control entry */
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun 
follower_update(struct link_follower * follower)57*4882a593Smuzhiyun static int follower_update(struct link_follower *follower)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	struct snd_ctl_elem_value *uctl;
60*4882a593Smuzhiyun 	int err, ch;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
63*4882a593Smuzhiyun 	if (!uctl)
64*4882a593Smuzhiyun 		return -ENOMEM;
65*4882a593Smuzhiyun 	uctl->id = follower->follower.id;
66*4882a593Smuzhiyun 	err = follower->follower.get(&follower->follower, uctl);
67*4882a593Smuzhiyun 	if (err < 0)
68*4882a593Smuzhiyun 		goto error;
69*4882a593Smuzhiyun 	for (ch = 0; ch < follower->info.count; ch++)
70*4882a593Smuzhiyun 		follower->vals[ch] = uctl->value.integer.value[ch];
71*4882a593Smuzhiyun  error:
72*4882a593Smuzhiyun 	kfree(uctl);
73*4882a593Smuzhiyun 	return err < 0 ? err : 0;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun /* get the follower ctl info and save the initial values */
follower_init(struct link_follower * follower)77*4882a593Smuzhiyun static int follower_init(struct link_follower *follower)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	struct snd_ctl_elem_info *uinfo;
80*4882a593Smuzhiyun 	int err;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	if (follower->info.count) {
83*4882a593Smuzhiyun 		/* already initialized */
84*4882a593Smuzhiyun 		if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE)
85*4882a593Smuzhiyun 			return follower_update(follower);
86*4882a593Smuzhiyun 		return 0;
87*4882a593Smuzhiyun 	}
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
90*4882a593Smuzhiyun 	if (!uinfo)
91*4882a593Smuzhiyun 		return -ENOMEM;
92*4882a593Smuzhiyun 	uinfo->id = follower->follower.id;
93*4882a593Smuzhiyun 	err = follower->follower.info(&follower->follower, uinfo);
94*4882a593Smuzhiyun 	if (err < 0) {
95*4882a593Smuzhiyun 		kfree(uinfo);
96*4882a593Smuzhiyun 		return err;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 	follower->info.type = uinfo->type;
99*4882a593Smuzhiyun 	follower->info.count = uinfo->count;
100*4882a593Smuzhiyun 	if (follower->info.count > 2  ||
101*4882a593Smuzhiyun 	    (follower->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
102*4882a593Smuzhiyun 	     follower->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
103*4882a593Smuzhiyun 		pr_err("ALSA: vmaster: invalid follower element\n");
104*4882a593Smuzhiyun 		kfree(uinfo);
105*4882a593Smuzhiyun 		return -EINVAL;
106*4882a593Smuzhiyun 	}
107*4882a593Smuzhiyun 	follower->info.min_val = uinfo->value.integer.min;
108*4882a593Smuzhiyun 	follower->info.max_val = uinfo->value.integer.max;
109*4882a593Smuzhiyun 	kfree(uinfo);
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	return follower_update(follower);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /* initialize master volume */
master_init(struct link_master * master)115*4882a593Smuzhiyun static int master_init(struct link_master *master)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	struct link_follower *follower;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	if (master->info.count)
120*4882a593Smuzhiyun 		return 0; /* already initialized */
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	list_for_each_entry(follower, &master->followers, list) {
123*4882a593Smuzhiyun 		int err = follower_init(follower);
124*4882a593Smuzhiyun 		if (err < 0)
125*4882a593Smuzhiyun 			return err;
126*4882a593Smuzhiyun 		master->info = follower->info;
127*4882a593Smuzhiyun 		master->info.count = 1; /* always mono */
128*4882a593Smuzhiyun 		/* set full volume as default (= no attenuation) */
129*4882a593Smuzhiyun 		master->val = master->info.max_val;
130*4882a593Smuzhiyun 		if (master->hook)
131*4882a593Smuzhiyun 			master->hook(master->hook_private_data, master->val);
132*4882a593Smuzhiyun 		return 1;
133*4882a593Smuzhiyun 	}
134*4882a593Smuzhiyun 	return -ENOENT;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
follower_get_val(struct link_follower * follower,struct snd_ctl_elem_value * ucontrol)137*4882a593Smuzhiyun static int follower_get_val(struct link_follower *follower,
138*4882a593Smuzhiyun 			    struct snd_ctl_elem_value *ucontrol)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	int err, ch;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	err = follower_init(follower);
143*4882a593Smuzhiyun 	if (err < 0)
144*4882a593Smuzhiyun 		return err;
145*4882a593Smuzhiyun 	for (ch = 0; ch < follower->info.count; ch++)
146*4882a593Smuzhiyun 		ucontrol->value.integer.value[ch] = follower->vals[ch];
147*4882a593Smuzhiyun 	return 0;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
follower_put_val(struct link_follower * follower,struct snd_ctl_elem_value * ucontrol)150*4882a593Smuzhiyun static int follower_put_val(struct link_follower *follower,
151*4882a593Smuzhiyun 			    struct snd_ctl_elem_value *ucontrol)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	int err, ch, vol;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	err = master_init(follower->master);
156*4882a593Smuzhiyun 	if (err < 0)
157*4882a593Smuzhiyun 		return err;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	switch (follower->info.type) {
160*4882a593Smuzhiyun 	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
161*4882a593Smuzhiyun 		for (ch = 0; ch < follower->info.count; ch++)
162*4882a593Smuzhiyun 			ucontrol->value.integer.value[ch] &=
163*4882a593Smuzhiyun 				!!follower->master->val;
164*4882a593Smuzhiyun 		break;
165*4882a593Smuzhiyun 	case SNDRV_CTL_ELEM_TYPE_INTEGER:
166*4882a593Smuzhiyun 		for (ch = 0; ch < follower->info.count; ch++) {
167*4882a593Smuzhiyun 			/* max master volume is supposed to be 0 dB */
168*4882a593Smuzhiyun 			vol = ucontrol->value.integer.value[ch];
169*4882a593Smuzhiyun 			vol += follower->master->val - follower->master->info.max_val;
170*4882a593Smuzhiyun 			if (vol < follower->info.min_val)
171*4882a593Smuzhiyun 				vol = follower->info.min_val;
172*4882a593Smuzhiyun 			else if (vol > follower->info.max_val)
173*4882a593Smuzhiyun 				vol = follower->info.max_val;
174*4882a593Smuzhiyun 			ucontrol->value.integer.value[ch] = vol;
175*4882a593Smuzhiyun 		}
176*4882a593Smuzhiyun 		break;
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun 	return follower->follower.put(&follower->follower, ucontrol);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun /*
182*4882a593Smuzhiyun  * ctl callbacks for followers
183*4882a593Smuzhiyun  */
follower_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)184*4882a593Smuzhiyun static int follower_info(struct snd_kcontrol *kcontrol,
185*4882a593Smuzhiyun 			 struct snd_ctl_elem_info *uinfo)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
188*4882a593Smuzhiyun 	return follower->follower.info(&follower->follower, uinfo);
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun 
follower_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)191*4882a593Smuzhiyun static int follower_get(struct snd_kcontrol *kcontrol,
192*4882a593Smuzhiyun 			struct snd_ctl_elem_value *ucontrol)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
195*4882a593Smuzhiyun 	return follower_get_val(follower, ucontrol);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun 
follower_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)198*4882a593Smuzhiyun static int follower_put(struct snd_kcontrol *kcontrol,
199*4882a593Smuzhiyun 			struct snd_ctl_elem_value *ucontrol)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
202*4882a593Smuzhiyun 	int err, ch, changed = 0;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	err = follower_init(follower);
205*4882a593Smuzhiyun 	if (err < 0)
206*4882a593Smuzhiyun 		return err;
207*4882a593Smuzhiyun 	for (ch = 0; ch < follower->info.count; ch++) {
208*4882a593Smuzhiyun 		if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
209*4882a593Smuzhiyun 			changed = 1;
210*4882a593Smuzhiyun 			follower->vals[ch] = ucontrol->value.integer.value[ch];
211*4882a593Smuzhiyun 		}
212*4882a593Smuzhiyun 	}
213*4882a593Smuzhiyun 	if (!changed)
214*4882a593Smuzhiyun 		return 0;
215*4882a593Smuzhiyun 	err = follower_put_val(follower, ucontrol);
216*4882a593Smuzhiyun 	if (err < 0)
217*4882a593Smuzhiyun 		return err;
218*4882a593Smuzhiyun 	return 1;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun 
follower_tlv_cmd(struct snd_kcontrol * kcontrol,int op_flag,unsigned int size,unsigned int __user * tlv)221*4882a593Smuzhiyun static int follower_tlv_cmd(struct snd_kcontrol *kcontrol,
222*4882a593Smuzhiyun 			    int op_flag, unsigned int size,
223*4882a593Smuzhiyun 			    unsigned int __user *tlv)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun 	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
226*4882a593Smuzhiyun 	/* FIXME: this assumes that the max volume is 0 dB */
227*4882a593Smuzhiyun 	return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun 
follower_free(struct snd_kcontrol * kcontrol)230*4882a593Smuzhiyun static void follower_free(struct snd_kcontrol *kcontrol)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
233*4882a593Smuzhiyun 	if (follower->follower.private_free)
234*4882a593Smuzhiyun 		follower->follower.private_free(&follower->follower);
235*4882a593Smuzhiyun 	if (follower->master)
236*4882a593Smuzhiyun 		list_del(&follower->list);
237*4882a593Smuzhiyun 	kfree(follower);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun /*
241*4882a593Smuzhiyun  * Add a follower control to the group with the given master control
242*4882a593Smuzhiyun  *
243*4882a593Smuzhiyun  * All followers must be the same type (returning the same information
244*4882a593Smuzhiyun  * via info callback).  The function doesn't check it, so it's your
245*4882a593Smuzhiyun  * responsibility.
246*4882a593Smuzhiyun  *
247*4882a593Smuzhiyun  * Also, some additional limitations:
248*4882a593Smuzhiyun  * - at most two channels
249*4882a593Smuzhiyun  * - logarithmic volume control (dB level), no linear volume
250*4882a593Smuzhiyun  * - master can only attenuate the volume, no gain
251*4882a593Smuzhiyun  */
_snd_ctl_add_follower(struct snd_kcontrol * master,struct snd_kcontrol * follower,unsigned int flags)252*4882a593Smuzhiyun int _snd_ctl_add_follower(struct snd_kcontrol *master,
253*4882a593Smuzhiyun 			  struct snd_kcontrol *follower,
254*4882a593Smuzhiyun 			  unsigned int flags)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun 	struct link_master *master_link = snd_kcontrol_chip(master);
257*4882a593Smuzhiyun 	struct link_follower *srec;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	srec = kzalloc(struct_size(srec, follower.vd, follower->count),
260*4882a593Smuzhiyun 		       GFP_KERNEL);
261*4882a593Smuzhiyun 	if (!srec)
262*4882a593Smuzhiyun 		return -ENOMEM;
263*4882a593Smuzhiyun 	srec->kctl = follower;
264*4882a593Smuzhiyun 	srec->follower = *follower;
265*4882a593Smuzhiyun 	memcpy(srec->follower.vd, follower->vd, follower->count * sizeof(*follower->vd));
266*4882a593Smuzhiyun 	srec->master = master_link;
267*4882a593Smuzhiyun 	srec->flags = flags;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	/* override callbacks */
270*4882a593Smuzhiyun 	follower->info = follower_info;
271*4882a593Smuzhiyun 	follower->get = follower_get;
272*4882a593Smuzhiyun 	follower->put = follower_put;
273*4882a593Smuzhiyun 	if (follower->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
274*4882a593Smuzhiyun 		follower->tlv.c = follower_tlv_cmd;
275*4882a593Smuzhiyun 	follower->private_data = srec;
276*4882a593Smuzhiyun 	follower->private_free = follower_free;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	list_add_tail(&srec->list, &master_link->followers);
279*4882a593Smuzhiyun 	return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun EXPORT_SYMBOL(_snd_ctl_add_follower);
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun /*
284*4882a593Smuzhiyun  * ctl callbacks for master controls
285*4882a593Smuzhiyun  */
master_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)286*4882a593Smuzhiyun static int master_info(struct snd_kcontrol *kcontrol,
287*4882a593Smuzhiyun 		      struct snd_ctl_elem_info *uinfo)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun 	struct link_master *master = snd_kcontrol_chip(kcontrol);
290*4882a593Smuzhiyun 	int ret;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	ret = master_init(master);
293*4882a593Smuzhiyun 	if (ret < 0)
294*4882a593Smuzhiyun 		return ret;
295*4882a593Smuzhiyun 	uinfo->type = master->info.type;
296*4882a593Smuzhiyun 	uinfo->count = master->info.count;
297*4882a593Smuzhiyun 	uinfo->value.integer.min = master->info.min_val;
298*4882a593Smuzhiyun 	uinfo->value.integer.max = master->info.max_val;
299*4882a593Smuzhiyun 	return 0;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun 
master_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)302*4882a593Smuzhiyun static int master_get(struct snd_kcontrol *kcontrol,
303*4882a593Smuzhiyun 		      struct snd_ctl_elem_value *ucontrol)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun 	struct link_master *master = snd_kcontrol_chip(kcontrol);
306*4882a593Smuzhiyun 	int err = master_init(master);
307*4882a593Smuzhiyun 	if (err < 0)
308*4882a593Smuzhiyun 		return err;
309*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = master->val;
310*4882a593Smuzhiyun 	return 0;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun 
sync_followers(struct link_master * master,int old_val,int new_val)313*4882a593Smuzhiyun static int sync_followers(struct link_master *master, int old_val, int new_val)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun 	struct link_follower *follower;
316*4882a593Smuzhiyun 	struct snd_ctl_elem_value *uval;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	uval = kmalloc(sizeof(*uval), GFP_KERNEL);
319*4882a593Smuzhiyun 	if (!uval)
320*4882a593Smuzhiyun 		return -ENOMEM;
321*4882a593Smuzhiyun 	list_for_each_entry(follower, &master->followers, list) {
322*4882a593Smuzhiyun 		master->val = old_val;
323*4882a593Smuzhiyun 		uval->id = follower->follower.id;
324*4882a593Smuzhiyun 		follower_get_val(follower, uval);
325*4882a593Smuzhiyun 		master->val = new_val;
326*4882a593Smuzhiyun 		follower_put_val(follower, uval);
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 	kfree(uval);
329*4882a593Smuzhiyun 	return 0;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
master_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)332*4882a593Smuzhiyun static int master_put(struct snd_kcontrol *kcontrol,
333*4882a593Smuzhiyun 		      struct snd_ctl_elem_value *ucontrol)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	struct link_master *master = snd_kcontrol_chip(kcontrol);
336*4882a593Smuzhiyun 	int err, new_val, old_val;
337*4882a593Smuzhiyun 	bool first_init;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	err = master_init(master);
340*4882a593Smuzhiyun 	if (err < 0)
341*4882a593Smuzhiyun 		return err;
342*4882a593Smuzhiyun 	first_init = err;
343*4882a593Smuzhiyun 	old_val = master->val;
344*4882a593Smuzhiyun 	new_val = ucontrol->value.integer.value[0];
345*4882a593Smuzhiyun 	if (new_val == old_val)
346*4882a593Smuzhiyun 		return 0;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	err = sync_followers(master, old_val, new_val);
349*4882a593Smuzhiyun 	if (err < 0)
350*4882a593Smuzhiyun 		return err;
351*4882a593Smuzhiyun 	if (master->hook && !first_init)
352*4882a593Smuzhiyun 		master->hook(master->hook_private_data, master->val);
353*4882a593Smuzhiyun 	return 1;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun 
master_free(struct snd_kcontrol * kcontrol)356*4882a593Smuzhiyun static void master_free(struct snd_kcontrol *kcontrol)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun 	struct link_master *master = snd_kcontrol_chip(kcontrol);
359*4882a593Smuzhiyun 	struct link_follower *follower, *n;
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	/* free all follower links and retore the original follower kctls */
362*4882a593Smuzhiyun 	list_for_each_entry_safe(follower, n, &master->followers, list) {
363*4882a593Smuzhiyun 		struct snd_kcontrol *sctl = follower->kctl;
364*4882a593Smuzhiyun 		struct list_head olist = sctl->list;
365*4882a593Smuzhiyun 		memcpy(sctl, &follower->follower, sizeof(*sctl));
366*4882a593Smuzhiyun 		memcpy(sctl->vd, follower->follower.vd,
367*4882a593Smuzhiyun 		       sctl->count * sizeof(*sctl->vd));
368*4882a593Smuzhiyun 		sctl->list = olist; /* keep the current linked-list */
369*4882a593Smuzhiyun 		kfree(follower);
370*4882a593Smuzhiyun 	}
371*4882a593Smuzhiyun 	kfree(master);
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun /**
376*4882a593Smuzhiyun  * snd_ctl_make_virtual_master - Create a virtual master control
377*4882a593Smuzhiyun  * @name: name string of the control element to create
378*4882a593Smuzhiyun  * @tlv: optional TLV int array for dB information
379*4882a593Smuzhiyun  *
380*4882a593Smuzhiyun  * Creates a virtual master control with the given name string.
381*4882a593Smuzhiyun  *
382*4882a593Smuzhiyun  * After creating a vmaster element, you can add the follower controls
383*4882a593Smuzhiyun  * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached().
384*4882a593Smuzhiyun  *
385*4882a593Smuzhiyun  * The optional argument @tlv can be used to specify the TLV information
386*4882a593Smuzhiyun  * for dB scale of the master control.  It should be a single element
387*4882a593Smuzhiyun  * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
388*4882a593Smuzhiyun  * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
389*4882a593Smuzhiyun  *
390*4882a593Smuzhiyun  * Return: The created control element, or %NULL for errors (ENOMEM).
391*4882a593Smuzhiyun  */
snd_ctl_make_virtual_master(char * name,const unsigned int * tlv)392*4882a593Smuzhiyun struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
393*4882a593Smuzhiyun 						 const unsigned int *tlv)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun 	struct link_master *master;
396*4882a593Smuzhiyun 	struct snd_kcontrol *kctl;
397*4882a593Smuzhiyun 	struct snd_kcontrol_new knew;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	memset(&knew, 0, sizeof(knew));
400*4882a593Smuzhiyun 	knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
401*4882a593Smuzhiyun 	knew.name = name;
402*4882a593Smuzhiyun 	knew.info = master_info;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	master = kzalloc(sizeof(*master), GFP_KERNEL);
405*4882a593Smuzhiyun 	if (!master)
406*4882a593Smuzhiyun 		return NULL;
407*4882a593Smuzhiyun 	INIT_LIST_HEAD(&master->followers);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	kctl = snd_ctl_new1(&knew, master);
410*4882a593Smuzhiyun 	if (!kctl) {
411*4882a593Smuzhiyun 		kfree(master);
412*4882a593Smuzhiyun 		return NULL;
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 	/* override some callbacks */
415*4882a593Smuzhiyun 	kctl->info = master_info;
416*4882a593Smuzhiyun 	kctl->get = master_get;
417*4882a593Smuzhiyun 	kctl->put = master_put;
418*4882a593Smuzhiyun 	kctl->private_free = master_free;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	/* additional (constant) TLV read */
421*4882a593Smuzhiyun 	if (tlv) {
422*4882a593Smuzhiyun 		unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
423*4882a593Smuzhiyun 		if (type == SNDRV_CTL_TLVT_DB_SCALE ||
424*4882a593Smuzhiyun 		    type == SNDRV_CTL_TLVT_DB_MINMAX ||
425*4882a593Smuzhiyun 		    type == SNDRV_CTL_TLVT_DB_MINMAX_MUTE) {
426*4882a593Smuzhiyun 			kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
427*4882a593Smuzhiyun 			memcpy(master->tlv, tlv, sizeof(master->tlv));
428*4882a593Smuzhiyun 			kctl->tlv.p = master->tlv;
429*4882a593Smuzhiyun 		}
430*4882a593Smuzhiyun 	}
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	return kctl;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun EXPORT_SYMBOL(snd_ctl_make_virtual_master);
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun /**
437*4882a593Smuzhiyun  * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
438*4882a593Smuzhiyun  * @kcontrol: vmaster kctl element
439*4882a593Smuzhiyun  * @hook: the hook function
440*4882a593Smuzhiyun  * @private_data: the private_data pointer to be saved
441*4882a593Smuzhiyun  *
442*4882a593Smuzhiyun  * Adds the given hook to the vmaster control element so that it's called
443*4882a593Smuzhiyun  * at each time when the value is changed.
444*4882a593Smuzhiyun  *
445*4882a593Smuzhiyun  * Return: Zero.
446*4882a593Smuzhiyun  */
snd_ctl_add_vmaster_hook(struct snd_kcontrol * kcontrol,void (* hook)(void * private_data,int),void * private_data)447*4882a593Smuzhiyun int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
448*4882a593Smuzhiyun 			     void (*hook)(void *private_data, int),
449*4882a593Smuzhiyun 			     void *private_data)
450*4882a593Smuzhiyun {
451*4882a593Smuzhiyun 	struct link_master *master = snd_kcontrol_chip(kcontrol);
452*4882a593Smuzhiyun 	master->hook = hook;
453*4882a593Smuzhiyun 	master->hook_private_data = private_data;
454*4882a593Smuzhiyun 	return 0;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun /**
459*4882a593Smuzhiyun  * snd_ctl_sync_vmaster - Sync the vmaster followers and hook
460*4882a593Smuzhiyun  * @kcontrol: vmaster kctl element
461*4882a593Smuzhiyun  * @hook_only: sync only the hook
462*4882a593Smuzhiyun  *
463*4882a593Smuzhiyun  * Forcibly call the put callback of each follower and call the hook function
464*4882a593Smuzhiyun  * to synchronize with the current value of the given vmaster element.
465*4882a593Smuzhiyun  * NOP when NULL is passed to @kcontrol.
466*4882a593Smuzhiyun  */
snd_ctl_sync_vmaster(struct snd_kcontrol * kcontrol,bool hook_only)467*4882a593Smuzhiyun void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
468*4882a593Smuzhiyun {
469*4882a593Smuzhiyun 	struct link_master *master;
470*4882a593Smuzhiyun 	bool first_init = false;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	if (!kcontrol)
473*4882a593Smuzhiyun 		return;
474*4882a593Smuzhiyun 	master = snd_kcontrol_chip(kcontrol);
475*4882a593Smuzhiyun 	if (!hook_only) {
476*4882a593Smuzhiyun 		int err = master_init(master);
477*4882a593Smuzhiyun 		if (err < 0)
478*4882a593Smuzhiyun 			return;
479*4882a593Smuzhiyun 		first_init = err;
480*4882a593Smuzhiyun 		err = sync_followers(master, master->val, master->val);
481*4882a593Smuzhiyun 		if (err < 0)
482*4882a593Smuzhiyun 			return;
483*4882a593Smuzhiyun 	}
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	if (master->hook && !first_init)
486*4882a593Smuzhiyun 		master->hook(master->hook_private_data, master->val);
487*4882a593Smuzhiyun }
488*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun /**
491*4882a593Smuzhiyun  * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower
492*4882a593Smuzhiyun  * @kctl: vmaster kctl element
493*4882a593Smuzhiyun  * @func: function to apply
494*4882a593Smuzhiyun  * @arg: optional function argument
495*4882a593Smuzhiyun  *
496*4882a593Smuzhiyun  * Apply the function @func to each follower kctl of the given vmaster kctl.
497*4882a593Smuzhiyun  * Returns 0 if successful, or a negative error code.
498*4882a593Smuzhiyun  */
snd_ctl_apply_vmaster_followers(struct snd_kcontrol * kctl,int (* func)(struct snd_kcontrol * vfollower,struct snd_kcontrol * follower,void * arg),void * arg)499*4882a593Smuzhiyun int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
500*4882a593Smuzhiyun 				    int (*func)(struct snd_kcontrol *vfollower,
501*4882a593Smuzhiyun 						struct snd_kcontrol *follower,
502*4882a593Smuzhiyun 						void *arg),
503*4882a593Smuzhiyun 				    void *arg)
504*4882a593Smuzhiyun {
505*4882a593Smuzhiyun 	struct link_master *master;
506*4882a593Smuzhiyun 	struct link_follower *follower;
507*4882a593Smuzhiyun 	int err;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	master = snd_kcontrol_chip(kctl);
510*4882a593Smuzhiyun 	err = master_init(master);
511*4882a593Smuzhiyun 	if (err < 0)
512*4882a593Smuzhiyun 		return err;
513*4882a593Smuzhiyun 	list_for_each_entry(follower, &master->followers, list) {
514*4882a593Smuzhiyun 		err = func(follower->kctl, &follower->follower, arg);
515*4882a593Smuzhiyun 		if (err < 0)
516*4882a593Smuzhiyun 			return err;
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	return 0;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);
522