Merge tag 'sound-5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound updates from Takashi Iwai:
 "This became wide and scattered updates all over the sound tree as
  diffstat shows: lots of (still ongoing) refactoring works in ASoC,
  fixes and cleanups caught by static analysis, inclusive term
  conversions as well as lots of new drivers. Below are highlights:

  ASoC core:
   - API cleanups and conversions to the unified mute_stream() call
   - Simplify I/O helper functions
   - Use helper macros to retrieve RTD from substreams

  ASoC drivers:
   - Lots of fixes and cleanups in Intel ASoC drivers
   - Lots of new stuff: Freescale MQS and i.MX6sx, Intel KeemBay I2S,
     Maxim MAX98360A and MAX98373 SoundWire, various Mediatek boards,
     nVidia Tegra 186 and 210, RealTek RL6231, Samsung Midas and Aries
     boards, TI J721e EVM

  ALSA core:
   - Minor code refacotring for SG-buffer handling

  HD-audio:
   - Generalization of mute-LED handling with LED classdev
   - Intel silent stream support for HDMI
   - Device-specific fixes: CA0132, Loongson-3

  Others:
   - Usual USB- and HD-audio quirks for various devices
   - Fixes for echoaudio DMA position handling
   - Various documents and trivial fixes for sparse warnings
   - Conversion to adopt inclusive terms"

* tag 'sound-5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (479 commits)
  ALSA: pci: delete repeated words in comments
  ALSA: isa: delete repeated words in comments
  ALSA: hda/tegra: Add 100us dma stop delay
  ALSA: hda: Add dma stop delay variable
  ASoC: hda/tegra: Set buffer alignment to 128 bytes
  ALSA: seq: oss: Serialize ioctls
  ALSA: hda/hdmi: Add quirk to force connectivity
  ALSA: usb-audio: add startech usb audio dock name
  ALSA: usb-audio: Add support for Lenovo ThinkStation P620
  Revert "ALSA: hda: call runtime_allow() for all hda controllers"
  ALSA: hda/ca0132 - Fix AE-5 microphone selection commands.
  ALSA: hda/ca0132 - Add new quirk ID for Recon3D.
  ALSA: hda/ca0132 - Fix ZxR Headphone gain control get value.
  ALSA: hda/realtek: Add alc269/alc662 pin-tables for Loongson-3 laptops
  ALSA: docs: fix typo
  ALSA: doc: use correct config variable name
  ASoC: core: Two step component registration
  ASoC: core: Simplify snd_soc_component_initialize declaration
  ASoC: core: Relocate and expose snd_soc_component_initialize
  ASoC: sh: Replace 'select' DMADEVICES 'with depends on'
  ...
This commit is contained in:
Linus Torvalds
2020-08-06 14:27:31 -07:00
688 changed files with 17946 additions and 5778 deletions

View File

@ -203,7 +203,10 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
mutex_unlock(&snd_card_mutex);
card->dev = parent;
card->number = idx;
#ifdef MODULE
WARN_ON(!module);
card->module = module;
#endif
INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock);

View File

@ -135,16 +135,17 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
dmab->dev.type = type;
dmab->dev.dev = device;
dmab->bytes = 0;
dmab->area = NULL;
dmab->addr = 0;
dmab->private_data = NULL;
switch (type) {
case SNDRV_DMA_TYPE_CONTINUOUS:
gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL);
dmab->area = alloc_pages_exact(size, gfp);
dmab->addr = 0;
break;
case SNDRV_DMA_TYPE_VMALLOC:
gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL | __GFP_HIGHMEM);
dmab->area = __vmalloc(size, gfp);
dmab->addr = 0;
break;
#ifdef CONFIG_HAS_DMA
#ifdef CONFIG_GENERIC_ALLOCATOR
@ -157,7 +158,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
*/
dmab->dev.type = SNDRV_DMA_TYPE_DEV;
#endif /* CONFIG_GENERIC_ALLOCATOR */
/* fall through */
fallthrough;
case SNDRV_DMA_TYPE_DEV:
case SNDRV_DMA_TYPE_DEV_UC:
snd_malloc_dev_pages(dmab, size);
@ -171,8 +172,6 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
#endif
default:
pr_err("snd-malloc: invalid device type %d\n", type);
dmab->area = NULL;
dmab->addr = 0;
return -ENXIO;
}
if (! dmab->area)

View File

@ -2851,7 +2851,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream)
break;
/* Fall through */
fallthrough;
case VM_READ:
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
break;

