Blame view

sound/core/vmaster.c 14 KB
a10e763b8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
2
  /*
9ab0cb309   Takashi Iwai   ALSA: Replace the...
3
   * Virtual master and follower controls
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
4
5
   *
   *  Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
6
7
8
   */
  
  #include <linux/slab.h>
d81a6d717   Paul Gortmaker   sound: Add export...
9
  #include <linux/export.h>
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
10
11
  #include <sound/core.h>
  #include <sound/control.h>
1c82ed1bc   Takashi Iwai   [ALSA] Keep priva...
12
  #include <sound/tlv.h>
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
13
14
15
16
17
  
  /*
   * a subset of information returned via ctl info callback
   */
  struct link_ctl_info {
fea952e5c   Clemens Ladisch   ALSA: core: spars...
18
  	snd_ctl_elem_type_t type; /* value type */
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
19
20
21
22
23
  	int count;		/* item count */
  	int min_val, max_val;	/* min, max values */
  };
  
  /*
9ab0cb309   Takashi Iwai   ALSA: Replace the...
24
   * link master - this contains a list of follower controls that are
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
25
26
27
28
   * identical types, i.e. info returns the same value type and value
   * ranges, but may have different number of counts.
   *
   * The master control is so far only mono volume/switch for simplicity.
9ab0cb309   Takashi Iwai   ALSA: Replace the...
29
   * The same value will be applied to all followers.
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
30
31
   */
  struct link_master {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
32
  	struct list_head followers;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
33
34
  	struct link_ctl_info info;
  	int val;		/* the master value */
1c82ed1bc   Takashi Iwai   [ALSA] Keep priva...
35
  	unsigned int tlv[4];
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
36
37
  	void (*hook)(void *private_data, int);
  	void *hook_private_data;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
38
39
40
  };
  
  /*
9ab0cb309   Takashi Iwai   ALSA: Replace the...
41
   * link follower - this contains a follower control element
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
42
   *
9ab0cb309   Takashi Iwai   ALSA: Replace the...
43
44
   * It fakes the control callbacks with additional attenuation by the
   * master control.  A follower may have either one or two channels.
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
45
   */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
46
  struct link_follower {
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
47
48
49
50
  	struct list_head list;
  	struct link_master *master;
  	struct link_ctl_info info;
  	int vals[2];		/* current values */
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
51
  	unsigned int flags;
9e226b4b7   Takashi Iwai   ALSA: vmaster - F...
52
  	struct snd_kcontrol *kctl; /* original kcontrol pointer */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
53
  	struct snd_kcontrol follower; /* the copy of original control entry */
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
54
  };
