vmaster.c 14.0 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
 * Virtual master and follower controls
4 5 6 7 8
 *
 *  Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
 */

#include <linux/slab.h>
9
#include <linux/export.h>
10 11
#include <sound/core.h>
#include <sound/control.h>
12
#include <sound/tlv.h>
13 14 15 16 17

/*
 * a subset of information returned via ctl info callback
 */
struct link_ctl_info {
C
Clemens Ladisch 已提交
18
	snd_ctl_elem_type_t type; /* value type */
19 20 21 22 23
	int count;		/* item count */
	int min_val, max_val;	/* min, max values */
};

/*
24
 * link master - this contains a list of follower controls that are
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.
29
 * The same value will be applied to all followers.
30 31
 */
struct link_master {
32
	struct list_head followers;
33 34
	struct link_ctl_info info;
	int val;		/* the master value */
35
	unsigned int tlv[4];
36 37
	void (*hook)(void *private_data, int);
	void *hook_private_data;
38 39 40
};

/*
41
 * link follower - this contains a follower control element
42
 *
43 44
 * It fakes the control callbacks with additional attenuation by the
 * master control.  A follower may have either one or two channels.
45 46
 */

47
struct link_follower {
48 49 50 51
	struct list_head list;
	struct link_master *master;
	struct link_ctl_info info;
	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
};

57
static int follower_update(struct link_follower *follower)
58 59 60 61
{
	struct snd_ctl_elem_value *uctl;
	int err, ch;

62
	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
63 64
	if (!uctl)
		return -ENOMEM;
65 66
	uctl->id = follower->follower.id;
	err = follower->follower.get(&follower->follower, uctl);
67 68
	if (err < 0)
		goto error;
69 70
	for (ch = 0; ch < follower->info.count; ch++)
		follower->vals[ch] = uctl->value.integer.value[ch];
71
 error:
72
	kfree(uctl);
73
	return err < 0 ? err : 0;
74 75
}

76 77
/* get the follower ctl info and save the initial values */
static 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 85
		if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE)
			return follower_update(follower);
86 87
		return 0;
	}
88 89 90 91

	uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
	if (!uinfo)
		return -ENOMEM;
92 93
	uinfo->id = follower->follower.id;
	err = follower->follower.info(&follower->follower, uinfo);
94 95 96 97
	if (err < 0) {
		kfree(uinfo);
		return err;
	}
98 99 100 101 102 103
	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\n");
104 105 106
		kfree(uinfo);
		return -EINVAL;
	}
107 108
	follower->info.min_val = uinfo->value.integer.min;
	follower->info.max_val = uinfo->value.integer.max;
109 110
	kfree(uinfo);

111
	return follower_update(follower);
112 113 114 115 116
}

/* initialize master volume */
static int master_init(struct link_master *master)
{
117
	struct link_follower *follower;
118 119 120 121

	if (master->info.count)
		return 0; /* already initialized */

122 123
	list_for_each_entry(follower, &master->followers, list) {
		int err = follower_init(follower);
124 125
		if (err < 0)
			return err;
126
		master->info = follower->info;
127 128 129
		master->info.count = 1; /* always mono */
		/* set full volume as default (= no attenuation) */
		master->val = master->info.max_val;
130 131 132
		if (master->hook)
			master->hook(master->hook_private_data, master->val);
		return 1;
133 134 135 136
	}
	return -ENOENT;
}

137 138
static int follower_get_val(struct link_follower *follower,
			    struct snd_ctl_elem_value *ucontrol)
139 140 141
{
	int err, ch;

142
	err = follower_init(follower);
143 144
	if (err < 0)
		return err;
145 146
	for (ch = 0; ch < follower->info.count; ch++)
		ucontrol->value.integer.value[ch] = follower->vals[ch];
147 148 149
	return 0;
}

150 151
static int follower_put_val(struct link_follower *follower,
			    struct snd_ctl_elem_value *ucontrol)
152 153 154
{
	int err, ch, vol;

155
	err = master_init(follower->master);
156 157 158
	if (err < 0)
		return err;

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 165
		break;
	case SNDRV_CTL_ELEM_TYPE_INTEGER:
166
		for (ch = 0; ch < follower->info.count; ch++) {
167 168
			/* max master volume is supposed to be 0 dB */
			vol = ucontrol->value.integer.value[ch];
169 170 171 172 173
			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;
174 175 176 177
			ucontrol->value.integer.value[ch] = vol;
		}
		break;
	}
178
	return follower->follower.put(&follower->follower, ucontrol);
179 180 181
}

/*
182
 * ctl callbacks for followers
183
 */
184 185
static int follower_info(struct snd_kcontrol *kcontrol,
			 struct snd_ctl_elem_info *uinfo)
186
{
187 188
	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
	return follower->follower.info(&follower->follower, uinfo);
189 190
}

191 192
static int follower_get(struct snd_kcontrol *kcontrol,
			struct snd_ctl_elem_value *ucontrol)
193
{
194 195
	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
	return follower_get_val(follower, ucontrol);
196 197
}

198 199
static int follower_put(struct snd_kcontrol *kcontrol,
			struct snd_ctl_elem_value *ucontrol)
200
{
201
	struct link_follower *follower = snd_kcontrol_chip(kcontrol);
202 203
	int err, ch, changed = 0;

204
	err = follower_init(follower);
205 206
	if (err < 0)
		return err;
207 208
	for (ch = 0; ch < follower->info.count; ch++) {
		if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
209
			changed = 1;
210
			follower->vals[ch] = ucontrol->value.integer.value[ch];
211 212 213 214
		}
	}
	if (!changed)
		return 0;
