Blame view
sound/core/vmaster.c
14 KB
a10e763b8 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
3b0a5f22d [ALSA] Add virtua... |
2 |
/* |
9ab0cb309 ALSA: Replace the... |
3 |
* Virtual master and follower controls |
3b0a5f22d [ALSA] Add virtua... |
4 5 |
* * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de> |
3b0a5f22d [ALSA] Add virtua... |
6 7 8 |
*/ #include <linux/slab.h> |
d81a6d717 sound: Add export... |
9 |
#include <linux/export.h> |
3b0a5f22d [ALSA] Add virtua... |
10 11 |
#include <sound/core.h> #include <sound/control.h> |
1c82ed1bc [ALSA] Keep priva... |
12 |
#include <sound/tlv.h> |
3b0a5f22d [ALSA] Add virtua... |
13 14 15 16 17 |
/* * a subset of information returned via ctl info callback */ struct link_ctl_info { |
fea952e5c ALSA: core: spars... |
18 |
snd_ctl_elem_type_t type; /* value type */ |
3b0a5f22d [ALSA] Add virtua... |
19 20 21 22 23 |
int count; /* item count */ int min_val, max_val; /* min, max values */ }; /* |
9ab0cb309 ALSA: Replace the... |
24 |
* link master - this contains a list of follower controls that are |
3b0a5f22d [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 ALSA: Replace the... |
29 |
* The same value will be applied to all followers. |
3b0a5f22d [ALSA] Add virtua... |
30 31 |
*/ struct link_master { |
9ab0cb309 ALSA: Replace the... |
32 |
struct list_head followers; |
3b0a5f22d [ALSA] Add virtua... |
33 34 |
struct link_ctl_info info; int val; /* the master value */ |
1c82ed1bc [ALSA] Keep priva... |
35 |
unsigned int tlv[4]; |
2ad787e9a ALSA: Add a hook ... |
36 37 |
void (*hook)(void *private_data, int); void *hook_private_data; |
3b0a5f22d [ALSA] Add virtua... |
38 39 40 |
}; /* |
9ab0cb309 ALSA: Replace the... |
41 |
* link follower - this contains a follower control element |
3b0a5f22d [ALSA] Add virtua... |
42 |
* |
9ab0cb309 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 [ALSA] Add virtua... |
45 |
*/ |
9ab0cb309 ALSA: Replace the... |
46 |
struct link_follower { |
3b0a5f22d [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 ALSA: add snd_ctl... |
51 |
unsigned int flags; |
9e226b4b7 ALSA: vmaster - F... |
52 |
struct snd_kcontrol *kctl; /* original kcontrol pointer */ |
9ab0cb309 ALSA: Replace the... |
53 |
struct snd_kcontrol follower; /* the copy of original control entry */ |
3b0a5f22d [ALSA] Add virtua... |
54 |
}; |
9ab0cb309 ALSA: Replace the... |
55 |
static int follower_update(struct link_follower *follower) |
f5b1db634 ALSA: add snd_ctl... |
56 57 58 |
{ struct snd_ctl_elem_value *uctl; int err, ch; |
7a33a02ff ALSA: vmaster: Ze... |
59 |
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); |
f5b1db634 ALSA: add snd_ctl... |
60 61 |
if (!uctl) return -ENOMEM; |
9ab0cb309 ALSA: Replace the... |
62 63 |
uctl->id = follower->follower.id; err = follower->follower.get(&follower->follower, uctl); |
2e2c177ca ALSA: vmaster: Pr... |
64 65 |
if (err < 0) goto error; |
9ab0cb309 ALSA: Replace the... |
66 67 |
for (ch = 0; ch < follower->info.count; ch++) follower->vals[ch] = uctl->value.integer.value[ch]; |
2e2c177ca ALSA: vmaster: Pr... |
68 |
error: |
f5b1db634 ALSA: add snd_ctl... |
69 |
kfree(uctl); |
2e2c177ca ALSA: vmaster: Pr... |
70 |
return err < 0 ? err : 0; |
f5b1db634 ALSA: add snd_ctl... |
71 |
} |
9ab0cb309 ALSA: Replace the... |
72 73 |
/* get the follower ctl info and save the initial values */ static int follower_init(struct link_follower *follower) |
3b0a5f22d [ALSA] Add virtua... |
74 75 |
{ struct snd_ctl_elem_info *uinfo; |
f5b1db634 ALSA: add snd_ctl... |
76 |
int err; |
3b0a5f22d [ALSA] Add virtua... |
77 |
|
9ab0cb309 ALSA: Replace the... |
78 |
if (follower->info.count) { |
f5b1db634 ALSA: add snd_ctl... |
79 |
/* already initialized */ |
9ab0cb309 ALSA: Replace the... |
80 81 |
if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE) return follower_update(follower); |
f5b1db634 ALSA: add snd_ctl... |
82 83 |
return 0; } |
3b0a5f22d [ALSA] Add virtua... |
84 85 86 87 |
uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); if (!uinfo) return -ENOMEM; |
9ab0cb309 ALSA: Replace the... |
88 89 |
uinfo->id = follower->follower.id; err = follower->follower.info(&follower->follower, uinfo); |
3b0a5f22d [ALSA] Add virtua... |
90 91 92 93 |
if (err < 0) { kfree(uinfo); return err; } |
9ab0cb309 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 [ALSA] Add virtua... |
101 102 103 |
kfree(uinfo); return -EINVAL; } |
9ab0cb309 ALSA: Replace the... |
104 105 |
follower->info.min_val = uinfo->value.integer.min; follower->info.max_val = uinfo->value.integer.max; |
3b0a5f22d [ALSA] Add virtua... |
106 |
kfree(uinfo); |
9ab0cb309 ALSA: Replace the... |
107 |
return follower_update(follower); |
3b0a5f22d [ALSA] Add virtua... |
108 109 110 111 112 |
} /* initialize master volume */ static int master_init(struct link_master *master) { |
9ab0cb309 ALSA: Replace the... |
113 |
struct link_follower *follower; |
3b0a5f22d [ALSA] Add virtua... |
114 115 116 |
if (master->info.count) return 0; /* already initialized */ |
9ab0cb309 ALSA: Replace the... |
117 118 |
list_for_each_entry(follower, &master->followers, list) { int err = follower_init(follower); |
3b0a5f22d [ALSA] Add virtua... |
119 120 |
if (err < 0) return err; |
9ab0cb309 ALSA: Replace the... |
121 |
master->info = follower->info; |
3b0a5f22d [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 ALSA: Add a hook ... |
125 126 127 |
if (master->hook) master->hook(master->hook_private_data, master->val); return 1; |
3b0a5f22d [ALSA] Add virtua... |
128 129 130 |
} return -ENOENT; } |
9ab0cb309 ALSA: Replace the... |
131 132 |
static int follower_get_val(struct link_follower *follower, struct snd_ctl_elem_value *ucontrol) |
3b0a5f22d [ALSA] Add virtua... |
133 134 |
{ int err, ch; |
9ab0cb309 ALSA: Replace the... |
135 |
err = follower_init(follower); |
3b0a5f22d [ALSA] Add virtua... |
136 137 |
if (err < 0) return err; |
9ab0cb309 ALSA: Replace the... |
138 139 |
for (ch = 0; ch < follower->info.count; ch++) ucontrol->value.integer.value[ch] = follower->vals[ch]; |
3b0a5f22d [ALSA] Add virtua... |
140 141 |
return 0; } |
9ab0cb309 ALSA: Replace the... |
142 143 |
static int follower_put_val(struct link_follower *follower, struct snd_ctl_elem_value *ucontrol) |
3b0a5f22d [ALSA] Add virtua... |
144 145 |
{ int err, ch, vol; |
9ab0cb309 ALSA: Replace the... |
146 |
err = master_init(follower->master); |
3b0a5f22d [ALSA] Add virtua... |
147 148 |
if (err < 0) return err; |
9ab0cb309 ALSA: Replace the... |
149 |
switch (follower->info.type) { |
3b0a5f22d [ALSA] Add virtua... |
150 |
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: |
9ab0cb309 ALSA: Replace the... |
151 |
for (ch = 0; ch < follower->info.count; ch++) |
3b0a5f22d [ALSA] Add virtua... |
152 |
ucontrol->value.integer.value[ch] &= |
9ab0cb309 ALSA: Replace the... |
153 |
!!follower->master->val; |
3b0a5f22d [ALSA] Add virtua... |
154 155 |
break; case SNDRV_CTL_ELEM_TYPE_INTEGER: |
9ab0cb309 ALSA: Replace the... |
156 |
for (ch = 0; ch < follower->info.count; ch++) { |
3b0a5f22d [ALSA] Add virtua... |
157 158 |
/* max master volume is supposed to be 0 dB */ vol = ucontrol->value.integer.value[ch]; |
9ab0cb309 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 [ALSA] Add virtua... |
164 165 166 167 |
ucontrol->value.integer.value[ch] = vol; } break; } |
9ab0cb309 ALSA: Replace the... |
168 |
return follower->follower.put(&follower->follower, ucontrol); |
3b0a5f22d [ALSA] Add virtua... |
169 170 171 |
} /* |
9ab0cb309 ALSA: Replace the... |
172 |
* ctl callbacks for followers |
3b0a5f22d [ALSA] Add virtua... |
173 |
*/ |
9ab0cb309 ALSA: Replace the... |
174 175 |
static int follower_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
3b0a5f22d [ALSA] Add virtua... |
176 |
{ |
9ab0cb309 ALSA: Replace the... |
177 178 |
struct link_follower *follower = snd_kcontrol_chip(kcontrol); return follower->follower.info(&follower->follower, uinfo); |
3b0a5f22d [ALSA] Add virtua... |
179 |
} |
9ab0cb309 ALSA: Replace the... |
180 181 |
static int follower_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
3b0a5f22d [ALSA] Add virtua... |
182 |
{ |
9ab0cb309 ALSA: Replace the... |
183 184 |
struct link_follower *follower = snd_kcontrol_chip(kcontrol); return follower_get_val(follower, ucontrol); |
3b0a5f22d [ALSA] Add virtua... |
185 |
} |
9ab0cb309 ALSA: Replace the... |
186 187 |
static int follower_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
3b0a5f22d [ALSA] Add virtua... |
188 |
{ |
9ab0cb309 ALSA: Replace the... |
189 |
struct link_follower *follower = snd_kcontrol_chip(kcontrol); |
3b0a5f22d [ALSA] Add virtua... |
190 |
int err, ch, changed = 0; |
9ab0cb309 ALSA: Replace the... |
191 |
err = follower_init(follower); |
3b0a5f22d [ALSA] Add virtua... |
192 193 |
if (err < 0) return err; |
9ab0cb309 ALSA: Replace the... |
194 195 |
for (ch = 0; ch < follower->info.count; ch++) { if (follower->vals[ch] != ucontrol->value.integer.value[ch]) { |
3b0a5f22d [ALSA] Add virtua... |
196 |
changed = 1; |
9ab0cb309 ALSA: Replace the... |
197 |
follower->vals[ch] = ucontrol->value.integer.value[ch]; |
3b0a5f22d [ALSA] Add virtua... |
198 199 200 201 |
} } if (!changed) return 0; |
9ab0cb309 ALSA: Replace the... |
202 |
err = follower_put_val(follower, ucontrol); |
2069d483b ALSA: vmaster: Fi... |
203 204 205 |
if (err < 0) return err; return 1; |
3b0a5f22d [ALSA] Add virtua... |
206 |
} |
9ab0cb309 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 [ALSA] Add virtua... |
210 |
{ |
9ab0cb309 ALSA: Replace the... |
211 |
struct link_follower *follower = snd_kcontrol_chip(kcontrol); |
3b0a5f22d [ALSA] Add virtua... |
212 |
/* FIXME: this assumes that the max volume is 0 dB */ |
9ab0cb309 ALSA: Replace the... |
213 |
return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv); |
3b0a5f22d [ALSA] Add virtua... |
214 |
} |
9ab0cb309 ALSA: Replace the... |
215 |
static void follower_free(struct snd_kcontrol *kcontrol) |
3b0a5f22d [ALSA] Add virtua... |
216 |
{ |
9ab0cb309 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 [ALSA] Add virtua... |
223 224 225 |
} /* |
9ab0cb309 ALSA: Replace the... |
226 |
* Add a follower control to the group with the given master control |
3b0a5f22d [ALSA] Add virtua... |
227 |
* |
9ab0cb309 ALSA: Replace the... |
228 |
* All followers must be the same type (returning the same information |
25985edce Fix common misspe... |
229 |
* via info callback). The function doesn't check it, so it's your |
3b0a5f22d [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 ALSA: Replace the... |
237 238 239 |
int _snd_ctl_add_follower(struct snd_kcontrol *master, struct snd_kcontrol *follower, unsigned int flags) |
3b0a5f22d [ALSA] Add virtua... |
240 241 |
{ struct link_master *master_link = snd_kcontrol_chip(master); |
9ab0cb309 ALSA: Replace the... |
242 |
struct link_follower *srec; |
3b0a5f22d [ALSA] Add virtua... |
243 |
|
9ab0cb309 ALSA: Replace the... |
244 |
srec = kzalloc(struct_size(srec, follower.vd, follower->count), |
acafe7e30 treewide: Use str... |
245 |
GFP_KERNEL); |
3b0a5f22d [ALSA] Add virtua... |
246 247 |
if (!srec) return -ENOMEM; |
9ab0cb309 ALSA: Replace the... |
248 249 250 |
srec->kctl = follower; srec->follower = *follower; memcpy(srec->follower.vd, follower->vd, follower->count * sizeof(*follower->vd)); |
3b0a5f22d [ALSA] Add virtua... |
251 |
srec->master = master_link; |
f5b1db634 ALSA: add snd_ctl... |
252 |
srec->flags = flags; |
3b0a5f22d [ALSA] Add virtua... |
253 254 |
/* override callbacks */ |
9ab0cb309 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 [ALSA] Add virtua... |
264 265 |
return 0; } |
9ab0cb309 ALSA: Replace the... |
266 |
EXPORT_SYMBOL(_snd_ctl_add_follower); |
e922b0028 [ALSA] Move vmast... |
267 |
|
3b0a5f22d [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 ALSA: Replace the... |
297 |
static int sync_followers(struct link_master *master, int old_val, int new_val) |
3b0a5f22d [ALSA] Add virtua... |
298 |
{ |
9ab0cb309 ALSA: Replace the... |
299 |
struct link_follower *follower; |
3b0a5f22d [ALSA] Add virtua... |
300 |
struct snd_ctl_elem_value *uval; |
3b0a5f22d [ALSA] Add virtua... |
301 302 303 304 |
uval = kmalloc(sizeof(*uval), GFP_KERNEL); if (!uval) return -ENOMEM; |
9ab0cb309 ALSA: Replace the... |
305 |
list_for_each_entry(follower, &master->followers, list) { |
3b0a5f22d [ALSA] Add virtua... |
306 |
master->val = old_val; |
9ab0cb309 ALSA: Replace the... |
307 308 |
uval->id = follower->follower.id; follower_get_val(follower, uval); |
1ca2f2ec9 ALSA: vmaster: Ad... |
309 |
master->val = new_val; |
9ab0cb309 ALSA: Replace the... |
310 |
follower_put_val(follower, uval); |
3b0a5f22d [ALSA] Add virtua... |
311 312 |
} kfree(uval); |
1ca2f2ec9 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 ALSA: Replace the... |
331 |
err = sync_followers(master, old_val, new_val); |
1ca2f2ec9 ALSA: vmaster: Ad... |
332 333 |
if (err < 0) return err; |
1ba65ae4b ALSA: vmaster: Fi... |
334 |
if (master->hook && !first_init) |
2ad787e9a ALSA: Add a hook ... |
335 |
master->hook(master->hook_private_data, master->val); |
3b0a5f22d [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 ALSA: Replace the... |
342 |
struct link_follower *follower, *n; |
9e226b4b7 ALSA: vmaster - F... |
343 |
|
9ab0cb309 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 ALSA: vmaster - F... |
347 |
struct list_head olist = sctl->list; |
9ab0cb309 ALSA: Replace the... |
348 349 |
memcpy(sctl, &follower->follower, sizeof(*sctl)); memcpy(sctl->vd, follower->follower.vd, |
9e226b4b7 ALSA: vmaster - F... |
350 351 |
sctl->count * sizeof(*sctl->vd)); sctl->list = olist; /* keep the current linked-list */ |
9ab0cb309 ALSA: Replace the... |
352 |
kfree(follower); |
9e226b4b7 ALSA: vmaster - F... |
353 |
} |
3b0a5f22d [ALSA] Add virtua... |
354 355 |
kfree(master); } |
79c7cdd54 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 ALSA: add/change ... |
361 |
* Creates a virtual master control with the given name string. |
79c7cdd54 ALSA: Add kernel-... |
362 |
* |
9ab0cb309 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 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 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 ALSA: add/change ... |
370 371 |
* * Return: The created control element, or %NULL for errors (ENOMEM). |
3b0a5f22d [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 ALSA: Replace the... |
388 |
INIT_LIST_HEAD(&master->followers); |
3b0a5f22d [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 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 [ALSA] Add virtua... |
411 |
} |
1c82ed1bc [ALSA] Keep priva... |
412 |
|
3b0a5f22d [ALSA] Add virtua... |
413 414 |
return kctl; } |
e922b0028 [ALSA] Move vmast... |
415 |
EXPORT_SYMBOL(snd_ctl_make_virtual_master); |
2ad787e9a 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 ALSA: fix core/vm... |
421 |
* @private_data: the private_data pointer to be saved |
2ad787e9a 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 ALSA: add/change ... |
425 426 |
* * Return: Zero. |
2ad787e9a 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 ALSA: Replace the... |
440 |
* snd_ctl_sync_vmaster - Sync the vmaster followers and hook |
2ad787e9a ALSA: Add a hook ... |
441 |
* @kcontrol: vmaster kctl element |
1ca2f2ec9 ALSA: vmaster: Ad... |
442 |
* @hook_only: sync only the hook |
2ad787e9a ALSA: Add a hook ... |
443 |
* |
9ab0cb309 ALSA: Replace the... |
444 |
* Forcibly call the put callback of each follower and call the hook function |
1ca2f2ec9 ALSA: vmaster: Ad... |
445 446 |
* to synchronize with the current value of the given vmaster element. * NOP when NULL is passed to @kcontrol. |
2ad787e9a ALSA: Add a hook ... |
447 |
*/ |
1ca2f2ec9 ALSA: vmaster: Ad... |
448 |
void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only) |
2ad787e9a ALSA: Add a hook ... |
449 450 |
{ struct link_master *master; |
1ca2f2ec9 ALSA: vmaster: Ad... |
451 |
bool first_init = false; |
2ad787e9a ALSA: Add a hook ... |
452 453 454 |
if (!kcontrol) return; master = snd_kcontrol_chip(kcontrol); |
1ca2f2ec9 ALSA: vmaster: Ad... |
455 456 457 458 459 |
if (!hook_only) { int err = master_init(master); if (err < 0) return; first_init = err; |
9ab0cb309 ALSA: Replace the... |
460 |
err = sync_followers(master, master->val, master->val); |
1ca2f2ec9 ALSA: vmaster: Ad... |
461 462 463 464 465 |
if (err < 0) return; } if (master->hook && !first_init) |
2ad787e9a ALSA: Add a hook ... |
466 467 |
master->hook(master->hook_private_data, master->val); } |
1ca2f2ec9 ALSA: vmaster: Ad... |
468 |
EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster); |
a91d66129 ALSA: hda - Fix i... |
469 470 |
/** |
9ab0cb309 ALSA: Replace the... |
471 |
* snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower |
a91d66129 ALSA: hda - Fix i... |
472 473 474 475 |
* @kctl: vmaster kctl element * @func: function to apply * @arg: optional function argument * |
9ab0cb309 ALSA: Replace the... |
476 |
* Apply the function @func to each follower kctl of the given vmaster kctl. |
a91d66129 ALSA: hda - Fix i... |
477 478 |
* Returns 0 if successful, or a negative error code. */ |
9ab0cb309 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 ALSA: hda - Fix i... |
484 485 |
{ struct link_master *master; |
9ab0cb309 ALSA: Replace the... |
486 |
struct link_follower *follower; |
a91d66129 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 ALSA: Replace the... |
493 494 |
list_for_each_entry(follower, &master->followers, list) { err = func(follower->kctl, &follower->follower, arg); |
a91d66129 ALSA: hda - Fix i... |
495 496 497 498 499 500 |
if (err < 0) return err; } return 0; } |
9ab0cb309 ALSA: Replace the... |
501 |
EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers); |