9ab0cb309   Takashi Iwai   ALSA: Replace the...
55
  static int follower_update(struct link_follower *follower)
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
56
57
58
  {
  	struct snd_ctl_elem_value *uctl;
  	int err, ch;
7a33a02ff   Takashi Iwai   ALSA: vmaster: Ze...
59
  	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
60
61
  	if (!uctl)
  		return -ENOMEM;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
62
63
  	uctl->id = follower->follower.id;
  	err = follower->follower.get(&follower->follower, uctl);
2e2c177ca   Takashi Iwai   ALSA: vmaster: Pr...
64
65
  	if (err < 0)
  		goto error;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
66
67
  	for (ch = 0; ch < follower->info.count; ch++)
  		follower->vals[ch] = uctl->value.integer.value[ch];
2e2c177ca   Takashi Iwai   ALSA: vmaster: Pr...
68
   error:
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
69
  	kfree(uctl);
2e2c177ca   Takashi Iwai   ALSA: vmaster: Pr...
70
  	return err < 0 ? err : 0;
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
71
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
72
73
  /* get the follower ctl info and save the initial values */
  static int follower_init(struct link_follower *follower)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
74
75
  {
  	struct snd_ctl_elem_info *uinfo;
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
76
  	int err;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
77

9ab0cb309   Takashi Iwai   ALSA: Replace the...
78
  	if (follower->info.count) {
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
79
  		/* already initialized */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
80
81
  		if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE)
  			return follower_update(follower);
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
82
83
  		return 0;
  	}
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
84
85
86
87
  
  	uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
  	if (!uinfo)
  		return -ENOMEM;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
88
89
  	uinfo->id = follower->follower.id;
  	err = follower->follower.info(&follower->follower, uinfo);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
90
91
92
93
  	if (err < 0) {
  		kfree(uinfo);
  		return err;
  	}
9ab0cb309   Takashi Iwai   ALSA: Replace the...
94
95
96
97
98
99
100
  	follower->info.type = uinfo->type;
  	follower->info.count = uinfo->count;
  	if (follower->info.count > 2  ||
  	    (follower->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
  	     follower->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
  		pr_err("ALSA: vmaster: invalid follower element
  ");
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
101
102
103
  		kfree(uinfo);
  		return -EINVAL;
  	}
9ab0cb309   Takashi Iwai   ALSA: Replace the...
104
105
  	follower->info.min_val = uinfo->value.integer.min;
  	follower->info.max_val = uinfo->value.integer.max;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
106
  	kfree(uinfo);
9ab0cb309   Takashi Iwai   ALSA: Replace the...
107
  	return follower_update(follower);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
108
109
110
111
112
  }
  
  /* initialize master volume */
  static int master_init(struct link_master *master)
  {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
113
  	struct link_follower *follower;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
114
115
116
  
  	if (master->info.count)
  		return 0; /* already initialized */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
117
118
  	list_for_each_entry(follower, &master->followers, list) {
  		int err = follower_init(follower);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
119
120
  		if (err < 0)
  			return err;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
121
  		master->info = follower->info;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
122
123
124
  		master->info.count = 1; /* always mono */
  		/* set full volume as default (= no attenuation) */
  		master->val = master->info.max_val;
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
125
126
127
  		if (master->hook)
  			master->hook(master->hook_private_data, master->val);
  		return 1;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
128
129
130
  	}
  	return -ENOENT;
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
131
132
  static int follower_get_val(struct link_follower *follower,
  			    struct snd_ctl_elem_value *ucontrol)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
133
134
  {
  	int err, ch;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
135
  	err = follower_init(follower);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
136
137
  	if (err < 0)
  		return err;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
138
139
  	for (ch = 0; ch < follower->info.count; ch++)
  		ucontrol->value.integer.value[ch] = follower->vals[ch];
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
140
141
  	return 0;
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
142
143
  static int follower_put_val(struct link_follower *follower,
  			    struct snd_ctl_elem_value *ucontrol)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
144
145
  {
  	int err, ch, vol;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
146
  	err = master_init(follower->master);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
147
148
  	if (err < 0)
  		return err;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
149
  	switch (follower->info.type) {
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
150
  	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
9ab0cb309   Takashi Iwai   ALSA: Replace the...
151
  		for (ch = 0; ch < follower->info.count; ch++)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
152
  			ucontrol->value.integer.value[ch] &=
9ab0cb309   Takashi Iwai   ALSA: Replace the...
153
  				!!follower->master->val;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
154
155
  		break;
  	case SNDRV_CTL_ELEM_TYPE_INTEGER:
9ab0cb309   Takashi Iwai   ALSA: Replace the...
156
  		for (ch = 0; ch < follower->info.count; ch++) {
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
157
158
  			/* max master volume is supposed to be 0 dB */
  			vol = ucontrol->value.integer.value[ch];
9ab0cb309   Takashi Iwai   ALSA: Replace the...
159
160
161
162
163
  			vol += follower->master->val - follower->master->info.max_val;
  			if (vol < follower->info.min_val)
  				vol = follower->info.min_val;
  			else if (vol > follower->info.max_val)
  				vol = follower->info.max_val;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
164
165
166
167
  			ucontrol->value.integer.value[ch] = vol;
  		}
  		break;
  	}
9ab0cb309   Takashi Iwai   ALSA: Replace the...
168
  	return follower->follower.put(&follower->follower, ucontrol);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
169
170
171
  }
  
  /*
9ab0cb309   Takashi Iwai   ALSA: Replace the...
172
   * ctl callbacks for followers
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
173
   */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
174
175
  static int follower_info(struct snd_kcontrol *kcontrol,
  			 struct snd_ctl_elem_info *uinfo)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
176
  {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
177
178
  	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
  	return follower->follower.info(&follower->follower, uinfo);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
179
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
180
181
  static int follower_get(struct snd_kcontrol *kcontrol,
  			struct snd_ctl_elem_value *ucontrol)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
182
  {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
183
184
  	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
  	return follower_get_val(follower, ucontrol);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
185
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
186
187
  static int follower_put(struct snd_kcontrol *kcontrol,
  			struct snd_ctl_elem_value *ucontrol)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
188
  {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
189
  	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
190
  	int err, ch, changed = 0;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
191
  	err = follower_init(follower);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
192
193
  	if (err < 0)
  		return err;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
194
195
  	for (ch = 0; ch < follower->info.count; ch++) {
  		if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
196
  			changed = 1;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
197
  			follower->vals[ch] = ucontrol->value.integer.value[ch];
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
198
199
200
201
  		}
  	}
  	if (!changed)
  		return 0;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
202
  	err = follower_put_val(follower, ucontrol);
2069d483b   Takashi Iwai   ALSA: vmaster: Fi...
203
204
205
  	if (err < 0)
  		return err;
  	return 1;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
206
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
207
208
209
  static int follower_tlv_cmd(struct snd_kcontrol *kcontrol,
  			    int op_flag, unsigned int size,
  			    unsigned int __user *tlv)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
210
  {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
211
  	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
212
  	/* FIXME: this assumes that the max volume is 0 dB */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
213
  	return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
214
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
215
  static void follower_free(struct snd_kcontrol *kcontrol)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
216
  {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
217
218
219
220
221
222
  	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
  	if (follower->follower.private_free)
  		follower->follower.private_free(&follower->follower);
  	if (follower->master)
  		list_del(&follower->list);
  	kfree(follower);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
223
224
225
  }
  
  /*
9ab0cb309   Takashi Iwai   ALSA: Replace the...
226
   * Add a follower control to the group with the given master control
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
227
   *
9ab0cb309   Takashi Iwai   ALSA: Replace the...
228
   * All followers must be the same type (returning the same information
25985edce   Lucas De Marchi   Fix common misspe...
229
   * via info callback).  The function doesn't check it, so it's your
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
230
231
232
233
234
235
236
   * responsibility.
   *
   * Also, some additional limitations:
   * - at most two channels
   * - logarithmic volume control (dB level), no linear volume
   * - master can only attenuate the volume, no gain
   */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
237
238
239
  int _snd_ctl_add_follower(struct snd_kcontrol *master,
  			  struct snd_kcontrol *follower,
  			  unsigned int flags)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
240
241
  {
  	struct link_master *master_link = snd_kcontrol_chip(master);
9ab0cb309   Takashi Iwai   ALSA: Replace the...
242
  	struct link_follower *srec;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
243

9ab0cb309   Takashi Iwai   ALSA: Replace the...
244
  	srec = kzalloc(struct_size(srec, follower.vd, follower->count),
acafe7e30   Kees Cook   treewide: Use str...
245
  		       GFP_KERNEL);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
246
247
  	if (!srec)
  		return -ENOMEM;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
248
249
250
  	srec->kctl = follower;
  	srec->follower = *follower;
  	memcpy(srec->follower.vd, follower->vd, follower->count * sizeof(*follower->vd));
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
251
  	srec->master = master_link;
f5b1db634   Takashi Iwai   ALSA: add snd_ctl...
252
  	srec->flags = flags;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
253
254
  
  	/* override callbacks */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
255
256
257
258
259
260
261
262
263
  	follower->info = follower_info;
  	follower->get = follower_get;
  	follower->put = follower_put;
  	if (follower->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
  		follower->tlv.c = follower_tlv_cmd;
  	follower->private_data = srec;
  	follower->private_free = follower_free;
  
  	list_add_tail(&srec->list, &master_link->followers);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
264
265
  	return 0;
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
266
  EXPORT_SYMBOL(_snd_ctl_add_follower);
e922b0028   Takashi Iwai   [ALSA] Move vmast...
267

3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  /*
   * ctl callbacks for master controls
   */
  static int master_info(struct snd_kcontrol *kcontrol,
  		      struct snd_ctl_elem_info *uinfo)
  {
  	struct link_master *master = snd_kcontrol_chip(kcontrol);
  	int ret;
  
  	ret = master_init(master);
  	if (ret < 0)
  		return ret;
  	uinfo->type = master->info.type;
  	uinfo->count = master->info.count;
  	uinfo->value.integer.min = master->info.min_val;
  	uinfo->value.integer.max = master->info.max_val;
  	return 0;
  }
  
  static int master_get(struct snd_kcontrol *kcontrol,
  		      struct snd_ctl_elem_value *ucontrol)
  {
  	struct link_master *master = snd_kcontrol_chip(kcontrol);
  	int err = master_init(master);
  	if (err < 0)
  		return err;
  	ucontrol->value.integer.value[0] = master->val;
  	return 0;
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
297
  static int sync_followers(struct link_master *master, int old_val, int new_val)
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
298
  {
9ab0cb309   Takashi Iwai   ALSA: Replace the...
299
  	struct link_follower *follower;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
300
  	struct snd_ctl_elem_value *uval;
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
301
302
303
304
  
  	uval = kmalloc(sizeof(*uval), GFP_KERNEL);
  	if (!uval)
  		return -ENOMEM;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
305
  	list_for_each_entry(follower, &master->followers, list) {
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
306
  		master->val = old_val;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
307
308
  		uval->id = follower->follower.id;
  		follower_get_val(follower, uval);
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
309
  		master->val = new_val;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
310
  		follower_put_val(follower, uval);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
311
312
  	}
  	kfree(uval);
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  	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;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
331
  	err = sync_followers(master, old_val, new_val);
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
332
333
  	if (err < 0)
  		return err;
1ba65ae4b   Takashi Iwai   ALSA: vmaster: Fi...
334
  	if (master->hook && !first_init)
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
335
  		master->hook(master->hook_private_data, master->val);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
336
337
338
339
340
341
  	return 1;
  }
  
  static void master_free(struct snd_kcontrol *kcontrol)
  {
  	struct link_master *master = snd_kcontrol_chip(kcontrol);
9ab0cb309   Takashi Iwai   ALSA: Replace the...
342
  	struct link_follower *follower, *n;
9e226b4b7   Takashi Iwai   ALSA: vmaster - F...
343

9ab0cb309   Takashi Iwai   ALSA: Replace the...
344
345
346
  	/* free all follower links and retore the original follower kctls */
  	list_for_each_entry_safe(follower, n, &master->followers, list) {
  		struct snd_kcontrol *sctl = follower->kctl;
9e226b4b7   Takashi Iwai   ALSA: vmaster - F...
347
  		struct list_head olist = sctl->list;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
348
349
  		memcpy(sctl, &follower->follower, sizeof(*sctl));
  		memcpy(sctl->vd, follower->follower.vd,
9e226b4b7   Takashi Iwai   ALSA: vmaster - F...
350
351
  		       sctl->count * sizeof(*sctl->vd));
  		sctl->list = olist; /* keep the current linked-list */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
352
  		kfree(follower);
9e226b4b7   Takashi Iwai   ALSA: vmaster - F...
353
  	}
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
354
355
  	kfree(master);
  }
79c7cdd54   Takashi Iwai   ALSA: Add kernel-...
356
357
358
359
360
  /**
   * snd_ctl_make_virtual_master - Create a virtual master control
   * @name: name string of the control element to create
   * @tlv: optional TLV int array for dB information
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
361
   * Creates a virtual master control with the given name string.
79c7cdd54   Takashi Iwai   ALSA: Add kernel-...
362
   *
9ab0cb309   Takashi Iwai   ALSA: Replace the...
363
364
   * After creating a vmaster element, you can add the follower controls
   * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached().
79c7cdd54   Takashi Iwai   ALSA: Add kernel-...
365
366
367
   *
   * The optional argument @tlv can be used to specify the TLV information
   * for dB scale of the master control.  It should be a single element
085f30654   Takashi Iwai   ALSA: Add new TLV...
368
369
   * 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.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
370
371
   *
   * Return: The created control element, or %NULL for errors (ENOMEM).
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
   */
  struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
  						 const unsigned int *tlv)
  {
  	struct link_master *master;
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_new knew;
  
  	memset(&knew, 0, sizeof(knew));
  	knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
  	knew.name = name;
  	knew.info = master_info;
  
  	master = kzalloc(sizeof(*master), GFP_KERNEL);
  	if (!master)
  		return NULL;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
388
  	INIT_LIST_HEAD(&master->followers);
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
389
390
391
392
393
394
395
396
397
398
399
400
401
  
  	kctl = snd_ctl_new1(&knew, master);
  	if (!kctl) {
  		kfree(master);
  		return NULL;
  	}
  	/* override some callbacks */
  	kctl->info = master_info;
  	kctl->get = master_get;
  	kctl->put = master_put;
  	kctl->private_free = master_free;
  
  	/* additional (constant) TLV read */
841bdb7c0   Takashi Sakamoto   ALSA: vmaster: us...
402
403
404
405
406
407
408
409
410
  	if (tlv) {
  		unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
  		if (type == SNDRV_CTL_TLVT_DB_SCALE ||
  		    type == SNDRV_CTL_TLVT_DB_MINMAX ||
  		    type == SNDRV_CTL_TLVT_DB_MINMAX_MUTE) {
  			kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
  			memcpy(master->tlv, tlv, sizeof(master->tlv));
  			kctl->tlv.p = master->tlv;
  		}
3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
411
  	}
1c82ed1bc   Takashi Iwai   [ALSA] Keep priva...
412

3b0a5f22d   Takashi Iwai   [ALSA] Add virtua...
413
414
  	return kctl;
  }
e922b0028   Takashi Iwai   [ALSA] Move vmast...
415
  EXPORT_SYMBOL(snd_ctl_make_virtual_master);
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
416
417
418
419
420
  
  /**
   * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
   * @kcontrol: vmaster kctl element
   * @hook: the hook function
f2ec52d4c   Randy Dunlap   ALSA: fix core/vm...
421
   * @private_data: the private_data pointer to be saved
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
422
423
424
   *
   * Adds the given hook to the vmaster control element so that it's called
   * at each time when the value is changed.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
425
426
   *
   * Return: Zero.
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
427
428
429
430
431
432
433
434
435
436
437
438
439
   */
  int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
  			     void (*hook)(void *private_data, int),
  			     void *private_data)
  {
  	struct link_master *master = snd_kcontrol_chip(kcontrol);
  	master->hook = hook;
  	master->hook_private_data = private_data;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
  
  /**
9ab0cb309   Takashi Iwai   ALSA: Replace the...
440
   * snd_ctl_sync_vmaster - Sync the vmaster followers and hook
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
441
   * @kcontrol: vmaster kctl element
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
442
   * @hook_only: sync only the hook
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
443
   *
9ab0cb309   Takashi Iwai   ALSA: Replace the...
444
   * Forcibly call the put callback of each follower and call the hook function
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
445
446
   * to synchronize with the current value of the given vmaster element.
   * NOP when NULL is passed to @kcontrol.
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
447
   */
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
448
  void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
449
450
  {
  	struct link_master *master;
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
451
  	bool first_init = false;
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
452
453
454
  	if (!kcontrol)
  		return;
  	master = snd_kcontrol_chip(kcontrol);
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
455
456
457
458
459
  	if (!hook_only) {
  		int err = master_init(master);
  		if (err < 0)
  			return;
  		first_init = err;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
460
  		err = sync_followers(master, master->val, master->val);
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
461
462
463
464
465
  		if (err < 0)
  			return;
  	}
  
  	if (master->hook && !first_init)
2ad787e9a   Takashi Iwai   ALSA: Add a hook ...
466
467
  		master->hook(master->hook_private_data, master->val);
  }
1ca2f2ec9   Takashi Iwai   ALSA: vmaster: Ad...
468
  EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
a91d66129   Takashi Iwai   ALSA: hda - Fix i...
469
470
  
  /**
9ab0cb309   Takashi Iwai   ALSA: Replace the...
471
   * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower
a91d66129   Takashi Iwai   ALSA: hda - Fix i...
472
473
474
475
   * @kctl: vmaster kctl element
   * @func: function to apply
   * @arg: optional function argument
   *
9ab0cb309   Takashi Iwai   ALSA: Replace the...
476
   * Apply the function @func to each follower kctl of the given vmaster kctl.
a91d66129   Takashi Iwai   ALSA: hda - Fix i...
477
478
   * Returns 0 if successful, or a negative error code.
   */
9ab0cb309   Takashi Iwai   ALSA: Replace the...
479
480
481
482
483
  int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
  				    int (*func)(struct snd_kcontrol *vfollower,
  						struct snd_kcontrol *follower,
  						void *arg),
  				    void *arg)
a91d66129   Takashi Iwai   ALSA: hda - Fix i...
484
485
  {
  	struct link_master *master;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
486
  	struct link_follower *follower;
a91d66129   Takashi Iwai   ALSA: hda - Fix i...
487
488
489
490
491
492
  	int err;
  
  	master = snd_kcontrol_chip(kctl);
  	err = master_init(master);
  	if (err < 0)
  		return err;
9ab0cb309   Takashi Iwai   ALSA: Replace the...
493
494
  	list_for_each_entry(follower, &master->followers, list) {
  		err = func(follower->kctl, &follower->follower, arg);
a91d66129   Takashi Iwai   ALSA: hda - Fix i...
495
496
497
498
499
500
  		if (err < 0)
  			return err;
  	}
  
  	return 0;
  }
9ab0cb309   Takashi Iwai   ALSA: Replace the...
501
  EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);