View File

@ -357,7 +357,7 @@ snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
if (snd_mask_test(format_mask, (__force int)format1))
return format1;
}
/* fall through */
fallthrough;
default:
return (__force snd_pcm_format_t)-EINVAL;
}

View File

@ -103,7 +103,7 @@ EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
/**
* snd_pcm_create_iec958_consumer_hw_params - create IEC958 channel status
* @hw_params: the hw_params instance for extracting rate and sample format
* @params: the hw_params instance for extracting rate and sample format
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*

View File

@ -39,6 +39,7 @@ static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
if (max_alloc_per_card &&
card->total_pcm_alloc_bytes + size > max_alloc_per_card)
return -ENOMEM;
err = snd_dma_alloc_pages(type, dev, size, dmab);
if (!err) {
mutex_lock(&card->memory_mutex);

View File

@ -1903,7 +1903,7 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
switch (substream->runtime->status->state) {
case SNDRV_PCM_STATE_PAUSED:
snd_pcm_pause(substream, false);
/* fallthru */
fallthrough;
case SNDRV_PCM_STATE_SUSPENDED:
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
break;
@ -2811,7 +2811,7 @@ static int do_pcm_hwsync(struct snd_pcm_substream *substream)
case SNDRV_PCM_STATE_DRAINING:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return -EBADFD;
/* Fall through */
fallthrough;
case SNDRV_PCM_STATE_RUNNING:
return snd_pcm_update_hw_ptr(substream);
case SNDRV_PCM_STATE_PREPARED:
@ -3713,7 +3713,6 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
area->vm_end - area->vm_start, area->vm_page_prot);
}
#endif /* CONFIG_GENERIC_ALLOCATOR */
#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */
if (IS_ENABLED(CONFIG_HAS_DMA) && !substream->ops->page &&
(substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV ||
substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_UC))
@ -3722,7 +3721,6 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
substream->runtime->dma_area,
substream->runtime->dma_addr,
substream->runtime->dma_bytes);
#endif /* CONFIG_X86 */
/* mmap with fault handler */
area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
@ -3816,7 +3814,7 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
case SNDRV_PCM_MMAP_OFFSET_STATUS_OLD:
if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
return -ENXIO;
/* fallthrough */
fallthrough;
case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
if (!pcm_status_mmap_allowed(pcm_file))
return -ENXIO;
@ -3824,7 +3822,7 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
case SNDRV_PCM_MMAP_OFFSET_CONTROL_OLD:
if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
return -ENXIO;
/* fallthrough */
fallthrough;
case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
if (!pcm_control_mmap_allowed(pcm_file))
return -ENXIO;

View File

@ -168,10 +168,16 @@ static long
odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct seq_oss_devinfo *dp;
long rc;
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
return snd_seq_oss_ioctl(dp, cmd, arg);
mutex_lock(&register_mutex);
rc = snd_seq_oss_ioctl(dp, cmd, arg);
mutex_unlock(&register_mutex);
return rc;
}
#ifdef CONFIG_COMPAT

View File

