// SPDX-License-Identifier: GPL-2.0-only /* * Virtual master and follower controls * * Copyright (c) 2008 by Takashi Iwai <[email protected]> */ #include <linux/slab.h> #include <linux/export.h> #include <sound/core.h> #include <sound/control.h> #include <sound/tlv.h> /* * a subset of information returned via ctl info callback */ struct link_ctl_info { … }; /* * link master - this contains a list of follower controls that are * 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. * The same value will be applied to all followers. */ struct link_master { … }; /* * link follower - this contains a follower control element * * It fakes the control callbacks with additional attenuation by the * master control. A follower may have either one or two channels. */ struct link_follower { … }; static int follower_update(struct link_follower *follower) { … } /* get the follower ctl info and save the initial values */ static int follower_init(struct link_follower *follower) { … } /* initialize master volume */ static int master_init(struct link_master *master) { … } static int follower_get_val(struct link_follower *follower, struct snd_ctl_elem_value *ucontrol) { … } static int follower_put_val(struct link_follower *follower, struct snd_ctl_elem_value *ucontrol) { … } /* * ctl callbacks for followers */ static int follower_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { … } static int follower_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int follower_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int follower_tlv_cmd(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { … } static void follower_free(struct snd_kcontrol *kcontrol) { … } /* * Add a follower control to the group with the given master control * * All followers must be the same type (returning the same information * via info callback). The function doesn't check it, so it's your * 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 */ int _snd_ctl_add_follower(struct snd_kcontrol *master, struct snd_kcontrol *follower, unsigned int flags) { … } EXPORT_SYMBOL(…); /** * snd_ctl_add_followers - add multiple followers to vmaster * @card: card instance * @master: the target vmaster kcontrol object * @list: NULL-terminated list of name strings of followers to be added * * Adds the multiple follower kcontrols with the given names. * Returns 0 for success or a negative error code. */ int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master, const char * const *list) { … } EXPORT_SYMBOL_GPL(…); /* * ctl callbacks for master controls */ static int master_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { … } static int master_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static int sync_followers(struct link_master *master, int old_val, int new_val) { … } static int master_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { … } static void master_free(struct snd_kcontrol *kcontrol) { … } /** * 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 * * Creates a virtual master control with the given name string. * * After creating a vmaster element, you can add the follower controls * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached(). * * 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 * 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. * * Return: The created control element, or %NULL for errors (ENOMEM). */ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, const unsigned int *tlv) { … } EXPORT_SYMBOL(…); /** * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control * @kcontrol: vmaster kctl element * @hook: the hook function * @private_data: the private_data pointer to be saved * * Adds the given hook to the vmaster control element so that it's called * at each time when the value is changed. * * Return: Zero. */ int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol, void (*hook)(void *private_data, int), void *private_data) { … } EXPORT_SYMBOL_GPL(…); /** * snd_ctl_sync_vmaster - Sync the vmaster followers and hook * @kcontrol: vmaster kctl element * @hook_only: sync only the hook * * Forcibly call the put callback of each follower and call the hook function * to synchronize with the current value of the given vmaster element. * NOP when NULL is passed to @kcontrol. */ void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only) { … } EXPORT_SYMBOL_GPL(…); /** * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower * @kctl: vmaster kctl element * @func: function to apply * @arg: optional function argument * * Apply the function @func to each follower kctl of the given vmaster kctl. * * Return: 0 if successful, or a negative error code */ int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl, int (*func)(struct snd_kcontrol *vfollower, struct snd_kcontrol *follower, void *arg), void *arg) { … } EXPORT_SYMBOL_GPL(…);