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