@ -79,7 +79,7 @@ snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
case TMR_WAIT_REL:
parm += rec->cur_tick;
rec->realtime = 0;
/* fall through */
fallthrough;
case TMR_WAIT_ABS:
if (parm == 0) {
rec->realtime = 1;

View File

@ -309,7 +309,7 @@ do_control(const struct snd_midi_op *ops, void *drv,
break;
case MIDI_CTL_MSB_DATA_ENTRY:
chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
/* fall through */
fallthrough;
case MIDI_CTL_LSB_DATA_ENTRY:
if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
rpn(ops, drv, chan, chset);

View File

@ -142,6 +142,9 @@ unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
struct snd_sg_buf *sg = dmab->private_data;
unsigned int start, end, pg;
if (!sg)
return size;
start = ofs >> PAGE_SHIFT;
end = (ofs + size - 1) >> PAGE_SHIFT;
/* check page continuity */

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Virtual master and slave controls
* Virtual master and follower controls
*
* Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
*/
@ -21,15 +21,15 @@ struct link_ctl_info {
};
/*
* link master - this contains a list of slave controls that are
* 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 slaves.
* The same value will be applied to all followers.
*/
struct link_master {
struct list_head slaves;
struct list_head followers;
struct link_ctl_info info;
int val; /* the master value */
unsigned int tlv[4];
@ -38,23 +38,23 @@ struct link_master {
};
/*
* link slave - this contains a slave control element
* link follower - this contains a follower control element
*
* It fakes the control callbacsk with additional attenuation by the
* master control. A slave may have either one or two channels.
* It fakes the control callbacks with additional attenuation by the
* master control. A follower may have either one or two channels.
*/
struct link_slave {
struct link_follower {
struct list_head list;
struct link_master *master;
struct link_ctl_info info;
int vals[2]; /* current values */
unsigned int flags;
struct snd_kcontrol *kctl; /* original kcontrol pointer */
struct snd_kcontrol slave; /* the copy of original control entry */
struct snd_kcontrol follower; /* the copy of original control entry */
};
static int slave_update(struct link_slave *slave)
static int follower_update(struct link_follower *follower)
{
struct snd_ctl_elem_value *uctl;
int err, ch;
@ -62,68 +62,68 @@ static int slave_update(struct link_slave *slave)
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (!uctl)
return -ENOMEM;
uctl->id = slave->slave.id;
err = slave->slave.get(&slave->slave, uctl);
uctl->id = follower->follower.id;
err = follower->follower.get(&follower->follower, uctl);
if (err < 0)
goto error;
for (ch = 0; ch < slave->info.count; ch++)
slave->vals[ch] = uctl->value.integer.value[ch];
for (ch = 0; ch < follower->info.count; ch++)
follower->vals[ch] = uctl->value.integer.value[ch];
error:
kfree(uctl);
return err < 0 ? err : 0;
}
/* get the slave ctl info and save the initial values */
static int slave_init(struct link_slave *slave)
/* get the follower ctl info and save the initial values */
static int follower_init(struct link_follower *follower)
{
struct snd_ctl_elem_info *uinfo;
int err;
if (slave->info.count) {
if (follower->info.count) {
/* already initialized */
if (slave->flags & SND_CTL_SLAVE_NEED_UPDATE)
return slave_update(slave);
if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE)
return follower_update(follower);
return 0;
}
uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
if (!uinfo)
return -ENOMEM;
uinfo->id = slave->slave.id;
err = slave->slave.info(&slave->slave, uinfo);
uinfo->id = follower->follower.id;
err = follower->follower.info(&follower->follower, uinfo);
if (err < 0) {
kfree(uinfo);
return err;
}
slave->info.type = uinfo->type;
slave->info.count = uinfo->count;
if (slave->info.count > 2 ||
(slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
pr_err("ALSA: vmaster: invalid slave element\n");
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");
kfree(uinfo);
return -EINVAL;
}
slave->info.min_val = uinfo->value.integer.min;
slave->info.max_val = uinfo->value.integer.max;
follower->info.min_val = uinfo->value.integer.min;
follower->info.max_val = uinfo->value.integer.max;
kfree(uinfo);
return slave_update(slave);
return follower_update(follower);
}
/* initialize master volume */
static int master_init(struct link_master *master)
{
struct link_slave *slave;
struct link_follower *follower;
if (master->info.count)
return 0; /* already initialized */
list_for_each_entry(slave, &master->slaves, list) {
int err = slave_init(slave);
list_for_each_entry(follower, &master->followers, list) {
int err = follower_init(follower);
if (err < 0)
return err;
master->info = slave->info;
master->info = follower->info;
master->info.count = 1; /* always mono */
/* set full volume as default (= no attenuation) */
master->val = master->info.max_val;
@ -134,113 +134,113 @@ static int master_init(struct link_master *master)
return -ENOENT;
}
static int slave_get_val(struct link_slave *slave,
struct snd_ctl_elem_value *ucontrol)
static int follower_get_val(struct link_follower *follower,
struct snd_ctl_elem_value *ucontrol)
{
int err, ch;
err = slave_init(slave);
err = follower_init(follower);
if (err < 0)
return err;
for (ch = 0; ch < slave->info.count; ch++)
ucontrol->value.integer.value[ch] = slave->vals[ch];
for (ch = 0; ch < follower->info.count; ch++)
ucontrol->value.integer.value[ch] = follower->vals[ch];
return 0;
}
static int slave_put_val(struct link_slave *slave,
struct snd_ctl_elem_value *ucontrol)
static int follower_put_val(struct link_follower *follower,
struct snd_ctl_elem_value *ucontrol)
{
int err, ch, vol;
err = master_init(slave->master);
err = master_init(follower->master);
if (err < 0)
return err;
switch (slave->info.type) {
switch (follower->info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
for (ch = 0; ch < slave->info.count; ch++)
for (ch = 0; ch < follower->info.count; ch++)
ucontrol->value.integer.value[ch] &=
!!slave->master->val;
!!follower->master->val;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER:
for (ch = 0; ch < slave->info.count; ch++) {
for (ch = 0; ch < follower->info.count; ch++) {
/* max master volume is supposed to be 0 dB */
vol = ucontrol->value.integer.value[ch];
vol += slave->master->val - slave->master->info.max_val;
if (vol < slave->info.min_val)
vol = slave->info.min_val;
else if (vol > slave->info.max_val)
vol = slave->info.max_val;
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;
ucontrol->value.integer.value[ch] = vol;
}
break;
}
return slave->slave.put(&slave->slave, ucontrol);
return follower->follower.put(&follower->follower, ucontrol);
}
/*
* ctl callbacks for slaves
* ctl callbacks for followers
*/
static int slave_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
static int follower_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct link_slave *slave = snd_kcontrol_chip(kcontrol);
return slave->slave.info(&slave->slave, uinfo);
struct link_follower *follower = snd_kcontrol_chip(kcontrol);
return follower->follower.info(&follower->follower, uinfo);
}
static int slave_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
static int follower_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct link_slave *slave = snd_kcontrol_chip(kcontrol);
return slave_get_val(slave, ucontrol);
struct link_follower *follower = snd_kcontrol_chip(kcontrol);
return follower_get_val(follower, ucontrol);
}
static int slave_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
static int follower_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct link_slave *slave = snd_kcontrol_chip(kcontrol);
struct link_follower *follower = snd_kcontrol_chip(kcontrol);
int err, ch, changed = 0;
err = slave_init(slave);
err = follower_init(follower);
if (err < 0)
return err;
for (ch = 0; ch < slave->info.count; ch++) {
if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
for (ch = 0; ch < follower->info.count; ch++) {
if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
changed = 1;
slave->vals[ch] = ucontrol->value.integer.value[ch];
follower->vals[ch] = ucontrol->value.integer.value[ch];
}
}
if (!changed)
return 0;
err = slave_put_val(slave, ucontrol);
err = follower_put_val(follower, ucontrol);
if (err < 0)
return err;
return 1;
}
static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
int op_flag, unsigned int size,
unsigned int __user *tlv)
static int follower_tlv_cmd(struct snd_kcontrol *kcontrol,
int op_flag, unsigned int size,
unsigned int __user *tlv)
{
struct link_slave *slave = snd_kcontrol_chip(kcontrol);
struct link_follower *follower = snd_kcontrol_chip(kcontrol);
/* FIXME: this assumes that the max volume is 0 dB */
return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv);
}
static void slave_free(struct snd_kcontrol *kcontrol)
static void follower_free(struct snd_kcontrol *kcontrol)
{
struct link_slave *slave = snd_kcontrol_chip(kcontrol);
if (slave->slave.private_free)
slave->slave.private_free(&slave->slave);
if (slave->master)
list_del(&slave->list);
kfree(slave);
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);
}
/*
* Add a slave control to the group with the given master control
* Add a follower control to the group with the given master control
*
* All slaves must be the same type (returning the same information
* 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.
*
@ -249,35 +249,36 @@ static void slave_free(struct snd_kcontrol *kcontrol)
* - logarithmic volume control (dB level), no linear volume
* - master can only attenuate the volume, no gain
*/
int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave,
unsigned int flags)
int _snd_ctl_add_follower(struct snd_kcontrol *master,
struct snd_kcontrol *follower,
unsigned int flags)
{
struct link_master *master_link = snd_kcontrol_chip(master);
struct link_slave *srec;
struct link_follower *srec;
srec = kzalloc(struct_size(srec, slave.vd, slave->count),
srec = kzalloc(struct_size(srec, follower.vd, follower->count),
GFP_KERNEL);
if (!srec)
return -ENOMEM;
srec->kctl = slave;
srec->slave = *slave;
memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
srec->kctl = follower;
srec->follower = *follower;
memcpy(srec->follower.vd, follower->vd, follower->count * sizeof(*follower->vd));
srec->master = master_link;
srec->flags = flags;
/* override callbacks */
slave->info = slave_info;
slave->get = slave_get;
slave->put = slave_put;
if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
slave->tlv.c = slave_tlv_cmd;
slave->private_data = srec;
slave->private_free = slave_free;
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->slaves);
list_add_tail(&srec->list, &master_link->followers);
return 0;
}
EXPORT_SYMBOL(_snd_ctl_add_slave);
EXPORT_SYMBOL(_snd_ctl_add_follower);
/*
* ctl callbacks for master controls
@ -309,20 +310,20 @@ static int master_get(struct snd_kcontrol *kcontrol,
return 0;
}
static int sync_slaves(struct link_master *master, int old_val, int new_val)
static int sync_followers(struct link_master *master, int old_val, int new_val)
{
struct link_slave *slave;
struct link_follower *follower;
struct snd_ctl_elem_value *uval;
uval = kmalloc(sizeof(*uval), GFP_KERNEL);
if (!uval)
return -ENOMEM;
list_for_each_entry(slave, &master->slaves, list) {
list_for_each_entry(follower, &master->followers, list) {
master->val = old_val;
uval->id = slave->slave.id;
slave_get_val(slave, uval);
uval->id = follower->follower.id;
follower_get_val(follower, uval);
master->val = new_val;
slave_put_val(slave, uval);
follower_put_val(follower, uval);
}
kfree(uval);
return 0;
@ -344,7 +345,7 @@ static int master_put(struct snd_kcontrol *kcontrol,
if (new_val == old_val)
return 0;
err = sync_slaves(master, old_val, new_val);
err = sync_followers(master, old_val, new_val);
if (err < 0)
return err;
if (master->hook && !first_init)
@ -355,17 +356,17 @@ static int master_put(struct snd_kcontrol *kcontrol,
static void master_free(struct snd_kcontrol *kcontrol)
{
struct link_master *master = snd_kcontrol_chip(kcontrol);
struct link_slave *slave, *n;
struct link_follower *follower, *n;
/* free all slave links and retore the original slave kctls */
list_for_each_entry_safe(slave, n, &master->slaves, list) {
struct snd_kcontrol *sctl = slave->kctl;
/* 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;
struct list_head olist = sctl->list;
memcpy(sctl, &slave->slave, sizeof(*sctl));
memcpy(sctl->vd, slave->slave.vd,
memcpy(sctl, &follower->follower, sizeof(*sctl));
memcpy(sctl->vd, follower->follower.vd,
sctl->count * sizeof(*sctl->vd));
sctl->list = olist; /* keep the current linked-list */
kfree(slave);
kfree(follower);
}
kfree(master);
}
@ -378,8 +379,8 @@ static void master_free(struct snd_kcontrol *kcontrol)
*
* Creates a virtual master control with the given name string.
*
* After creating a vmaster element, you can add the slave controls
* via snd_ctl_add_slave() or snd_ctl_add_slave_uncached().
* 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
@ -403,7 +404,7 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return NULL;
INIT_LIST_HEAD(&master->slaves);
INIT_LIST_HEAD(&master->followers);
kctl = snd_ctl_new1(&knew, master);
if (!kctl) {
@ -455,11 +456,11 @@ int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
/**
* snd_ctl_sync_vmaster - Sync the vmaster slaves and hook
* 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 slave and call the hook function
* 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.
*/
@ -476,7 +477,7 @@ void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
if (err < 0)
return;
first_init = err;
err = sync_slaves(master, master->val, master->val);
err = sync_followers(master, master->val, master->val);
if (err < 0)
return;
}
@ -487,34 +488,34 @@ void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
/**
* snd_ctl_apply_vmaster_slaves - Apply function to each vmaster slave
* 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 slave kctl of the given vmaster kctl.
* Apply the function @func to each follower kctl of the given vmaster kctl.
* Returns 0 if successful, or a negative error code.
*/
int snd_ctl_apply_vmaster_slaves(struct snd_kcontrol *kctl,
int (*func)(struct snd_kcontrol *vslave,
struct snd_kcontrol *slave,
void *arg),
void *arg)
int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
int (*func)(struct snd_kcontrol *vfollower,
struct snd_kcontrol *follower,
void *arg),
void *arg)
{
struct link_master *master;
struct link_slave *slave;
struct link_follower *follower;
int err;
master = snd_kcontrol_chip(kctl);
err = master_init(master);
if (err < 0)
return err;
list_for_each_entry(slave, &master->slaves, list) {
err = func(slave->kctl, &slave->slave, arg);
list_for_each_entry(follower, &master->followers, list) {
err = func(follower->kctl, &follower->follower, arg);
if (err < 0)
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_slaves);
EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);