215
	err = follower_put_val(follower, ucontrol);
216 217 218
	if (err < 0)
		return err;
	return 1;
219 220
}

221 222 223
static int follower_tlv_cmd(struct snd_kcontrol *kcontrol,
			    int op_flag, unsigned int size,
			    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
}

230
static void follower_free(struct snd_kcontrol *kcontrol)
231
{
232 233 234 235 236 237
	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);
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
L
Lucas De Marchi 已提交
244
 * via info callback).  The function doesn't check it, so it's your
245 246 247 248 249 250 251
 * 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
 */
252 253 254
int _snd_ctl_add_follower(struct snd_kcontrol *master,
			  struct snd_kcontrol *follower,
			  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 262
	if (!srec)
		return -ENOMEM;
263 264 265
	srec->kctl = follower;
	srec->follower = *follower;
	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 271 272 273 274 275 276 277 278
	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);
279 280
	return 0;
}
281
EXPORT_SYMBOL(_snd_ctl_add_follower);
282

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
/*
 * 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;
}

313
static int sync_followers(struct link_master *master, int old_val, int new_val)
314
{
315
	struct link_follower *follower;
316 317 318 319 320
	struct snd_ctl_elem_value *uval;

	uval = kmalloc(sizeof(*uval), GFP_KERNEL);
	if (!uval)
		return -ENOMEM;
321
	list_for_each_entry(follower, &master->followers, list) {
322
		master->val = old_val;
323 324
		uval->id = follower->follower.id;
		follower_get_val(follower, uval);
325
		master->val = new_val;
326
		follower_put_val(follower, uval);
327 328
	}
	kfree(uval);
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
	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;

348
	err = sync_followers(master, old_val, new_val);
349 350
	if (err < 0)
		return err;
351
	if (master->hook && !first_init)
352
		master->hook(master->hook_private_data, master->val);
353 354 355 356 357 358
	return 1;
}

static void master_free(struct snd_kcontrol *kcontrol)
{
	struct link_master *master = snd_kcontrol_chip(kcontrol);
359
	struct link_follower *follower, *n;
360

361 362 363
	/* 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;
364
		struct list_head olist = sctl->list;
365 366
		memcpy(sctl, &follower->follower, sizeof(*sctl));
		memcpy(sctl->vd, follower->follower.vd,
367 368
		       sctl->count * sizeof(*sctl->vd));
		sctl->list = olist; /* keep the current linked-list */
369
		kfree(follower);
370
	}
371 372 373 374
	kfree(master);
}


375 376 377 378 379
/**
 * 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
 *
380
 * Creates a virtual master control with the given name string.
381
 *
382 383
 * After creating a vmaster element, you can add the follower controls
 * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached().
384 385 386
 *
 * 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
387 388
 * 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.
389 390
 *
 * Return: The created control element, or %NULL for errors (ENOMEM).
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
 */
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;
407
	INIT_LIST_HEAD(&master->followers);
408 409 410 411 412 413 414 415 416 417 418 419 420

	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 */
421 422 423 424 425 426 427 428 429
	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;
		}
430
	}
431

432 433
	return kctl;
}
434
EXPORT_SYMBOL(snd_ctl_make_virtual_master);
435 436 437 438 439

/**
 * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
 * @kcontrol: vmaster kctl element
 * @hook: the hook function
440
 * @private_data: the private_data pointer to be saved
441 442 443
 *
 * Adds the given hook to the vmaster control element so that it's called
 * at each time when the value is changed.
444 445
 *
 * Return: Zero.
446 447 448 449 450 451 452 453 454 455 456 457 458
 */
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);

/**
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 465
 * to synchronize with the current value of the given vmaster element.
 * NOP when NULL is passed to @kcontrol.
466
 */
467
void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
468 469
{
	struct link_master *master;
470 471
	bool first_init = false;

472 473 474
	if (!kcontrol)
		return;
	master = snd_kcontrol_chip(kcontrol);
475 476 477 478 479
	if (!hook_only) {
		int err = master_init(master);
		if (err < 0)
			return;
		first_init = err;
480
		err = sync_followers(master, master->val, master->val);
481 482 483 484 485
		if (err < 0)
			return;
	}

	if (master->hook && !first_init)
486 487
		master->hook(master->hook_private_data, master->val);
}
488
EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
489 490

/**
491
 * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower
492 493 494 495
 * @kctl: vmaster kctl element
 * @func: function to apply
 * @arg: optional function argument
 *
496
 * Apply the function @func to each follower kctl of the given vmaster kctl.
497 498
 * Returns 0 if successful, or a negative error code.
 */
499 500 501 502 503
int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
				    int (*func)(struct snd_kcontrol *vfollower,
						struct snd_kcontrol *follower,
						void *arg),
				    void *arg)
504 505
{
	struct link_master *master;
506
	struct link_follower *follower;
507 508 509 510 511 512
	int err;

	master = snd_kcontrol_chip(kctl);
	err = master_init(master);
	if (err < 0)
		return err;
513 514
	list_for_each_entry(follower, &master->followers, list) {
		err = func(follower->kctl, &follower->follower, arg);
515 516 517 518 519 520
		if (err < 0)
			return err;
	}

	return 0;
}
521
EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);