From 0ae0337f929a970ee8d83e0e95e6b8d05562ce3b Mon Sep 17 00:00:00 2001 From: Anton Yakovlev Date: Tue, 2 Mar 2021 17:47:01 +0100 Subject: uapi: virtio_ids: add a sound device type ID from OASIS spec The OASIS virtio spec defines a sound device type ID that is not present in the header yet. Signed-off-by: Anton Yakovlev Link: https://lore.kernel.org/r/20210302164709.3142702-2-anton.yakovlev@opensynergy.com Signed-off-by: Takashi Iwai --- include/uapi/linux/virtio_ids.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index bc1c0621f5ed..029a2e07a7f9 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -51,6 +51,7 @@ #define VIRTIO_ID_PSTORE 22 /* virtio pstore device */ #define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */ #define VIRTIO_ID_MEM 24 /* virtio mem */ +#define VIRTIO_ID_SOUND 25 /* virtio sound */ #define VIRTIO_ID_FS 26 /* virtio filesystem */ #define VIRTIO_ID_PMEM 27 /* virtio pmem */ #define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */ -- cgit v1.2.3-70-g09d2 From de3a9980d8c34b2479173e809afa820473db676a Mon Sep 17 00:00:00 2001 From: Anton Yakovlev Date: Tue, 2 Mar 2021 17:47:02 +0100 Subject: ALSA: virtio: add virtio sound driver Introduce skeleton of the virtio sound driver. The driver implements the virtio sound device specification, which has become part of the virtio standard. Initial initialization of the device, virtqueues and creation of an empty ALSA sound device. Signed-off-by: Anton Yakovlev Link: https://lore.kernel.org/r/20210302164709.3142702-3-anton.yakovlev@opensynergy.com Signed-off-by: Takashi Iwai --- MAINTAINERS | 9 ++ include/uapi/linux/virtio_snd.h | 334 ++++++++++++++++++++++++++++++++++++++++ sound/Kconfig | 2 + sound/Makefile | 3 +- sound/virtio/Kconfig | 10 ++ sound/virtio/Makefile | 7 + sound/virtio/virtio_card.c | 324 ++++++++++++++++++++++++++++++++++++++ sound/virtio/virtio_card.h | 65 ++++++++ 8 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 include/uapi/linux/virtio_snd.h create mode 100644 sound/virtio/Kconfig create mode 100644 sound/virtio/Makefile create mode 100644 sound/virtio/virtio_card.c create mode 100644 sound/virtio/virtio_card.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index d92f85ca831d..a68c543c28f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19086,6 +19086,15 @@ W: https://virtio-mem.gitlab.io/ F: drivers/virtio/virtio_mem.c F: include/uapi/linux/virtio_mem.h +VIRTIO SOUND DRIVER +M: Anton Yakovlev +M: "Michael S. Tsirkin" +L: virtualization@lists.linux-foundation.org +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: include/uapi/linux/virtio_snd.h +F: sound/virtio/* + VIRTUAL BOX GUEST DEVICE DRIVER M: Hans de Goede M: Arnd Bergmann diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_snd.h new file mode 100644 index 000000000000..dfe49547a7b0 --- /dev/null +++ b/include/uapi/linux/virtio_snd.h @@ -0,0 +1,334 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2021 OpenSynergy GmbH + */ +#ifndef VIRTIO_SND_IF_H +#define VIRTIO_SND_IF_H + +#include + +/******************************************************************************* + * CONFIGURATION SPACE + */ +struct virtio_snd_config { + /* # of available physical jacks */ + __le32 jacks; + /* # of available PCM streams */ + __le32 streams; + /* # of available channel maps */ + __le32 chmaps; +}; + +enum { + /* device virtqueue indexes */ + VIRTIO_SND_VQ_CONTROL = 0, + VIRTIO_SND_VQ_EVENT, + VIRTIO_SND_VQ_TX, + VIRTIO_SND_VQ_RX, + /* # of device virtqueues */ + VIRTIO_SND_VQ_MAX +}; + +/******************************************************************************* + * COMMON DEFINITIONS + */ + +/* supported dataflow directions */ +enum { + VIRTIO_SND_D_OUTPUT = 0, + VIRTIO_SND_D_INPUT +}; + +enum { + /* jack control request types */ + VIRTIO_SND_R_JACK_INFO = 1, + VIRTIO_SND_R_JACK_REMAP, + + /* PCM control request types */ + VIRTIO_SND_R_PCM_INFO = 0x0100, + VIRTIO_SND_R_PCM_SET_PARAMS, + VIRTIO_SND_R_PCM_PREPARE, + VIRTIO_SND_R_PCM_RELEASE, + VIRTIO_SND_R_PCM_START, + VIRTIO_SND_R_PCM_STOP, + + /* channel map control request types */ + VIRTIO_SND_R_CHMAP_INFO = 0x0200, + + /* jack event types */ + VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, + VIRTIO_SND_EVT_JACK_DISCONNECTED, + + /* PCM event types */ + VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, + VIRTIO_SND_EVT_PCM_XRUN, + + /* common status codes */ + VIRTIO_SND_S_OK = 0x8000, + VIRTIO_SND_S_BAD_MSG, + VIRTIO_SND_S_NOT_SUPP, + VIRTIO_SND_S_IO_ERR +}; + +/* common header */ +struct virtio_snd_hdr { + __le32 code; +}; + +/* event notification */ +struct virtio_snd_event { + /* VIRTIO_SND_EVT_XXX */ + struct virtio_snd_hdr hdr; + /* optional event data */ + __le32 data; +}; + +/* common control request to query an item information */ +struct virtio_snd_query_info { + /* VIRTIO_SND_R_XXX_INFO */ + struct virtio_snd_hdr hdr; + /* item start identifier */ + __le32 start_id; + /* item count to query */ + __le32 count; + /* item information size in bytes */ + __le32 size; +}; + +/* common item information header */ +struct virtio_snd_info { + /* function group node id (High Definition Audio Specification 7.1.2) */ + __le32 hda_fn_nid; +}; + +/******************************************************************************* + * JACK CONTROL MESSAGES + */ +struct virtio_snd_jack_hdr { + /* VIRTIO_SND_R_JACK_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::jacks - 1 */ + __le32 jack_id; +}; + +/* supported jack features */ +enum { + VIRTIO_SND_JACK_F_REMAP = 0 +}; + +struct virtio_snd_jack_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_JACK_F_XXX) */ + __le32 features; + /* pin configuration (High Definition Audio Specification 7.3.3.31) */ + __le32 hda_reg_defconf; + /* pin capabilities (High Definition Audio Specification 7.3.4.9) */ + __le32 hda_reg_caps; + /* current jack connection status (0: disconnected, 1: connected) */ + __u8 connected; + + __u8 padding[7]; +}; + +/* jack remapping control request */ +struct virtio_snd_jack_remap { + /* .code = VIRTIO_SND_R_JACK_REMAP */ + struct virtio_snd_jack_hdr hdr; + /* selected association number */ + __le32 association; + /* selected sequence number */ + __le32 sequence; +}; + +/******************************************************************************* + * PCM CONTROL MESSAGES + */ +struct virtio_snd_pcm_hdr { + /* VIRTIO_SND_R_PCM_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* supported PCM stream features */ +enum { + VIRTIO_SND_PCM_F_SHMEM_HOST = 0, + VIRTIO_SND_PCM_F_SHMEM_GUEST, + VIRTIO_SND_PCM_F_MSG_POLLING, + VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS, + VIRTIO_SND_PCM_F_EVT_XRUNS +}; + +/* supported PCM sample formats */ +enum { + /* analog formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_IMA_ADPCM = 0, /* 4 / 4 bits */ + VIRTIO_SND_PCM_FMT_MU_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_A_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_S18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_U18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_U20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_S24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_U24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_U20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_S24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_U24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_S32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT64, /* 64 / 64 bits */ + /* digital formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_DSD_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_DSD_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_DSD_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME /* 32 / 32 bits */ +}; + +/* supported PCM frame rates */ +enum { + VIRTIO_SND_PCM_RATE_5512 = 0, + VIRTIO_SND_PCM_RATE_8000, + VIRTIO_SND_PCM_RATE_11025, + VIRTIO_SND_PCM_RATE_16000, + VIRTIO_SND_PCM_RATE_22050, + VIRTIO_SND_PCM_RATE_32000, + VIRTIO_SND_PCM_RATE_44100, + VIRTIO_SND_PCM_RATE_48000, + VIRTIO_SND_PCM_RATE_64000, + VIRTIO_SND_PCM_RATE_88200, + VIRTIO_SND_PCM_RATE_96000, + VIRTIO_SND_PCM_RATE_176400, + VIRTIO_SND_PCM_RATE_192000, + VIRTIO_SND_PCM_RATE_384000 +}; + +struct virtio_snd_pcm_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* supported sample format bit map (1 << VIRTIO_SND_PCM_FMT_XXX) */ + __le64 formats; + /* supported frame rate bit map (1 << VIRTIO_SND_PCM_RATE_XXX) */ + __le64 rates; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* minimum # of supported channels */ + __u8 channels_min; + /* maximum # of supported channels */ + __u8 channels_max; + + __u8 padding[5]; +}; + +/* set PCM stream format */ +struct virtio_snd_pcm_set_params { + /* .code = VIRTIO_SND_R_PCM_SET_PARAMS */ + struct virtio_snd_pcm_hdr hdr; + /* size of the hardware buffer */ + __le32 buffer_bytes; + /* size of the hardware period */ + __le32 period_bytes; + /* selected feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* selected # of channels */ + __u8 channels; + /* selected sample format (VIRTIO_SND_PCM_FMT_XXX) */ + __u8 format; + /* selected frame rate (VIRTIO_SND_PCM_RATE_XXX) */ + __u8 rate; + + __u8 padding; +}; + +/******************************************************************************* + * PCM I/O MESSAGES + */ + +/* I/O request header */ +struct virtio_snd_pcm_xfer { + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* I/O request status */ +struct virtio_snd_pcm_status { + /* VIRTIO_SND_S_XXX */ + __le32 status; + /* current device latency */ + __le32 latency_bytes; +}; + +/******************************************************************************* + * CHANNEL MAP CONTROL MESSAGES + */ +struct virtio_snd_chmap_hdr { + /* VIRTIO_SND_R_CHMAP_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::chmaps - 1 */ + __le32 chmap_id; +}; + +/* standard channel position definition */ +enum { + VIRTIO_SND_CHMAP_NONE = 0, /* undefined */ + VIRTIO_SND_CHMAP_NA, /* silent */ + VIRTIO_SND_CHMAP_MONO, /* mono stream */ + VIRTIO_SND_CHMAP_FL, /* front left */ + VIRTIO_SND_CHMAP_FR, /* front right */ + VIRTIO_SND_CHMAP_RL, /* rear left */ + VIRTIO_SND_CHMAP_RR, /* rear right */ + VIRTIO_SND_CHMAP_FC, /* front center */ + VIRTIO_SND_CHMAP_LFE, /* low frequency (LFE) */ + VIRTIO_SND_CHMAP_SL, /* side left */ + VIRTIO_SND_CHMAP_SR, /* side right */ + VIRTIO_SND_CHMAP_RC, /* rear center */ + VIRTIO_SND_CHMAP_FLC, /* front left center */ + VIRTIO_SND_CHMAP_FRC, /* front right center */ + VIRTIO_SND_CHMAP_RLC, /* rear left center */ + VIRTIO_SND_CHMAP_RRC, /* rear right center */ + VIRTIO_SND_CHMAP_FLW, /* front left wide */ + VIRTIO_SND_CHMAP_FRW, /* front right wide */ + VIRTIO_SND_CHMAP_FLH, /* front left high */ + VIRTIO_SND_CHMAP_FCH, /* front center high */ + VIRTIO_SND_CHMAP_FRH, /* front right high */ + VIRTIO_SND_CHMAP_TC, /* top center */ + VIRTIO_SND_CHMAP_TFL, /* top front left */ + VIRTIO_SND_CHMAP_TFR, /* top front right */ + VIRTIO_SND_CHMAP_TFC, /* top front center */ + VIRTIO_SND_CHMAP_TRL, /* top rear left */ + VIRTIO_SND_CHMAP_TRR, /* top rear right */ + VIRTIO_SND_CHMAP_TRC, /* top rear center */ + VIRTIO_SND_CHMAP_TFLC, /* top front left center */ + VIRTIO_SND_CHMAP_TFRC, /* top front right center */ + VIRTIO_SND_CHMAP_TSL, /* top side left */ + VIRTIO_SND_CHMAP_TSR, /* top side right */ + VIRTIO_SND_CHMAP_LLFE, /* left LFE */ + VIRTIO_SND_CHMAP_RLFE, /* right LFE */ + VIRTIO_SND_CHMAP_BC, /* bottom center */ + VIRTIO_SND_CHMAP_BLC, /* bottom left center */ + VIRTIO_SND_CHMAP_BRC /* bottom right center */ +}; + +/* maximum possible number of channels */ +#define VIRTIO_SND_CHMAP_MAX_SIZE 18 + +struct virtio_snd_chmap_info { + /* common header */ + struct virtio_snd_info hdr; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* # of valid channel position values */ + __u8 channels; + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ + __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE]; +}; + +#endif /* VIRTIO_SND_IF_H */ diff --git a/sound/Kconfig b/sound/Kconfig index 36785410fbe1..e56d96d2b11c 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -99,6 +99,8 @@ source "sound/synth/Kconfig" source "sound/xen/Kconfig" +source "sound/virtio/Kconfig" + endif # SND endif # !UML diff --git a/sound/Makefile b/sound/Makefile index 797ecdcd35e2..04ef04b1168f 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += oss/dmasound/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \ + virtio/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/virtio/Kconfig b/sound/virtio/Kconfig new file mode 100644 index 000000000000..094cba24ee5b --- /dev/null +++ b/sound/virtio/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Sound card driver for virtio + +config SND_VIRTIO + tristate "Virtio sound driver" + depends on VIRTIO + select SND_PCM + select SND_JACK + help + This is the virtual sound driver for virtio. Say Y or M. diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile new file mode 100644 index 000000000000..8c87ebb9982b --- /dev/null +++ b/sound/virtio/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o + +virtio_snd-objs := \ + virtio_card.o + diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c new file mode 100644 index 000000000000..5a37056858e9 --- /dev/null +++ b/sound/virtio/virtio_card.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2021 OpenSynergy GmbH + */ +#include +#include +#include +#include +#include + +#include "virtio_card.h" + +static void virtsnd_remove(struct virtio_device *vdev); + +/** + * virtsnd_event_send() - Add an event to the event queue. + * @vqueue: Underlying event virtqueue. + * @event: Event. + * @notify: Indicates whether or not to send a notification to the device. + * @gfp: Kernel flags for memory allocation. + * + * Context: Any context. + */ +static void virtsnd_event_send(struct virtqueue *vqueue, + struct virtio_snd_event *event, bool notify, + gfp_t gfp) +{ + struct scatterlist sg; + struct scatterlist *psgs[1] = { &sg }; + + /* reset event content */ + memset(event, 0, sizeof(*event)); + + sg_init_one(&sg, event, sizeof(*event)); + + if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify) + return; + + if (virtqueue_kick_prepare(vqueue)) + virtqueue_notify(vqueue); +} + +/** + * virtsnd_event_dispatch() - Dispatch an event from the device side. + * @snd: VirtIO sound device. + * @event: VirtIO sound event. + * + * Context: Any context. + */ +static void virtsnd_event_dispatch(struct virtio_snd *snd, + struct virtio_snd_event *event) +{ +} + +/** + * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue. + * @vqueue: Underlying event virtqueue. + * + * This callback function is called upon a vring interrupt request from the + * device. + * + * Context: Interrupt context. + */ +static void virtsnd_event_notify_cb(struct virtqueue *vqueue) +{ + struct virtio_snd *snd = vqueue->vdev->priv; + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + struct virtio_snd_event *event; + u32 length; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + do { + virtqueue_disable_cb(vqueue); + while ((event = virtqueue_get_buf(vqueue, &length))) { + virtsnd_event_dispatch(snd, event); + virtsnd_event_send(vqueue, event, true, GFP_ATOMIC); + } + if (unlikely(virtqueue_is_broken(vqueue))) + break; + } while (!virtqueue_enable_cb(vqueue)); + spin_unlock_irqrestore(&queue->lock, flags); +} + +/** + * virtsnd_find_vqs() - Enumerate and initialize all virtqueues. + * @snd: VirtIO sound device. + * + * After calling this function, the event queue is disabled. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_find_vqs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { + [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb + }; + static const char *names[VIRTIO_SND_VQ_MAX] = { + [VIRTIO_SND_VQ_EVENT] = "virtsnd-event" + }; + struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 }; + unsigned int i; + unsigned int n; + int rc; + + rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, + NULL); + if (rc) { + dev_err(&vdev->dev, "failed to initialize virtqueues\n"); + return rc; + } + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + snd->queues[i].vqueue = vqs[i]; + + /* Allocate events and populate the event queue */ + virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]); + + n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]); + + snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs), + GFP_KERNEL); + if (!snd->event_msgs) + return -ENOMEM; + + for (i = 0; i < n; ++i) + virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT], + &snd->event_msgs[i], false, GFP_KERNEL); + + return 0; +} + +/** + * virtsnd_enable_event_vq() - Enable the event virtqueue. + * @snd: VirtIO sound device. + * + * Context: Any context. + */ +static void virtsnd_enable_event_vq(struct virtio_snd *snd) +{ + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + + if (!virtqueue_enable_cb(queue->vqueue)) + virtsnd_event_notify_cb(queue->vqueue); +} + +/** + * virtsnd_disable_event_vq() - Disable the event virtqueue. + * @snd: VirtIO sound device. + * + * Context: Any context. + */ +static void virtsnd_disable_event_vq(struct virtio_snd *snd) +{ + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + struct virtio_snd_event *event; + u32 length; + unsigned long flags; + + if (queue->vqueue) { + spin_lock_irqsave(&queue->lock, flags); + virtqueue_disable_cb(queue->vqueue); + while ((event = virtqueue_get_buf(queue->vqueue, &length))) + virtsnd_event_dispatch(snd, event); + spin_unlock_irqrestore(&queue->lock, flags); + } +} + +/** + * virtsnd_build_devs() - Read configuration and build ALSA devices. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_build_devs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + struct device *dev = &vdev->dev; + int rc; + + rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &snd->card); + if (rc < 0) + return rc; + + snd->card->private_data = snd; + + strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER, + sizeof(snd->card->driver)); + strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME, + sizeof(snd->card->shortname)); + if (dev->parent->bus) + snprintf(snd->card->longname, sizeof(snd->card->longname), + VIRTIO_SND_CARD_NAME " at %s/%s/%s", + dev->parent->bus->name, dev_name(dev->parent), + dev_name(dev)); + else + snprintf(snd->card->longname, sizeof(snd->card->longname), + VIRTIO_SND_CARD_NAME " at %s/%s", + dev_name(dev->parent), dev_name(dev)); + + return snd_card_register(snd->card); +} + +/** + * virtsnd_validate() - Validate if the device can be started. + * @vdev: VirtIO parent device. + * + * Context: Any context. + * Return: 0 on success, -EINVAL on failure. + */ +static int virtsnd_validate(struct virtio_device *vdev) +{ + if (!vdev->config->get) { + dev_err(&vdev->dev, "configuration access disabled\n"); + return -EINVAL; + } + + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + dev_err(&vdev->dev, + "device does not comply with spec version 1.x\n"); + return -EINVAL; + } + + return 0; +} + +/** + * virtsnd_probe() - Create and initialize the device. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_probe(struct virtio_device *vdev) +{ + struct virtio_snd *snd; + unsigned int i; + int rc; + + snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL); + if (!snd) + return -ENOMEM; + + snd->vdev = vdev; + + vdev->priv = snd; + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + spin_lock_init(&snd->queues[i].lock); + + rc = virtsnd_find_vqs(snd); + if (rc) + goto on_exit; + + virtio_device_ready(vdev); + + rc = virtsnd_build_devs(snd); + if (rc) + goto on_exit; + + virtsnd_enable_event_vq(snd); + +on_exit: + if (rc) + virtsnd_remove(vdev); + + return rc; +} + +/** + * virtsnd_remove() - Remove VirtIO and ALSA devices. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + */ +static void virtsnd_remove(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + + virtsnd_disable_event_vq(snd); + + if (snd->card) + snd_card_free(snd->card); + + vdev->config->del_vqs(vdev); + vdev->config->reset(vdev); + + kfree(snd->event_msgs); +} + +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtsnd_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .validate = virtsnd_validate, + .probe = virtsnd_probe, + .remove = virtsnd_remove, +}; + +static int __init init(void) +{ + return register_virtio_driver(&virtsnd_driver); +} +module_init(init); + +static void __exit fini(void) +{ + unregister_virtio_driver(&virtsnd_driver); +} +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio sound card driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h new file mode 100644 index 000000000000..b903b1b12e90 --- /dev/null +++ b/sound/virtio/virtio_card.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2021 OpenSynergy GmbH + */ +#ifndef VIRTIO_SND_CARD_H +#define VIRTIO_SND_CARD_H + +#include +#include +#include +#include + +#define VIRTIO_SND_CARD_DRIVER "virtio-snd" +#define VIRTIO_SND_CARD_NAME "VirtIO SoundCard" + +/** + * struct virtio_snd_queue - Virtqueue wrapper structure. + * @lock: Used to synchronize access to a virtqueue. + * @vqueue: Underlying virtqueue. + */ +struct virtio_snd_queue { + spinlock_t lock; + struct virtqueue *vqueue; +}; + +/** + * struct virtio_snd - VirtIO sound card device. + * @vdev: Underlying virtio device. + * @queues: Virtqueue wrappers. + * @card: ALSA sound card. + * @event_msgs: Device events. + */ +struct virtio_snd { + struct virtio_device *vdev; + struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX]; + struct snd_card *card; + struct virtio_snd_event *event_msgs; +}; + +static inline struct virtio_snd_queue * +virtsnd_control_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_CONTROL]; +} + +static inline struct virtio_snd_queue * +virtsnd_event_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_EVENT]; +} + +static inline struct virtio_snd_queue * +virtsnd_tx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_TX]; +} + +static inline struct virtio_snd_queue * +virtsnd_rx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_RX]; +} + +#endif /* VIRTIO_SND_CARD_H */ -- cgit v1.2.3-70-g09d2 From 933f98be60a7b9c287acb081fb5d6659dd5e0441 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 21 Feb 2021 16:30:24 +0100 Subject: ASoC: constify of_phandle_args in snd_soc_get_dai_name() The pointer to of_phandle_args passed to snd_soc_get_dai_name() and of_xlate_dai_name() implementations is not modified. Since it is being used only to translate passed OF node to a DAI name, it should not be modified, so mark it as const for correctness and safer code. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210221153024.453583-1-krzk@kernel.org Signed-off-by: Mark Brown --- include/sound/soc-component.h | 4 ++-- include/sound/soc.h | 2 +- sound/soc/meson/aiu-acodec-ctrl.c | 2 +- sound/soc/meson/aiu-codec-ctrl.c | 2 +- sound/soc/meson/aiu.c | 4 ++-- sound/soc/meson/aiu.h | 2 +- sound/soc/qcom/lpass-cpu.c | 2 +- sound/soc/qcom/qdsp6/q6afe-dai.c | 2 +- sound/soc/soc-component.c | 2 +- sound/soc/soc-core.c | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 5b47768222b7..7dc75b39287f 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -101,7 +101,7 @@ struct snd_soc_component_driver { /* DT */ int (*of_xlate_dai_name)(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name); int (*of_xlate_dai_id)(struct snd_soc_component *comment, struct device_node *endpoint); @@ -450,7 +450,7 @@ void snd_soc_component_remove(struct snd_soc_component *component); int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component, struct device_node *ep); int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name); int snd_soc_component_compr_open(struct snd_compr_stream *cstream); void snd_soc_component_compr_free(struct snd_compr_stream *cstream, diff --git a/include/sound/soc.h b/include/sound/soc.h index bd38015d6c6d..78609ab331c8 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1219,7 +1219,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, struct device_node **bitclkmaster, struct device_node **framemaster); int snd_soc_get_dai_id(struct device_node *ep); -int snd_soc_get_dai_name(struct of_phandle_args *args, +int snd_soc_get_dai_name(const struct of_phandle_args *args, const char **dai_name); int snd_soc_of_get_dai_name(struct device_node *of_node, const char **dai_name); diff --git a/sound/soc/meson/aiu-acodec-ctrl.c b/sound/soc/meson/aiu-acodec-ctrl.c index 7078197e0cc5..27a6d3259c50 100644 --- a/sound/soc/meson/aiu-acodec-ctrl.c +++ b/sound/soc/meson/aiu-acodec-ctrl.c @@ -159,7 +159,7 @@ static const struct snd_kcontrol_new aiu_acodec_ctrl_controls[] = { }; static int aiu_acodec_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name) { return aiu_of_xlate_dai_name(component, args, dai_name, AIU_ACODEC); diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c index 4b773d3e8b07..c3ea733fce91 100644 --- a/sound/soc/meson/aiu-codec-ctrl.c +++ b/sound/soc/meson/aiu-codec-ctrl.c @@ -125,7 +125,7 @@ static const struct snd_soc_dapm_route aiu_hdmi_ctrl_routes[] = { }; static int aiu_hdmi_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name) { return aiu_of_xlate_dai_name(component, args, dai_name, AIU_HDMI); diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index dc35ca79021c..ba15d5762b0b 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -42,7 +42,7 @@ static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { }; int aiu_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name, unsigned int component_id) { @@ -72,7 +72,7 @@ int aiu_of_xlate_dai_name(struct snd_soc_component *component, } static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name) { return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU); diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 87aa19ac4af3..393b6c2307e4 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -45,7 +45,7 @@ struct aiu { SNDRV_PCM_FMTBIT_S24_LE) int aiu_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name, unsigned int component_id); diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index c642e5f8f28c..4762286b33fe 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -340,7 +340,7 @@ int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name) { struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 4e1f101281e7..e8915519f427 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -1315,7 +1315,7 @@ static struct snd_soc_dai_driver q6afe_dais[] = { }; static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name) { int id = args->args[0]; diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 159bf88b9f8c..8415e9bd2932 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -370,7 +370,7 @@ int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component, } int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, + const struct of_phandle_args *args, const char **dai_name) { if (component->driver->of_xlate_dai_name) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0cffc9527e28..16ba54eb8164 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2998,7 +2998,7 @@ int snd_soc_get_dai_id(struct device_node *ep) } EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); -int snd_soc_get_dai_name(struct of_phandle_args *args, +int snd_soc_get_dai_name(const struct of_phandle_args *args, const char **dai_name) { struct snd_soc_component *pos; -- cgit v1.2.3-70-g09d2 From 452801cabc0a0f0cb742e98617d3f0e8a2b11295 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Mar 2021 00:02:22 +0100 Subject: ASoC: rt5645: Move rt5645_platform_data to sound/soc/codecs/rt5645.c sound/soc/codecs/rt5645.c is the only user of the rt5645_platform_data, move its definition to sound/soc/codecs/rt5645.c and remove the now empty include/sound/rt5645.h file. Note since the DMI quirk mechanism uses pointers to the rt5645_platform_data struct we can NOT simply add its members to the rt5645_priv struct and completely remove the struct. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210306230223.516566-1-hdegoede@redhat.com Signed-off-by: Mark Brown --- include/sound/rt5645.h | 32 -------------------------------- sound/soc/codecs/rt5645.c | 23 ++++++++++++++++++++++- sound/soc/codecs/rt5645.h | 2 -- 3 files changed, 22 insertions(+), 35 deletions(-) delete mode 100644 include/sound/rt5645.h (limited to 'include') diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h deleted file mode 100644 index 710c95be5509..000000000000 --- a/include/sound/rt5645.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * linux/sound/rt5645.h -- Platform data for RT5645 - * - * Copyright 2013 Realtek Microelectronics - */ - -#ifndef __LINUX_SND_RT5645_H -#define __LINUX_SND_RT5645_H - -struct rt5645_platform_data { - /* IN2 can optionally be differential */ - bool in2_diff; - - unsigned int dmic1_data_pin; - /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */ - unsigned int dmic2_data_pin; - /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */ - - unsigned int jd_mode; - /* Use level triggered irq */ - bool level_trigger_irq; - /* Invert JD1_1 status polarity */ - bool inv_jd1_1; - /* Invert HP detect status polarity */ - bool inv_hp_pol; - - /* Value to asign to snd_soc_card.long_name */ - const char *long_name; -}; - -#endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index c39095fa14ce..3849a9987541 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -411,6 +411,27 @@ static const char *const rt5645_supply_names[] = { "cpvdd", }; +struct rt5645_platform_data { + /* IN2 can optionally be differential */ + bool in2_diff; + + unsigned int dmic1_data_pin; + /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */ + unsigned int dmic2_data_pin; + /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */ + + unsigned int jd_mode; + /* Use level triggered irq */ + bool level_trigger_irq; + /* Invert JD1_1 status polarity */ + bool inv_jd1_1; + /* Invert HP detect status polarity */ + bool inv_hp_pol; + + /* Value to assign to snd_soc_card.long_name */ + const char *long_name; +}; + struct rt5645_priv { struct snd_soc_component *component; struct rt5645_platform_data pdata; @@ -3834,7 +3855,7 @@ static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) static int rt5645_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5645_platform_data *pdata = NULL; const struct dmi_system_id *dmi_data; struct rt5645_priv *rt5645; int ret, i; diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index e2d72ae17484..ac3de6f3bc2f 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -9,8 +9,6 @@ #ifndef __RT5645_H__ #define __RT5645_H__ -#include - /* Info */ #define RT5645_RESET 0x00 #define RT5645_VENDOR_ID 0xfd -- cgit v1.2.3-70-g09d2 From 531590bb40f827fb3c4398148af0797f95bbaee2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 9 Mar 2021 10:08:17 +0900 Subject: ASoC: soc-pcm: share DPCM BE DAI stop operation soc-pcm has very similar but different DPCM BE DAI stop operation at 1) dpcm_be_dai_startup() error case rollback 2) dpcm_be_dai_startup_unwind() 3) dpcm_be_dai_shutdown() The differences are 1) for rollback 2) Doesn't check by snd_soc_dpcm_be_can_update() (Is this bug ?) 3) Do soc_pcm_hw_free() if it was not !OPENed and !HW_FREEed, and call soc_pcm_close(). We can share same code by 1) hw_free is not needed. Needs last dpcm as rollback. 2) hw_free is not needed. 3) hw_free is needed. This patch adds new dpcm_be_dai_stop() and share these 3. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87a6rduoam.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 8 ++++- sound/soc/soc-compress.c | 2 +- sound/soc/soc-pcm.c | 94 +++++++++++------------------------------------- 3 files changed, 28 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 0f6c50b17bba..d76cb1eeeaca 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -149,7 +149,8 @@ void dpcm_path_put(struct snd_soc_dapm_widget_list **list); int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list, int new); int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream); -int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream); +void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, + int do_hw_free, struct snd_soc_dpcm *last); void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); @@ -159,4 +160,9 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event); +#define dpcm_be_dai_startup_rollback(fe, stream, last) \ + dpcm_be_dai_stop(fe, stream, 0, last) +#define dpcm_be_dai_startup_unwind(fe, stream) dpcm_be_dai_stop(fe, stream, 0, NULL) +#define dpcm_be_dai_shutdown(fe, stream) dpcm_be_dai_stop(fe, stream, 1, NULL) + #endif diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 246a5e32e22a..89445ba0e86b 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -189,7 +189,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) if (ret < 0) dev_err(fe->dev, "Compressed ASoC: hw_free failed: %d\n", ret); - ret = dpcm_be_dai_shutdown(fe, stream); + dpcm_be_dai_shutdown(fe, stream); /* mark FE's links ready to prune */ for_each_dpcm_be(fe, stream, dpcm) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 0ae386f0790e..a27385ab7b55 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1417,18 +1417,24 @@ void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); } -static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, - int stream) +void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, + int do_hw_free, struct snd_soc_dpcm *last) { struct snd_soc_dpcm *dpcm; /* disable any enabled and non active backends */ for_each_dpcm_be(fe, stream, dpcm) { - struct snd_soc_pcm_runtime *be = dpcm->be; struct snd_pcm_substream *be_substream = snd_soc_dpcm_get_substream(be, stream); + if (dpcm == last) + return; + + /* is this op for this BE ? */ + if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + continue; + if (be->dpcm[stream].users == 0) { dev_err(be->dev, "ASoC: no users %s at close - state %d\n", stream ? "capture" : "playback", @@ -1439,8 +1445,15 @@ static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, if (--be->dpcm[stream].users != 0) continue; - if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) - continue; + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) { + if (!do_hw_free) + continue; + + if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) { + soc_pcm_hw_free(be_substream); + be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; + } + } soc_pcm_close(be_substream); be_substream->runtime = NULL; @@ -1509,32 +1522,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) return count; unwind: - /* disable any enabled and non active backends */ - for_each_dpcm_be_rollback(fe, stream, dpcm) { - struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_pcm_substream *be_substream = - snd_soc_dpcm_get_substream(be, stream); - - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) - continue; - - if (be->dpcm[stream].users == 0) { - dev_err(be->dev, "ASoC: no users %s at close %d\n", - stream ? "capture" : "playback", - be->dpcm[stream].state); - continue; - } - - if (--be->dpcm[stream].users != 0) - continue; - - if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) - continue; - - soc_pcm_close(be_substream); - be_substream->runtime = NULL; - be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; - } + dpcm_be_dai_startup_rollback(fe, stream, dpcm); return err; } @@ -1782,46 +1770,6 @@ be_err: return ret; } -int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) -{ - struct snd_soc_dpcm *dpcm; - - /* only shutdown BEs that are either sinks or sources to this FE DAI */ - for_each_dpcm_be(fe, stream, dpcm) { - - struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_pcm_substream *be_substream = - snd_soc_dpcm_get_substream(be, stream); - - /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) - continue; - - if (be->dpcm[stream].users == 0) - dev_err(be->dev, "ASoC: no users %s at close - state %d\n", - stream ? "capture" : "playback", - be->dpcm[stream].state); - - if (--be->dpcm[stream].users != 0) - continue; - - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) { - soc_pcm_hw_free(be_substream); - be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; - } - - dev_dbg(be->dev, "ASoC: close BE %s\n", - be->dai_link->name); - - soc_pcm_close(be_substream); - be_substream->runtime = NULL; - - be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; - } - return 0; -} - static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); @@ -2371,9 +2319,7 @@ static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) if (err < 0) dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err); - err = dpcm_be_dai_shutdown(fe, stream); - if (err < 0) - dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err); + dpcm_be_dai_shutdown(fe, stream); /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); -- cgit v1.2.3-70-g09d2 From 1d49439c04792a4a3d8299a32b7673ab7ba13b77 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 15 Mar 2021 16:38:42 -0300 Subject: ASoC: mx27vis: Remove unused file i.MX has been converted to a devicetree-only platform and asoc-mx27vis.h is no longer used. Get rid of this unused file. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20210315193842.183042-1-festevam@gmail.com Signed-off-by: Mark Brown --- include/linux/platform_data/asoc-mx27vis.h | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 include/linux/platform_data/asoc-mx27vis.h (limited to 'include') diff --git a/include/linux/platform_data/asoc-mx27vis.h b/include/linux/platform_data/asoc-mx27vis.h deleted file mode 100644 index 2107d0d992dd..000000000000 --- a/include/linux/platform_data/asoc-mx27vis.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __PLATFORM_DATA_ASOC_MX27VIS_H -#define __PLATFORM_DATA_ASOC_MX27VIS_H - -struct snd_mx27vis_platform_data { - int amp_gain0_gpio; - int amp_gain1_gpio; - int amp_mutel_gpio; - int amp_muter_gpio; -}; - -#endif /* __PLATFORM_DATA_ASOC_MX27VIS_H */ -- cgit v1.2.3-70-g09d2 From 48d71395896d54eec989179dd265e569fcecb15a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Sun, 14 Mar 2021 18:44:46 +0300 Subject: reset: Add reset_control_bulk API Follow the clock and regulator subsystems' lead and add a bulk API for reset controls. Signed-off-by: Philipp Zabel Tested-by: Dmitry Osipenko Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210314154459.15375-5-digetx@gmail.com Signed-off-by: Mark Brown --- drivers/reset/core.c | 215 ++++++++++++++++++++++++++++++++++ include/linux/reset.h | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 530 insertions(+) (limited to 'include') diff --git a/drivers/reset/core.c b/drivers/reset/core.c index dbf881b586d9..71c1c8264b2d 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -358,6 +358,30 @@ int reset_control_reset(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_reset); +/** + * reset_control_bulk_reset - reset the controlled devices in order + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset controls set + * + * Issue a reset on all provided reset controls, in order. + * + * See also: reset_control_reset() + */ +int reset_control_bulk_reset(int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + int ret, i; + + for (i = 0; i < num_rstcs; i++) { + ret = reset_control_reset(rstcs[i].rstc); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(reset_control_bulk_reset); + /** * reset_control_rearm - allow shared reset line to be re-triggered" * @rstc: reset controller @@ -461,6 +485,36 @@ int reset_control_assert(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_assert); +/** + * reset_control_bulk_assert - asserts the reset lines in order + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset controls set + * + * Assert the reset lines for all provided reset controls, in order. + * If an assertion fails, already asserted resets are deasserted again. + * + * See also: reset_control_assert() + */ +int reset_control_bulk_assert(int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + int ret, i; + + for (i = 0; i < num_rstcs; i++) { + ret = reset_control_assert(rstcs[i].rstc); + if (ret) + goto err; + } + + return 0; + +err: + while (i--) + reset_control_deassert(rstcs[i].rstc); + return ret; +} +EXPORT_SYMBOL_GPL(reset_control_bulk_assert); + /** * reset_control_deassert - deasserts the reset line * @rstc: reset controller @@ -511,6 +565,36 @@ int reset_control_deassert(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_deassert); +/** + * reset_control_bulk_deassert - deasserts the reset lines in reverse order + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset controls set + * + * Deassert the reset lines for all provided reset controls, in reverse order. + * If a deassertion fails, already deasserted resets are asserted again. + * + * See also: reset_control_deassert() + */ +int reset_control_bulk_deassert(int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + int ret, i; + + for (i = num_rstcs - 1; i >= 0; i--) { + ret = reset_control_deassert(rstcs[i].rstc); + if (ret) + goto err; + } + + return 0; + +err: + while (i < num_rstcs) + reset_control_assert(rstcs[i++].rstc); + return ret; +} +EXPORT_SYMBOL_GPL(reset_control_bulk_deassert); + /** * reset_control_status - returns a negative errno if not supported, a * positive value if the reset line is asserted, or zero if the reset @@ -588,6 +672,36 @@ int reset_control_acquire(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_acquire); +/** + * reset_control_bulk_acquire - acquires reset controls for exclusive use + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset controls set + * + * This is used to explicitly acquire reset controls requested with + * reset_control_bulk_get_exclusive_release() for temporary exclusive use. + * + * See also: reset_control_acquire(), reset_control_bulk_release() + */ +int reset_control_bulk_acquire(int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + int ret, i; + + for (i = 0; i < num_rstcs; i++) { + ret = reset_control_acquire(rstcs[i].rstc); + if (ret) + goto err; + } + + return 0; + +err: + while (i--) + reset_control_release(rstcs[i].rstc); + return ret; +} +EXPORT_SYMBOL_GPL(reset_control_bulk_acquire); + /** * reset_control_release() - releases exclusive access to a reset control * @rstc: reset control @@ -610,6 +724,26 @@ void reset_control_release(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_release); +/** + * reset_control_bulk_release() - releases exclusive access to reset controls + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset controls set + * + * Releases exclusive access right to reset controls previously obtained by a + * call to reset_control_bulk_acquire(). + * + * See also: reset_control_release(), reset_control_bulk_acquire() + */ +void reset_control_bulk_release(int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + int i; + + for (i = 0; i < num_rstcs; i++) + reset_control_release(rstcs[i].rstc); +} +EXPORT_SYMBOL_GPL(reset_control_bulk_release); + static struct reset_control *__reset_control_get_internal( struct reset_controller_dev *rcdev, unsigned int index, bool shared, bool acquired) @@ -814,6 +948,32 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id, } EXPORT_SYMBOL_GPL(__reset_control_get); +int __reset_control_bulk_get(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs, + bool shared, bool optional, bool acquired) +{ + int ret, i; + + for (i = 0; i < num_rstcs; i++) { + rstcs[i].rstc = __reset_control_get(dev, rstcs[i].id, 0, + shared, optional, acquired); + if (IS_ERR(rstcs[i].rstc)) { + ret = PTR_ERR(rstcs[i].rstc); + goto err; + } + } + + return 0; + +err: + mutex_lock(&reset_list_mutex); + while (i--) + __reset_control_put_internal(rstcs[i].rstc); + mutex_unlock(&reset_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(__reset_control_bulk_get); + static void reset_control_array_put(struct reset_control_array *resets) { int i; @@ -845,6 +1005,23 @@ void reset_control_put(struct reset_control *rstc) } EXPORT_SYMBOL_GPL(reset_control_put); +/** + * reset_control_bulk_put - free the reset controllers + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset controls set + */ +void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs) +{ + mutex_lock(&reset_list_mutex); + while (num_rstcs--) { + if (IS_ERR_OR_NULL(rstcs[num_rstcs].rstc)) + continue; + __reset_control_put_internal(rstcs[num_rstcs].rstc); + } + mutex_unlock(&reset_list_mutex); +} +EXPORT_SYMBOL_GPL(reset_control_bulk_put); + static void devm_reset_control_release(struct device *dev, void *res) { reset_control_put(*(struct reset_control **)res); @@ -874,6 +1051,44 @@ struct reset_control *__devm_reset_control_get(struct device *dev, } EXPORT_SYMBOL_GPL(__devm_reset_control_get); +struct reset_control_bulk_devres { + int num_rstcs; + struct reset_control_bulk_data *rstcs; +}; + +static void devm_reset_control_bulk_release(struct device *dev, void *res) +{ + struct reset_control_bulk_devres *devres = res; + + reset_control_bulk_put(devres->num_rstcs, devres->rstcs); +} + +int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs, + bool shared, bool optional, bool acquired) +{ + struct reset_control_bulk_devres *ptr; + int ret; + + ptr = devres_alloc(devm_reset_control_bulk_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = __reset_control_bulk_get(dev, num_rstcs, rstcs, shared, optional, acquired); + if (ret < 0) { + devres_free(ptr); + return ret; + } + + ptr->num_rstcs = num_rstcs; + ptr->rstcs = rstcs; + devres_add(dev, ptr); + + return 0; +} +EXPORT_SYMBOL_GPL(__devm_reset_control_bulk_get); + /** * __device_reset - find reset controller associated with the device * and perform reset diff --git a/include/linux/reset.h b/include/linux/reset.h index b9109efa2a5c..46e6372cb431 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -10,6 +10,21 @@ struct device; struct device_node; struct reset_control; +/** + * struct reset_control_bulk_data - Data used for bulk reset control operations. + * + * @id: reset control consumer ID + * @rstc: struct reset_control * to store the associated reset control + * + * The reset APIs provide a series of reset_control_bulk_*() API calls as + * a convenience to consumers which require multiple reset controls. + * This structure is used to manage data for these calls. + */ +struct reset_control_bulk_data { + const char *id; + struct reset_control *rstc; +}; + #ifdef CONFIG_RESET_CONTROLLER int reset_control_reset(struct reset_control *rstc); @@ -20,6 +35,12 @@ int reset_control_status(struct reset_control *rstc); int reset_control_acquire(struct reset_control *rstc); void reset_control_release(struct reset_control *rstc); +int reset_control_bulk_reset(int num_rstcs, struct reset_control_bulk_data *rstcs); +int reset_control_bulk_assert(int num_rstcs, struct reset_control_bulk_data *rstcs); +int reset_control_bulk_deassert(int num_rstcs, struct reset_control_bulk_data *rstcs); +int reset_control_bulk_acquire(int num_rstcs, struct reset_control_bulk_data *rstcs); +void reset_control_bulk_release(int num_rstcs, struct reset_control_bulk_data *rstcs); + struct reset_control *__of_reset_control_get(struct device_node *node, const char *id, int index, bool shared, bool optional, bool acquired); @@ -27,10 +48,18 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id, int index, bool shared, bool optional, bool acquired); void reset_control_put(struct reset_control *rstc); +int __reset_control_bulk_get(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs, + bool shared, bool optional, bool acquired); +void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs); + int __device_reset(struct device *dev, bool optional); struct reset_control *__devm_reset_control_get(struct device *dev, const char *id, int index, bool shared, bool optional, bool acquired); +int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs, + bool shared, bool optional, bool acquired); struct reset_control *devm_reset_control_array_get(struct device *dev, bool shared, bool optional); @@ -96,6 +125,48 @@ static inline struct reset_control *__reset_control_get( return optional ? NULL : ERR_PTR(-ENOTSUPP); } +static inline int +reset_control_bulk_reset(int num_rstcs, struct reset_control_bulk_data *rstcs) +{ + return 0; +} + +static inline int +reset_control_bulk_assert(int num_rstcs, struct reset_control_bulk_data *rstcs) +{ + return 0; +} + +static inline int +reset_control_bulk_deassert(int num_rstcs, struct reset_control_bulk_data *rstcs) +{ + return 0; +} + +static inline int +reset_control_bulk_acquire(int num_rstcs, struct reset_control_bulk_data *rstcs) +{ + return 0; +} + +static inline void +reset_control_bulk_release(int num_rstcs, struct reset_control_bulk_data *rstcs) +{ +} + +static inline int +__reset_control_bulk_get(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs, + bool shared, bool optional, bool acquired) +{ + return optional ? 0 : -EOPNOTSUPP; +} + +static inline void +reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs) +{ +} + static inline struct reset_control *__devm_reset_control_get( struct device *dev, const char *id, int index, bool shared, bool optional, @@ -104,6 +175,14 @@ static inline struct reset_control *__devm_reset_control_get( return optional ? NULL : ERR_PTR(-ENOTSUPP); } +static inline int +__devm_reset_control_bulk_get(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs, + bool shared, bool optional, bool acquired) +{ + return optional ? 0 : -EOPNOTSUPP; +} + static inline struct reset_control * devm_reset_control_array_get(struct device *dev, bool shared, bool optional) { @@ -155,6 +234,23 @@ __must_check reset_control_get_exclusive(struct device *dev, const char *id) return __reset_control_get(dev, id, 0, false, false, true); } +/** + * reset_control_bulk_get_exclusive - Lookup and obtain exclusive references to + * multiple reset controllers. + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Fills the rstcs array with pointers to exclusive reset controls and + * returns 0, or an IS_ERR() condition containing errno. + */ +static inline int __must_check +reset_control_bulk_get_exclusive(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __reset_control_bulk_get(dev, num_rstcs, rstcs, false, false, true); +} + /** * reset_control_get_exclusive_released - Lookup and obtain a temoprarily * exclusive reference to a reset @@ -176,6 +272,48 @@ __must_check reset_control_get_exclusive_released(struct device *dev, return __reset_control_get(dev, id, 0, false, false, false); } +/** + * reset_control_bulk_get_exclusive_released - Lookup and obtain temporarily + * exclusive references to multiple reset + * controllers. + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Fills the rstcs array with pointers to exclusive reset controls and + * returns 0, or an IS_ERR() condition containing errno. + * reset-controls returned by this function must be acquired via + * reset_control_bulk_acquire() before they can be used and should be released + * via reset_control_bulk_release() afterwards. + */ +static inline int __must_check +reset_control_bulk_get_exclusive_released(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __reset_control_bulk_get(dev, num_rstcs, rstcs, false, false, false); +} + +/** + * reset_control_bulk_get_optional_exclusive_released - Lookup and obtain optional + * temporarily exclusive references to multiple + * reset controllers. + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Optional variant of reset_control_bulk_get_exclusive_released(). If the + * requested reset is not specified in the device tree, this function returns 0 + * instead of an error and missing rtsc is set to NULL. + * + * See reset_control_bulk_get_exclusive_released() for more information. + */ +static inline int __must_check +reset_control_bulk_get_optional_exclusive_released(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __reset_control_bulk_get(dev, num_rstcs, rstcs, false, true, false); +} + /** * reset_control_get_shared - Lookup and obtain a shared reference to a * reset controller. @@ -204,6 +342,23 @@ static inline struct reset_control *reset_control_get_shared( return __reset_control_get(dev, id, 0, true, false, false); } +/** + * reset_control_bulk_get_shared - Lookup and obtain shared references to + * multiple reset controllers. + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Fills the rstcs array with pointers to shared reset controls and + * returns 0, or an IS_ERR() condition containing errno. + */ +static inline int __must_check +reset_control_bulk_get_shared(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __reset_control_bulk_get(dev, num_rstcs, rstcs, true, false, false); +} + /** * reset_control_get_optional_exclusive - optional reset_control_get_exclusive() * @dev: device to be reset by the controller @@ -221,6 +376,26 @@ static inline struct reset_control *reset_control_get_optional_exclusive( return __reset_control_get(dev, id, 0, false, true, true); } +/** + * reset_control_bulk_get_optional_exclusive - optional + * reset_control_bulk_get_exclusive() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Optional variant of reset_control_bulk_get_exclusive(). If any of the + * requested resets are not specified in the device tree, this function sets + * them to NULL instead of returning an error. + * + * See reset_control_bulk_get_exclusive() for more information. + */ +static inline int __must_check +reset_control_bulk_get_optional_exclusive(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __reset_control_bulk_get(dev, num_rstcs, rstcs, false, true, true); +} + /** * reset_control_get_optional_shared - optional reset_control_get_shared() * @dev: device to be reset by the controller @@ -238,6 +413,26 @@ static inline struct reset_control *reset_control_get_optional_shared( return __reset_control_get(dev, id, 0, true, true, false); } +/** + * reset_control_bulk_get_optional_shared - optional + * reset_control_bulk_get_shared() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Optional variant of reset_control_bulk_get_shared(). If the requested resets + * are not specified in the device tree, this function sets them to NULL + * instead of returning an error. + * + * See reset_control_bulk_get_shared() for more information. + */ +static inline int __must_check +reset_control_bulk_get_optional_shared(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __reset_control_bulk_get(dev, num_rstcs, rstcs, true, true, false); +} + /** * of_reset_control_get_exclusive - Lookup and obtain an exclusive reference * to a reset controller. @@ -343,6 +538,26 @@ __must_check devm_reset_control_get_exclusive(struct device *dev, return __devm_reset_control_get(dev, id, 0, false, false, true); } +/** + * devm_reset_control_bulk_get_exclusive - resource managed + * reset_control_bulk_get_exclusive() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Managed reset_control_bulk_get_exclusive(). For reset controllers returned + * from this function, reset_control_put() is called automatically on driver + * detach. + * + * See reset_control_bulk_get_exclusive() for more information. + */ +static inline int __must_check +devm_reset_control_bulk_get_exclusive(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __devm_reset_control_bulk_get(dev, num_rstcs, rstcs, false, false, true); +} + /** * devm_reset_control_get_exclusive_released - resource managed * reset_control_get_exclusive_released() @@ -362,6 +577,26 @@ __must_check devm_reset_control_get_exclusive_released(struct device *dev, return __devm_reset_control_get(dev, id, 0, false, false, false); } +/** + * devm_reset_control_bulk_get_exclusive_released - resource managed + * reset_control_bulk_get_exclusive_released() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Managed reset_control_bulk_get_exclusive_released(). For reset controllers + * returned from this function, reset_control_put() is called automatically on + * driver detach. + * + * See reset_control_bulk_get_exclusive_released() for more information. + */ +static inline int __must_check +devm_reset_control_bulk_get_exclusive_released(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __devm_reset_control_bulk_get(dev, num_rstcs, rstcs, false, false, false); +} + /** * devm_reset_control_get_optional_exclusive_released - resource managed * reset_control_get_optional_exclusive_released() @@ -381,6 +616,26 @@ __must_check devm_reset_control_get_optional_exclusive_released(struct device *d return __devm_reset_control_get(dev, id, 0, false, true, false); } +/** + * devm_reset_control_bulk_get_optional_exclusive_released - resource managed + * reset_control_bulk_optional_get_exclusive_released() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Managed reset_control_bulk_optional_get_exclusive_released(). For reset + * controllers returned from this function, reset_control_put() is called + * automatically on driver detach. + * + * See reset_control_bulk_optional_get_exclusive_released() for more information. + */ +static inline int __must_check +devm_reset_control_bulk_get_optional_exclusive_released(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __devm_reset_control_bulk_get(dev, num_rstcs, rstcs, false, true, false); +} + /** * devm_reset_control_get_shared - resource managed reset_control_get_shared() * @dev: device to be reset by the controller @@ -396,6 +651,26 @@ static inline struct reset_control *devm_reset_control_get_shared( return __devm_reset_control_get(dev, id, 0, true, false, false); } +/** + * devm_reset_control_bulk_get_shared - resource managed + * reset_control_bulk_get_shared() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Managed reset_control_bulk_get_shared(). For reset controllers returned + * from this function, reset_control_put() is called automatically on driver + * detach. + * + * See reset_control_bulk_get_shared() for more information. + */ +static inline int __must_check +devm_reset_control_bulk_get_shared(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __devm_reset_control_bulk_get(dev, num_rstcs, rstcs, true, false, false); +} + /** * devm_reset_control_get_optional_exclusive - resource managed * reset_control_get_optional_exclusive() @@ -414,6 +689,26 @@ static inline struct reset_control *devm_reset_control_get_optional_exclusive( return __devm_reset_control_get(dev, id, 0, false, true, true); } +/** + * devm_reset_control_bulk_get_optional_exclusive - resource managed + * reset_control_bulk_get_optional_exclusive() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Managed reset_control_bulk_get_optional_exclusive(). For reset controllers + * returned from this function, reset_control_put() is called automatically on + * driver detach. + * + * See reset_control_bulk_get_optional_exclusive() for more information. + */ +static inline int __must_check +devm_reset_control_bulk_get_optional_exclusive(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __devm_reset_control_bulk_get(dev, num_rstcs, rstcs, true, false, true); +} + /** * devm_reset_control_get_optional_shared - resource managed * reset_control_get_optional_shared() @@ -432,6 +727,26 @@ static inline struct reset_control *devm_reset_control_get_optional_shared( return __devm_reset_control_get(dev, id, 0, true, true, false); } +/** + * devm_reset_control_bulk_get_optional_shared - resource managed + * reset_control_bulk_get_optional_shared() + * @dev: device to be reset by the controller + * @num_rstcs: number of entries in rstcs array + * @rstcs: array of struct reset_control_bulk_data with reset line names set + * + * Managed reset_control_bulk_get_optional_shared(). For reset controllers + * returned from this function, reset_control_put() is called automatically on + * driver detach. + * + * See reset_control_bulk_get_optional_shared() for more information. + */ +static inline int __must_check +devm_reset_control_bulk_get_optional_shared(struct device *dev, int num_rstcs, + struct reset_control_bulk_data *rstcs) +{ + return __devm_reset_control_bulk_get(dev, num_rstcs, rstcs, true, true, false); +} + /** * devm_reset_control_get_exclusive_by_index - resource managed * reset_control_get_exclusive() -- cgit v1.2.3-70-g09d2 From f52366e6831ecb2da133b6ecfc7c69266086660c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Mar 2021 09:58:32 +0900 Subject: ASoC: soc-pcm: don't indicate error message for dpcm_be_dai_hw_free() dpcm_be_dai_hw_free() never fail, error message is not needed. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87blblutaf.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 2 +- sound/soc/soc-compress.c | 5 +---- sound/soc/soc-pcm.c | 14 ++++---------- 3 files changed, 6 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index d76cb1eeeaca..e296a3949b18 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -153,7 +153,7 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, int do_hw_free, struct snd_soc_dpcm *last); void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream); -int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); +void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int tream); int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd); int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 94f1f7a9dd53..83b511f8b8c9 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -175,7 +175,6 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); struct snd_soc_dpcm *dpcm; int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ - int ret; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); @@ -183,9 +182,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; - ret = dpcm_be_dai_hw_free(fe, stream); - if (ret < 0) - dev_err(fe->dev, "Compressed ASoC: hw_free failed: %d\n", ret); + dpcm_be_dai_hw_free(fe, stream); dpcm_be_dai_shutdown(fe, stream); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index eb52f78ca053..2a126840677c 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1808,7 +1808,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) return 0; } -int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) +void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1847,14 +1847,12 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; } - - return 0; } static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); - int err, stream = substream->stream; + int stream = substream->stream; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); @@ -1866,9 +1864,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) /* only hw_params backends that are either sinks or sources * to this frontend DAI */ - err = dpcm_be_dai_hw_free(fe, stream); - if (err < 0) - dev_err(fe->dev, "ASoC: hw_free BE failed %d\n", err); + dpcm_be_dai_hw_free(fe, stream); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); @@ -2330,9 +2326,7 @@ static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); } - err = dpcm_be_dai_hw_free(fe, stream); - if (err < 0) - dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err); + dpcm_be_dai_hw_free(fe, stream); dpcm_be_dai_shutdown(fe, stream); -- cgit v1.2.3-70-g09d2 From 12b2b508300d08206674bfb3f53bb84f69cf2555 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 18 Mar 2021 17:06:17 +0100 Subject: ALSA: hda: Change AZX_MAX_BUF_SIZE from 1GB to 4MB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When SND_HDA_PREALLOC_SIZE is set to 0, applications can request as much memory as there is allowed. With value of AZX_MAX_BUF_SIZE it is 1GB per stream, which is not realistic use case. Change it 4MB. Bug: https://bugzilla.kernel.org/show_bug.cgi?id=201251#c322 Suggested-by: Takashi Iwai Reviewed-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20210318160618.2504068-3-amadeuszx.slawinski@linux.intel.com Signed-off-by: Takashi Iwai --- include/sound/hda_register.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index 4f987b1f32f7..ad8b71b1dbb6 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -140,8 +140,12 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define BDL_SIZE 4096 #define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) #define AZX_MAX_FRAG 32 -/* max buffer size - no h/w limit, you can increase as you like */ -#define AZX_MAX_BUF_SIZE (1024*1024*1024) +/* + * max buffer size - artificial 4MB limit per stream to avoid big allocations + * In theory it can be really big, but as it is per stream on systems with many streams memory could + * be quickly saturated if userspace requests maximum buffer size for each of them. + */ +#define AZX_MAX_BUF_SIZE (4*1024*1024) /* RIRB int mask: overrun[2], response[0] */ #define RIRB_INT_RESPONSE 0x01 -- cgit v1.2.3-70-g09d2 From b951b51e2ca4d37dc9781e14d8a49d2f2b7e715b Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 19 Mar 2021 14:49:49 +0200 Subject: ASoC: SOF: add a helper to get topology configured mclk Add helper sof_dai_ssp_mclk to get the topology configured MCLK from a pcm_runtime, return 0 if it is not available, and error if the dai type is not SSP at the moment. Export the helper for external use, e.g. from machine drivers. Signed-off-by: Keyon Jie Reviewed-by: Guennadi Liakhovetski Reviewed-by: Daniel Baluta Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20210319124950.3853994-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 1 + sound/soc/sof/sof-audio.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/include/sound/sof.h b/include/sound/sof.h index 646a655c3c6b..b93bb8038080 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -103,5 +103,6 @@ struct sof_dev_desc { int sof_nocodec_setup(struct device *dev, const struct snd_sof_dsp_ops *ops, int (*pcm_dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params)); +int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd); #endif diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 3277489fee5e..928d7a46d820 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -433,6 +433,33 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, return NULL; } +/* + * Helper to get SSP MCLK from a pcm_runtime. + * Return 0 if not exist. + */ +int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); + struct snd_sof_dai *dai = + snd_sof_find_dai(component, (char *)rtd->dai_link->name); + + /* use the tplg configured mclk if existed */ + if (!dai || !dai->dai_config) + return 0; + + switch (dai->dai_config->type) { + case SOF_DAI_INTEL_SSP: + return dai->dai_config->ssp.mclk_rate; + default: + /* not yet implemented for platforms other than the above */ + dev_err(rtd->dev, "mclk for dai_config->type %d not supported yet!\n", + dai->dai_config->type); + return -EINVAL; + } +} +EXPORT_SYMBOL(sof_dai_get_mclk); + /* * SOF Driver enumeration. */ -- cgit v1.2.3-70-g09d2 From 8bdfc0455e3a59e2c1207a56be22e910fae0e0d5 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 12 Mar 2021 10:38:40 +0800 Subject: ASoC: soc-component: Add snd_soc_pcm_component_ack Add snd_soc_pcm_component_ack back, which can be used to get an updated buffer pointer in the platform driver. On Asymmetric multiprocessor, this pointer can be sent to Cortex-M core for audio processing. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1615516725-4975-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 3 +++ sound/soc/soc-component.c | 14 ++++++++++++++ sound/soc/soc-pcm.c | 2 ++ 3 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 7dc75b39287f..722cfab28d29 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -146,6 +146,8 @@ struct snd_soc_component_driver { int (*mmap)(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct vm_area_struct *vma); + int (*ack)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); const struct snd_compress_ops *compress_ops; @@ -498,5 +500,6 @@ int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd, void *stream); void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, void *stream, int rollback); +int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream); #endif /* __SOC_COMPONENT_H */ diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 8415e9bd2932..3a5e84e16a87 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1212,3 +1212,17 @@ void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, soc_component_mark_pop(component, stream, pm); } } + +int snd_soc_pcm_component_ack(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_component *component; + int i; + + /* FIXME: use 1st pointer */ + for_each_rtd_components(rtd, i, component) + if (component->driver->ack) + return component->driver->ack(component, substream); + + return 0; +} diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2df70ab851ea..02968a4e52b4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2772,6 +2772,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.page = snd_soc_pcm_component_page; if (drv->mmap) rtd->ops.mmap = snd_soc_pcm_component_mmap; + if (drv->ack) + rtd->ops.ack = snd_soc_pcm_component_ack; } if (playback) -- cgit v1.2.3-70-g09d2 From 4da40cb9955c63c3aca02be267faea4abbd2c649 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 22 Mar 2021 11:48:15 +0900 Subject: ASoC: soc.h: add asoc_link_to_cpu/codec/platform() macro We shouldn't use dai_link->cpus/codecs/platforms directly, because these are array now to supporting multi CPU/Codec/Platform. This patch adds asoc_link_to_xxx() macro for it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/874kh3aopc.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 78609ab331c8..e4161071f300 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -716,20 +716,38 @@ struct snd_soc_dai_link { struct snd_soc_dobj dobj; /* For topology */ #endif }; + +static inline struct snd_soc_dai_link_component* +asoc_link_to_cpu(struct snd_soc_dai_link *link, int n) { + return &(link)->cpus[n]; +} + +static inline struct snd_soc_dai_link_component* +asoc_link_to_codec(struct snd_soc_dai_link *link, int n) { + return &(link)->codecs[n]; +} + +static inline struct snd_soc_dai_link_component* +asoc_link_to_platform(struct snd_soc_dai_link *link, int n) { + return &(link)->platforms[n]; +} + #define for_each_link_codecs(link, i, codec) \ for ((i) = 0; \ - ((i) < link->num_codecs) && ((codec) = &link->codecs[i]); \ + ((i) < link->num_codecs) && \ + ((codec) = asoc_link_to_codec(link, i)); \ (i)++) #define for_each_link_platforms(link, i, platform) \ for ((i) = 0; \ ((i) < link->num_platforms) && \ - ((platform) = &link->platforms[i]); \ + ((platform) = asoc_link_to_platform(link, i)); \ (i)++) #define for_each_link_cpus(link, i, cpu) \ for ((i) = 0; \ - ((i) < link->num_cpus) && ((cpu) = &link->cpus[i]); \ + ((i) < link->num_cpus) && \ + ((cpu) = asoc_link_to_cpu(link, i)); \ (i)++) /* -- cgit v1.2.3-70-g09d2 From 4a50724eb0ba96b849f4a0c8da28b2b796859f9e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 22 Mar 2021 11:48:40 +0900 Subject: ASoC: soc.h: fixup return timing for snd_soc_fixup_dai_links_platform_name() Current snd_soc_fixup_dai_links_platform_name() creates name first (A), and checks setup target pointer (B), and set it (C). We should check target pointer first IMO. This patch exchange the order to (B) -> (A) -> (C). int snd_soc_fixup_dai_links_platform_name(...) { ... /* set platform name for each dailink */ for_each_card_prelinks(card, i, dai_link) { (A) name = devm_kstrdup(...); if (!name) return -ENOMEM; (B) if (!dai_link->platforms) return -EINVAL; /* only single platform is supported for now */ (C) dai_link->platforms->name = name; } return 0; } Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/8735wnaoon.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index e4161071f300..200815ca4112 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1280,13 +1280,13 @@ int snd_soc_fixup_dai_links_platform_name(struct snd_soc_card *card, /* set platform name for each dailink */ for_each_card_prelinks(card, i, dai_link) { + if (!dai_link->platforms) + return -EINVAL; + name = devm_kstrdup(card->dev, platform_name, GFP_KERNEL); if (!name) return -ENOMEM; - if (!dai_link->platforms) - return -EINVAL; - /* only single platform is supported for now */ dai_link->platforms->name = name; } -- cgit v1.2.3-70-g09d2 From d908b922c71791568384336ccc3d12a8cbcd1777 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 22 Mar 2021 11:48:54 +0900 Subject: ASoC: soc.h: return error if multi platform at snd_soc_fixup_dai_links_platform_name() snd_soc_fixup_dai_links_platform_name() is assuming it is single platform. return error if multi platforms. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/871rc7aoo9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 200815ca4112..e746da996351 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1280,6 +1280,10 @@ int snd_soc_fixup_dai_links_platform_name(struct snd_soc_card *card, /* set platform name for each dailink */ for_each_card_prelinks(card, i, dai_link) { + /* only single platform is supported for now */ + if (dai_link->num_platforms != 1) + return -EINVAL; + if (!dai_link->platforms) return -EINVAL; -- cgit v1.2.3-70-g09d2 From 1fa4445f9adf19a3028ce0e8f375bac75214fc10 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 17 Mar 2021 18:29:40 +0100 Subject: ALSA: control - introduce snd_ctl_notify_one() helper This helper is required for the following generic LED mute patch. The helper also simplifies some other functions. Signed-off-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20210317172945.842280-2-perex@perex.cz Signed-off-by: Takashi Iwai --- include/sound/control.h | 4 +-- sound/core/control.c | 68 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/sound/control.h b/include/sound/control.h index 77d9fa10812d..22f3d48163ff 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -115,6 +115,7 @@ typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card, unsigned int cmd, unsigned long arg); void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id); +void snd_ctl_notify_one(struct snd_card * card, unsigned int mask, struct snd_kcontrol * kctl, unsigned int ioff); struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data); void snd_ctl_free_one(struct snd_kcontrol * kcontrol); @@ -123,8 +124,7 @@ int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol); int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace); int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id); int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id); -int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, - int active); +int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int active); struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid); struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id); diff --git a/sound/core/control.c b/sound/core/control.c index 5165741a8400..8a5cedb0a4be 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -181,6 +181,27 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, } EXPORT_SYMBOL(snd_ctl_notify); +/** + * snd_ctl_notify_one - Send notification to user-space for a control change + * @card: the card to send notification + * @mask: the event mask, SNDRV_CTL_EVENT_* + * @kctl: the pointer with the control instance + * @ioff: the additional offset to the control index + * + * This function calls snd_ctl_notify() and does additional jobs + * like LED state changes. + */ +void snd_ctl_notify_one(struct snd_card *card, unsigned int mask, + struct snd_kcontrol *kctl, unsigned int ioff) +{ + struct snd_ctl_elem_id id = kctl->id; + + id.index += ioff; + id.numid += ioff; + snd_ctl_notify(card, mask, &id); +} +EXPORT_SYMBOL(snd_ctl_notify_one); + /** * snd_ctl_new - create a new control instance with some elements * @kctl: the pointer to store new control instance @@ -342,7 +363,6 @@ static int __snd_ctl_add_replace(struct snd_card *card, { struct snd_ctl_elem_id id; unsigned int idx; - unsigned int count; struct snd_kcontrol *old; int err; @@ -376,10 +396,8 @@ static int __snd_ctl_add_replace(struct snd_card *card, kcontrol->id.numid = card->last_numid + 1; card->last_numid += kcontrol->count; - id = kcontrol->id; - count = kcontrol->count; - for (idx = 0; idx < count; idx++, id.index++, id.numid++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); + for (idx = 0; idx < kcontrol->count; idx++) + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx); return 0; } @@ -462,16 +480,14 @@ EXPORT_SYMBOL(snd_ctl_replace); */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { - struct snd_ctl_elem_id id; unsigned int idx; if (snd_BUG_ON(!card || !kcontrol)) return -EINVAL; list_del(&kcontrol->list); card->controls_count -= kcontrol->count; - id = kcontrol->id; - for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id); + for (idx = 0; idx < kcontrol->count; idx++) + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx); snd_ctl_free_one(kcontrol); return 0; } @@ -584,11 +600,13 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; } snd_ctl_build_ioff(id, kctl, index_offset); - ret = 1; + downgrade_write(&card->controls_rwsem); + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset); + up_read(&card->controls_rwsem); + return 1; + unlock: up_write(&card->controls_rwsem); - if (ret > 0) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id); return ret; } EXPORT_SYMBOL_GPL(snd_ctl_activate_id); @@ -1110,25 +1128,34 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, unsigned int index_offset; int result; + down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, &control->id); - if (kctl == NULL) + if (kctl == NULL) { + up_write(&card->controls_rwsem); return -ENOENT; + } index_offset = snd_ctl_get_ioff(kctl, &control->id); vd = &kctl->vd[index_offset]; if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL || (file && vd->owner && vd->owner != file)) { + up_write(&card->controls_rwsem); return -EPERM; } snd_ctl_build_ioff(&control->id, kctl, index_offset); result = kctl->put(kctl, control); - if (result < 0) + if (result < 0) { + up_write(&card->controls_rwsem); return result; + } if (result > 0) { - struct snd_ctl_elem_id id = control->id; - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id); + downgrade_write(&card->controls_rwsem); + snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset); + up_read(&card->controls_rwsem); + } else { + up_write(&card->controls_rwsem); } return 0; @@ -1150,9 +1177,7 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file, if (result < 0) goto error; - down_write(&card->controls_rwsem); result = snd_ctl_elem_write(card, file, control); - up_write(&card->controls_rwsem); if (result < 0) goto error; @@ -1301,7 +1326,6 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf, { struct user_element *ue = kctl->private_data; unsigned int *container; - struct snd_ctl_elem_id id; unsigned int mask = 0; int i; int change; @@ -1333,10 +1357,8 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf, ue->tlv_data_size = size; mask |= SNDRV_CTL_EVENT_MASK_TLV; - for (i = 0; i < kctl->count; ++i) { - snd_ctl_build_ioff(&id, kctl, i); - snd_ctl_notify(ue->card, mask, &id); - } + for (i = 0; i < kctl->count; ++i) + snd_ctl_notify_one(ue->card, mask, kctl, i); return change; } -- cgit v1.2.3-70-g09d2 From 3f0638a0333bfdd0549985aa620f2ab69737af47 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 17 Mar 2021 18:29:41 +0100 Subject: ALSA: control - add layer registration routines The layer registration allows to handle an extra functionality on top of the control API. It can be used for the audio LED control for example. Signed-off-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20210317172945.842280-3-perex@perex.cz Signed-off-by: Takashi Iwai --- include/sound/control.h | 12 ++++++ sound/core/control.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/control.h b/include/sound/control.h index 22f3d48163ff..175610bfa8c8 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -108,6 +108,14 @@ struct snd_ctl_file { struct list_head events; /* waiting events for read */ }; +struct snd_ctl_layer_ops { + struct snd_ctl_layer_ops *next; + const char *module_name; + void (*lregister)(struct snd_card *card); + void (*ldisconnect)(struct snd_card *card); + void (*lnotify)(struct snd_card *card, unsigned int mask, struct snd_kcontrol *kctl, unsigned int ioff); +}; + #define snd_ctl_file(n) list_entry(n, struct snd_ctl_file, list) typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card, @@ -140,6 +148,10 @@ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn); #define snd_ctl_unregister_ioctl_compat(fcn) #endif +int snd_ctl_request_layer(const char *module_name); +void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops); +void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops); + int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type); static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id) diff --git a/sound/core/control.c b/sound/core/control.c index 8a5cedb0a4be..87630021e434 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -28,10 +28,12 @@ struct snd_kctl_ioctl { }; static DECLARE_RWSEM(snd_ioctl_rwsem); +static DECLARE_RWSEM(snd_ctl_layer_rwsem); static LIST_HEAD(snd_control_ioctls); #ifdef CONFIG_COMPAT static LIST_HEAD(snd_control_compat_ioctls); #endif +static struct snd_ctl_layer_ops *snd_ctl_layer; static int snd_ctl_open(struct inode *inode, struct file *file) { @@ -195,10 +197,15 @@ void snd_ctl_notify_one(struct snd_card *card, unsigned int mask, struct snd_kcontrol *kctl, unsigned int ioff) { struct snd_ctl_elem_id id = kctl->id; + struct snd_ctl_layer_ops *lops; id.index += ioff; id.numid += ioff; snd_ctl_notify(card, mask, &id); + down_read(&snd_ctl_layer_rwsem); + for (lops = snd_ctl_layer; lops; lops = lops->next) + lops->lnotify(card, mask, kctl, ioff); + up_read(&snd_ctl_layer_rwsem); } EXPORT_SYMBOL(snd_ctl_notify_one); @@ -1997,6 +2004,86 @@ EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice); #define snd_ctl_ioctl_compat NULL #endif +/* + * control layers (audio LED etc.) + */ + +/** + * snd_ctl_request_layer - request to use the layer + * @module_name: Name of the kernel module (NULL == build-in) + * + * Return an error code when the module cannot be loaded. + */ +int snd_ctl_request_layer(const char *module_name) +{ + struct snd_ctl_layer_ops *lops; + + if (module_name == NULL) + return 0; + down_read(&snd_ctl_layer_rwsem); + for (lops = snd_ctl_layer; lops; lops = lops->next) + if (strcmp(lops->module_name, module_name) == 0) + break; + up_read(&snd_ctl_layer_rwsem); + if (lops) + return 0; + return request_module(module_name); +} +EXPORT_SYMBOL_GPL(snd_ctl_request_layer); + +/** + * snd_ctl_register_layer - register new control layer + * @lops: operation structure + * + * The new layer can track all control elements and do additional + * operations on top (like audio LED handling). + */ +void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops) +{ + struct snd_card *card; + int card_number; + + down_write(&snd_ctl_layer_rwsem); + lops->next = snd_ctl_layer; + snd_ctl_layer = lops; + up_write(&snd_ctl_layer_rwsem); + for (card_number = 0; card_number < SNDRV_CARDS; card_number++) { + card = snd_card_ref(card_number); + if (card) { + down_read(&card->controls_rwsem); + lops->lregister(card); + up_read(&card->controls_rwsem); + snd_card_unref(card); + } + } +} +EXPORT_SYMBOL_GPL(snd_ctl_register_layer); + +/** + * snd_ctl_disconnect_layer - disconnect control layer + * @lops: operation structure + * + * It is expected that the information about tracked cards + * is freed before this call (the disconnect callback is + * not called here). + */ +void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops) +{ + struct snd_ctl_layer_ops *lops2, *prev_lops2; + + down_write(&snd_ctl_layer_rwsem); + for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next) + if (lops2 == lops) { + if (!prev_lops2) + snd_ctl_layer = lops->next; + else + prev_lops2->next = lops->next; + break; + } + up_write(&snd_ctl_layer_rwsem); +} +EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer); + /* * INIT PART */ @@ -2020,9 +2107,20 @@ static const struct file_operations snd_ctl_f_ops = static int snd_ctl_dev_register(struct snd_device *device) { struct snd_card *card = device->device_data; + struct snd_ctl_layer_ops *lops; + int err; - return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, - &snd_ctl_f_ops, card, &card->ctl_dev); + err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, + &snd_ctl_f_ops, card, &card->ctl_dev); + if (err < 0) + return err; + down_read(&card->controls_rwsem); + down_read(&snd_ctl_layer_rwsem); + for (lops = snd_ctl_layer; lops; lops = lops->next) + lops->lregister(card); + up_read(&snd_ctl_layer_rwsem); + up_read(&card->controls_rwsem); + return 0; } /* @@ -2032,6 +2130,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) { struct snd_card *card = device->device_data; struct snd_ctl_file *ctl; + struct snd_ctl_layer_ops *lops; unsigned long flags; read_lock_irqsave(&card->ctl_files_rwlock, flags); @@ -2041,6 +2140,13 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) } read_unlock_irqrestore(&card->ctl_files_rwlock, flags); + down_read(&card->controls_rwsem); + down_read(&snd_ctl_layer_rwsem); + for (lops = snd_ctl_layer; lops; lops = lops->next) + lops->ldisconnect(card); + up_read(&snd_ctl_layer_rwsem); + up_read(&card->controls_rwsem); + return snd_unregister_device(&card->ctl_dev); } -- cgit v1.2.3-70-g09d2 From 22d8de62f11b287b279f1d4473a78c7d5e53e7bc Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 17 Mar 2021 18:29:42 +0100 Subject: ALSA: control - add generic LED trigger module as the new control layer The recent laptops have usually two LEDs assigned to reflect the speaker and microphone mute state. This implementation adds a tiny layer on top of the control API which calculates the state for those LEDs using the driver callbacks. Two new access flags are introduced to describe the controls which affects the audio path settings (an easy code change for drivers). The LED resource can be shared with multiple sound cards with this code. The user space controls may be added to the state chain on demand, too. This code should replace the LED code in the HDA driver and add a possibility to easy extend the other drivers (ASoC codecs etc.). Signed-off-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20210317172945.842280-4-perex@perex.cz Signed-off-by: Takashi Iwai --- include/sound/control.h | 19 +++- sound/core/Kconfig | 6 + sound/core/Makefile | 2 + sound/core/control.c | 4 +- sound/core/control_led.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 sound/core/control_led.c (limited to 'include') diff --git a/include/sound/control.h b/include/sound/control.h index 175610bfa8c8..985c51a8fb74 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -24,7 +24,7 @@ typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, /* internal flag for skipping validations */ #ifdef CONFIG_SND_CTL_VALIDATION -#define SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK (1 << 27) +#define SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK (1 << 24) #define snd_ctl_skip_validation(info) \ ((info)->access & SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK) #else @@ -32,6 +32,12 @@ typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, #define snd_ctl_skip_validation(info) true #endif +/* kernel only - LED bits */ +#define SNDRV_CTL_ELEM_ACCESS_LED_SHIFT 25 +#define SNDRV_CTL_ELEM_ACCESS_LED_MASK (7<<25) /* kernel three bits - LED group */ +#define SNDRV_CTL_ELEM_ACCESS_SPK_LED (1<<25) /* kernel speaker (output) LED flag */ +#define SNDRV_CTL_ELEM_ACCESS_MIC_LED (2<<25) /* kernel microphone (input) LED flag */ + enum { SNDRV_CTL_TLV_OP_READ = 0, SNDRV_CTL_TLV_OP_WRITE = 1, @@ -265,6 +271,17 @@ int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl, void *arg), void *arg); +/* + * Control LED trigger layer + */ +#define SND_CTL_LAYER_MODULE_LED "snd-ctl-led" + +#if IS_MODULE(CONFIG_SND_CTL_LED) +static inline int snd_ctl_led_request(void) { return snd_ctl_request_layer(SND_CTL_LAYER_MODULE_LED); } +#else +static inline int snd_ctl_led_request(void) { return 0; } +#endif + /* * Helper functions for jack-detection controls */ diff --git a/sound/core/Kconfig b/sound/core/Kconfig index a4050f87f230..db2e3c63ff41 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -203,4 +203,10 @@ config SND_DMA_SGBUF def_bool y depends on X86 +config SND_CTL_LED + tristate + select NEW_LEDS if SND_CTL_LED + select LEDS_TRIGGERS if SND_CTL_LED + select LEDS_TRIGGER_AUDIO if SND_CTL_LED + source "sound/core/seq/Kconfig" diff --git a/sound/core/Makefile b/sound/core/Makefile index ee4a4a6b99ba..d774792850f3 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -27,6 +27,7 @@ CFLAGS_pcm_native.o := -I$(src) snd-pcm-dmaengine-objs := pcm_dmaengine.o +snd-ctl-led-objs := control_led.o snd-rawmidi-objs := rawmidi.o snd-timer-objs := timer.o snd-hrtimer-objs := hrtimer.o @@ -37,6 +38,7 @@ snd-seq-device-objs := seq_device.o snd-compress-objs := compress_offload.o obj-$(CONFIG_SND) += snd.o +obj-$(CONFIG_SND_CTL_LED) += snd-ctl-led.o obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o diff --git a/sound/core/control.c b/sound/core/control.c index 87630021e434..6825ca75daf5 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -278,6 +278,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK | + SNDRV_CTL_ELEM_ACCESS_LED_MASK | SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK); err = snd_ctl_new(&kctl, count, access, NULL); @@ -1047,7 +1048,8 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl, if (result < 0) return result; /* drop internal access flags */ - info.access &= ~SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK; + info.access &= ~(SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK| + SNDRV_CTL_ELEM_ACCESS_LED_MASK); if (copy_to_user(_info, &info, sizeof(info))) return -EFAULT; return result; diff --git a/sound/core/control_led.c b/sound/core/control_led.c new file mode 100644 index 000000000000..3e2c3c485c5c --- /dev/null +++ b/sound/core/control_led.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * LED state routines for driver control interface + * Copyright (c) 2021 by Jaroslav Kysela + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ALSA control interface to LED trigger code."); +MODULE_LICENSE("GPL"); + +#define MAX_LED (((SNDRV_CTL_ELEM_ACCESS_MIC_LED - SNDRV_CTL_ELEM_ACCESS_SPK_LED) \ + >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) + 1) + +struct snd_ctl_led { + struct list_head list; + struct snd_card *card; + unsigned int access; + struct snd_kcontrol *kctl; + unsigned int index_offset; +}; + +static DEFINE_MUTEX(snd_ctl_led_mutex); +static struct list_head snd_ctl_led_controls[MAX_LED]; +static bool snd_ctl_led_card_valid[SNDRV_CARDS]; + +#define UPDATE_ROUTE(route, cb) \ + do { \ + int route2 = (cb); \ + if (route2 >= 0) \ + route = route < 0 ? route2 : (route | route2); \ + } while (0) + +static inline unsigned int access_to_group(unsigned int access) +{ + return ((access & SNDRV_CTL_ELEM_ACCESS_LED_MASK) >> + SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1; +} + +static inline unsigned int group_to_access(unsigned int group) +{ + return (group + 1) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT; +} + +static struct list_head *snd_ctl_led_controls_by_access(unsigned int access) +{ + unsigned int group = access_to_group(access); + if (group >= MAX_LED) + return NULL; + return &snd_ctl_led_controls[group]; +} + +static int snd_ctl_led_get(struct snd_ctl_led *lctl) +{ + struct snd_kcontrol *kctl = lctl->kctl; + struct snd_ctl_elem_info info; + struct snd_ctl_elem_value value; + unsigned int i; + int result; + + memset(&info, 0, sizeof(info)); + info.id = kctl->id; + info.id.index += lctl->index_offset; + info.id.numid += lctl->index_offset; + result = kctl->info(kctl, &info); + if (result < 0) + return -1; + memset(&value, 0, sizeof(value)); + value.id = info.id; + result = kctl->get(kctl, &value); + if (result < 0) + return -1; + if (info.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + info.type == SNDRV_CTL_ELEM_TYPE_INTEGER) { + for (i = 0; i < info.count; i++) + if (value.value.integer.value[i] != info.value.integer.min) + return 1; + } else if (info.type == SNDRV_CTL_ELEM_TYPE_INTEGER64) { + for (i = 0; i < info.count; i++) + if (value.value.integer64.value[i] != info.value.integer64.min) + return 1; + } + return 0; +} + +static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access, + struct snd_kcontrol *kctl, unsigned int ioff) +{ + struct list_head *controls; + struct snd_ctl_led *lctl; + enum led_audio led_trigger_type; + int route; + bool found; + + controls = snd_ctl_led_controls_by_access(access); + if (!controls) + return; + if (access == SNDRV_CTL_ELEM_ACCESS_SPK_LED) { + led_trigger_type = LED_AUDIO_MUTE; + } else if (access == SNDRV_CTL_ELEM_ACCESS_MIC_LED) { + led_trigger_type = LED_AUDIO_MICMUTE; + } else { + return; + } + route = -1; + found = false; + mutex_lock(&snd_ctl_led_mutex); + /* the card may not be registered (active) at this point */ + if (card && !snd_ctl_led_card_valid[card->number]) { + mutex_unlock(&snd_ctl_led_mutex); + return; + } + list_for_each_entry(lctl, controls, list) { + if (lctl->kctl == kctl && lctl->index_offset == ioff) + found = true; + UPDATE_ROUTE(route, snd_ctl_led_get(lctl)); + } + if (!found && kctl && card) { + lctl = kzalloc(sizeof(*lctl), GFP_KERNEL); + if (lctl) { + lctl->card = card; + lctl->access = access; + lctl->kctl = kctl; + lctl->index_offset = ioff; + list_add(&lctl->list, controls); + UPDATE_ROUTE(route, snd_ctl_led_get(lctl)); + } + } + mutex_unlock(&snd_ctl_led_mutex); + if (route >= 0) + ledtrig_audio_set(led_trigger_type, route ? LED_OFF : LED_ON); +} + +static struct snd_ctl_led *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff) +{ + struct list_head *controls; + struct snd_ctl_led *lctl; + unsigned int group; + + for (group = 0; group < MAX_LED; group++) { + controls = &snd_ctl_led_controls[group]; + list_for_each_entry(lctl, controls, list) + if (lctl->kctl == kctl && lctl->index_offset == ioff) + return lctl; + } + return NULL; +} + +static unsigned int snd_ctl_led_remove(struct snd_kcontrol *kctl, unsigned int ioff, + unsigned int access) +{ + struct snd_ctl_led *lctl; + unsigned int ret = 0; + + mutex_lock(&snd_ctl_led_mutex); + lctl = snd_ctl_led_find(kctl, ioff); + if (lctl && (access == 0 || access != lctl->access)) { + ret = lctl->access; + list_del(&lctl->list); + kfree(lctl); + } + mutex_unlock(&snd_ctl_led_mutex); + return ret; +} + +static void snd_ctl_led_notify(struct snd_card *card, unsigned int mask, + struct snd_kcontrol *kctl, unsigned int ioff) +{ + struct snd_kcontrol_volatile *vd; + unsigned int access, access2; + + if (mask == SNDRV_CTL_EVENT_MASK_REMOVE) { + access = snd_ctl_led_remove(kctl, ioff, 0); + if (access) + snd_ctl_led_set_state(card, access, NULL, 0); + } else if (mask & SNDRV_CTL_EVENT_MASK_INFO) { + vd = &kctl->vd[ioff]; + access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK; + access2 = snd_ctl_led_remove(kctl, ioff, access); + if (access2) + snd_ctl_led_set_state(card, access2, NULL, 0); + if (access) + snd_ctl_led_set_state(card, access, kctl, ioff); + } else if ((mask & (SNDRV_CTL_EVENT_MASK_ADD | + SNDRV_CTL_EVENT_MASK_VALUE)) != 0) { + vd = &kctl->vd[ioff]; + access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK; + if (access) + snd_ctl_led_set_state(card, access, kctl, ioff); + } +} + +static void snd_ctl_led_refresh(void) +{ + unsigned int group; + + for (group = 0; group < MAX_LED; group++) + snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0); +} + +static void snd_ctl_led_clean(struct snd_card *card) +{ + unsigned int group; + struct list_head *controls; + struct snd_ctl_led *lctl; + + for (group = 0; group < MAX_LED; group++) { + controls = &snd_ctl_led_controls[group]; +repeat: + list_for_each_entry(lctl, controls, list) + if (!card || lctl->card == card) { + list_del(&lctl->list); + kfree(lctl); + goto repeat; + } + } +} + +static void snd_ctl_led_register(struct snd_card *card) +{ + struct snd_kcontrol *kctl; + unsigned int ioff; + + if (snd_BUG_ON(card->number < 0 || + card->number >= ARRAY_SIZE(snd_ctl_led_card_valid))) + return; + mutex_lock(&snd_ctl_led_mutex); + snd_ctl_led_card_valid[card->number] = true; + mutex_unlock(&snd_ctl_led_mutex); + /* the register callback is already called with held card->controls_rwsem */ + list_for_each_entry(kctl, &card->controls, list) + for (ioff = 0; ioff < kctl->count; ioff++) + snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, ioff); + snd_ctl_led_refresh(); +} + +static void snd_ctl_led_disconnect(struct snd_card *card) +{ + mutex_lock(&snd_ctl_led_mutex); + snd_ctl_led_card_valid[card->number] = false; + snd_ctl_led_clean(card); + mutex_unlock(&snd_ctl_led_mutex); + snd_ctl_led_refresh(); +} + +/* + * Control layer registration + */ +static struct snd_ctl_layer_ops snd_ctl_led_lops = { + .module_name = SND_CTL_LAYER_MODULE_LED, + .lregister = snd_ctl_led_register, + .ldisconnect = snd_ctl_led_disconnect, + .lnotify = snd_ctl_led_notify, +}; + +static int __init snd_ctl_led_init(void) +{ + unsigned int group; + + for (group = 0; group < MAX_LED; group++) + INIT_LIST_HEAD(&snd_ctl_led_controls[group]); + snd_ctl_register_layer(&snd_ctl_led_lops); + return 0; +} + +static void __exit snd_ctl_led_exit(void) +{ + snd_ctl_disconnect_layer(&snd_ctl_led_lops); + snd_ctl_led_clean(NULL); +} + +module_init(snd_ctl_led_init) +module_exit(snd_ctl_led_exit) -- cgit v1.2.3-70-g09d2 From 050c7950fd706fec229af9f30e8ce254cea9b675 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Mar 2021 12:26:06 +0900 Subject: ASoC: simple-card-utils: alloc dai_link information for CPU/Codec/Platform simple-card / audio-graph are assuming single CPU/Codec/Platform on dai_link. Because of it, it is difficult to support Multi-CPU/Codec. This patch allocs CPU/Codec/Platform dai_link imformation instead of using existing props information. It can update to multi-CPU/Codec, but is still assuming single-CPU/Codec for now. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87blb61tpv.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 7 ++++--- sound/soc/generic/simple-card-utils.c | 15 +++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index ba4a3e1897b9..86e46cbf9e14 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -43,9 +43,9 @@ struct asoc_simple_priv { struct simple_dai_props { struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; - struct snd_soc_dai_link_component cpus; /* single cpu */ - struct snd_soc_dai_link_component codecs; /* single codec */ - struct snd_soc_dai_link_component platforms; + struct snd_soc_dai_link_component *cpus; + struct snd_soc_dai_link_component *codecs; + struct snd_soc_dai_link_component *platforms; struct asoc_simple_data adata; struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; @@ -54,6 +54,7 @@ struct asoc_simple_priv { struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; + struct snd_soc_dai_link_component *dlcs; struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; const struct snd_soc_ops *ops; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 6897455219d1..f05954529dfc 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -601,13 +601,15 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct snd_soc_dai_link *dai_link; struct simple_dai_props *dai_props; struct asoc_simple_dai *dais; + struct snd_soc_dai_link_component *dlcs; struct snd_soc_codec_conf *cconf = NULL; int i; dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL); dais = devm_kcalloc(dev, li->dais, sizeof(*dais), GFP_KERNEL); - if (!dai_props || !dai_link || !dais) + dlcs = devm_kcalloc(dev, li->link * 3, sizeof(*dai_props), GFP_KERNEL); + if (!dai_props || !dai_link || !dais || !dlcs) return -ENOMEM; if (li->conf) { @@ -622,17 +624,22 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, * simple-card-utils.c :: asoc_simple_canonicalize_platform() */ for (i = 0; i < li->link; i++) { - dai_link[i].cpus = &dai_props[i].cpus; + dai_props[i].cpus = dlcs + (3 * i) + 0; + dai_props[i].codecs = dlcs + (3 * i) + 1; + dai_props[i].platforms = dlcs + (3 * i) + 2; + + dai_link[i].cpus = dai_props[i].cpus; dai_link[i].num_cpus = 1; - dai_link[i].codecs = &dai_props[i].codecs; + dai_link[i].codecs = dai_props[i].codecs; dai_link[i].num_codecs = 1; - dai_link[i].platforms = &dai_props[i].platforms; + dai_link[i].platforms = dai_props[i].platforms; dai_link[i].num_platforms = 1; } priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; + priv->dlcs = dlcs; priv->codec_conf = cconf; card->dai_link = priv->dai_link; -- cgit v1.2.3-70-g09d2 From 66c6d1ef86ff3c1466e646d94c8eb3dcc9ccf873 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 8 Apr 2021 19:31:49 +0900 Subject: ALSA: control: Add memory consumption limit to user controls ALSA control interface allows users to add arbitrary control elements (called "user controls" or "user elements"), and its resource usage is limited just by the max number of control sets (currently 32). This limit, however, is quite loose: each allocation of control set may have 1028 elements, and each element may have up to 512 bytes (ILP32) or 1024 bytes (LP64) of value data. Moreover, each control set may contain the enum strings and TLV data, which can be up to 64kB and 128kB, respectively. Totally, the whole memory consumption may go over 38MB -- it's quite large, and we'd rather like to reduce the size. OTOH, there have been other requests even to increase the max number of user elements; e.g. ALSA firewire stack require the more user controls, hence we want to raise the bar, too. For satisfying both requirements, this patch changes the management of user controls: instead of setting the upper limit of the number of user controls, we check the actual memory allocation size and set the upper limit of the total allocation in bytes. As long as the memory consumption stays below the limit, more user controls are allowed than the current limit 32. At the same time, we set the lower limit (8MB) as default than the current theoretical limit, in order to lower the risk of DoS. As a compromise for lowering the default limit, now the actual memory limit is defined as a module option, 'max_user_ctl_alloc_size', so that user can increase/decrease the limit if really needed, too. Link: https://lore.kernel.org/r/s5htur3zl5e.wl-tiwai@suse.de Co-developed-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Tested-by: Takashi Sakamoto Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20210408103149.40357-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai --- include/sound/core.h | 2 +- sound/core/control.c | 75 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index 2e24f194ef70..1f9aef0adbc9 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -100,7 +100,7 @@ struct snd_card { struct rw_semaphore controls_rwsem; /* controls list lock */ rwlock_t ctl_files_rwlock; /* ctl_files list lock */ int controls_count; /* count of all controls */ - int user_ctl_count; /* count of all user controls */ + size_t user_ctl_alloc_size; // current memory allocation by user controls. struct list_head controls; /* all controls for this card */ struct list_head ctl_files; /* active control files */ diff --git a/sound/core/control.c b/sound/core/control.c index 20d707d4ef40..a076c08c21b6 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -18,8 +19,11 @@ #include #include -/* max number of user-defined controls */ -#define MAX_USER_CONTROLS 32 +// Max allocation size for user controls. +static int max_user_ctl_alloc_size = 8 * 1024 * 1024; +module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444); +MODULE_PARM_DESC(max_user_ctl_alloc_size, "Max allocation size for user controls"); + #define MAX_CONTROL_COUNT 1028 struct snd_kctl_ioctl { @@ -561,9 +565,6 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, goto error; } ret = snd_ctl_remove(card, kctl); - if (ret < 0) - goto error; - card->user_ctl_count--; error: up_write(&card->controls_rwsem); return ret; @@ -1265,6 +1266,12 @@ struct user_element { void *priv_data; /* private data (like strings for enumerated type) */ }; +// check whether the addition (in bytes) of user ctl element may overflow the limit. +static bool check_user_elem_overflow(struct snd_card *card, ssize_t add) +{ + return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size; +} + static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1342,6 +1349,10 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf, if (size > 1024 * 128) /* sane value */ return -EINVAL; + // does the TLV size change cause overflow? + if (check_user_elem_overflow(ue->card, (ssize_t)(size - ue->tlv_data_size))) + return -ENOMEM; + container = vmemdup_user(buf, size); if (IS_ERR(container)) return PTR_ERR(container); @@ -1359,11 +1370,16 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf, for (i = 0; i < kctl->count; ++i) kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; mask = SNDRV_CTL_EVENT_MASK_INFO; + } else { + ue->card->user_ctl_alloc_size -= ue->tlv_data_size; + ue->tlv_data_size = 0; + kvfree(ue->tlv_data); } - kvfree(ue->tlv_data); ue->tlv_data = container; ue->tlv_data_size = size; + // decremented at private_free. + ue->card->user_ctl_alloc_size += size; mask |= SNDRV_CTL_EVENT_MASK_TLV; for (i = 0; i < kctl->count; ++i) @@ -1405,16 +1421,17 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue) unsigned int i; const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr; - if (ue->info.value.enumerated.names_length > 64 * 1024) + buf_len = ue->info.value.enumerated.names_length; + if (buf_len > 64 * 1024) return -EINVAL; - names = vmemdup_user((const void __user *)user_ptrval, - ue->info.value.enumerated.names_length); + if (check_user_elem_overflow(ue->card, buf_len)) + return -ENOMEM; + names = vmemdup_user((const void __user *)user_ptrval, buf_len); if (IS_ERR(names)) return PTR_ERR(names); /* check that there are enough valid names */ - buf_len = ue->info.value.enumerated.names_length; p = names; for (i = 0; i < ue->info.value.enumerated.items; ++i) { name_len = strnlen(p, buf_len); @@ -1428,14 +1445,27 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue) ue->priv_data = names; ue->info.value.enumerated.names_ptr = 0; + // increment the allocation size; decremented again at private_free. + ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length; return 0; } +static size_t compute_user_elem_size(size_t size, unsigned int count) +{ + return sizeof(struct user_element) + size * count; +} + static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) { struct user_element *ue = kcontrol->private_data; + // decrement the allocation size. + ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count); + ue->card->user_ctl_alloc_size -= ue->tlv_data_size; + if (ue->priv_data) + ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length; + kvfree(ue->tlv_data); kvfree(ue->priv_data); kfree(ue); @@ -1449,6 +1479,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, unsigned int count; unsigned int access; long private_size; + size_t alloc_size; struct user_element *ue; unsigned int offset; int err; @@ -1466,13 +1497,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, return err; } - /* - * The number of userspace controls are counted control by control, - * not element by element. - */ - if (card->user_ctl_count + 1 > MAX_USER_CONTROLS) - return -ENOMEM; - /* Check the number of elements for this userspace control. */ count = info->owner; if (count == 0) @@ -1503,6 +1527,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (info->count < 1) return -EINVAL; private_size = value_sizes[info->type] * info->count; + alloc_size = compute_user_elem_size(private_size, count); + + if (check_user_elem_overflow(card, alloc_size)) + return -ENOMEM; /* * Keep memory object for this userspace control. After passing this @@ -1514,16 +1542,18 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (err < 0) return err; memcpy(&kctl->id, &info->id, sizeof(kctl->id)); - kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count, - GFP_KERNEL); - if (kctl->private_data == NULL) { + ue = kzalloc(alloc_size, GFP_KERNEL); + if (!ue) { kfree(kctl); return -ENOMEM; } + kctl->private_data = ue; kctl->private_free = snd_ctl_elem_user_free; + // increment the allocated size; decremented again at private_free. + card->user_ctl_alloc_size += alloc_size; + /* Set private data for this userspace control. */ - ue = (struct user_element *)kctl->private_data; ue->card = card; ue->info = *info; ue->info.access = 0; @@ -1565,9 +1595,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, * applications because the field originally means PID of a process * which locks the element. */ - - card->user_ctl_count++; - unlock: up_write(&card->controls_rwsem); return err; -- cgit v1.2.3-70-g09d2 From f2138aed231c88d5c4fa8d06aa15ad19685087c2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 1 Apr 2021 13:15:23 +0900 Subject: ASoC: simple-card-utils: enable flexible CPU/Codec/Platform Current simple-card / audio-graph are assuming fixed single-CPU/Codec/Platform. This patch prepares multi-CPU/Codec/Platform support. Note is that it is not yet full-multi-support. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87v996od2c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 8 ++++ sound/soc/generic/audio-graph-card.c | 22 +++++++++++ sound/soc/generic/simple-card-utils.c | 72 ++++++++++++++++++++++++----------- sound/soc/generic/simple-card.c | 30 +++++++++++++++ 4 files changed, 110 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 86e46cbf9e14..475f8cb14492 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -38,6 +38,12 @@ struct asoc_simple_jack { struct snd_soc_jack_gpio gpio; }; +struct prop_nums { + int cpus; + int codecs; + int platforms; +}; + struct asoc_simple_priv { struct snd_soc_card snd_card; struct simple_dai_props { @@ -48,6 +54,7 @@ struct asoc_simple_priv { struct snd_soc_dai_link_component *platforms; struct asoc_simple_data adata; struct snd_soc_codec_conf *codec_conf; + struct prop_nums num; unsigned int mclk_fs; } *dai_props; struct asoc_simple_jack hp_jack; @@ -71,6 +78,7 @@ struct link_info { int link; /* number of link */ int conf; /* number of codec_conf */ int cpu; /* turn for CPU / Codec */ + struct prop_nums num[SNDRV_MINOR_DEVICES]; }; int asoc_simple_parse_daifmt(struct device *dev, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 76036ea377a9..5fe53c5efcbf 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -628,6 +628,15 @@ static int graph_count_noml(struct asoc_simple_priv *priv, { struct device *dev = simple_priv_to_dev(priv); + if (li->link >= SNDRV_MINOR_DEVICES) { + dev_err(dev, "too many links\n"); + return -EINVAL; + } + + li->num[li->link].cpus = 1; + li->num[li->link].codecs = 1; + li->num[li->link].platforms = 1; + li->link += 1; /* 1xCPU-Codec */ li->dais += 2; /* 1xCPU + 1xCodec */ @@ -643,10 +652,23 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, { struct device *dev = simple_priv_to_dev(priv); + if (li->link >= SNDRV_MINOR_DEVICES) { + dev_err(dev, "too many links\n"); + return -EINVAL; + } + if (li->cpu) { + li->num[li->link].cpus = 1; + li->num[li->link].codecs = 1; + li->num[li->link].platforms = 1; + li->link++; /* 1xCPU-dummy */ li->dais++; /* 1xCPU */ } else { + li->num[li->link].cpus = 1; + li->num[li->link].codecs = 1; + li->num[li->link].platforms = 1; + li->link++; /* 1xdummy-Codec */ li->conf++; /* 1xdummy-Codec */ li->dais++; /* 1xCodec */ diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 3010ff63c71d..1606b9bc6b71 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -604,13 +604,27 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct asoc_simple_dai *dais; struct snd_soc_dai_link_component *dlcs; struct snd_soc_codec_conf *cconf = NULL; - int i; + int i, dai_num = 0, dlc_num = 0; dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL); - dais = devm_kcalloc(dev, li->dais, sizeof(*dais), GFP_KERNEL); - dlcs = devm_kcalloc(dev, li->link * 3, sizeof(*dai_props), GFP_KERNEL); - if (!dai_props || !dai_link || !dais || !dlcs) + if (!dai_props || !dai_link) + return -ENOMEM; + + /* + * dais (= CPU+Codec) + * dlcs (= CPU+Codec+Platform) + */ + for (i = 0; i < li->link; i++) { + int cc = li->num[i].cpus + li->num[i].codecs; + + dai_num += cc; + dlc_num += cc + li->num[i].platforms; + } + + dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL); + dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dai_props), GFP_KERNEL); + if (!dais || !dlcs) return -ENOMEM; if (li->conf) { @@ -619,24 +633,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, return -ENOMEM; } - /* - * "platform" might be removed - * see - * simple-card-utils.c :: asoc_simple_canonicalize_platform() - */ - for (i = 0; i < li->link; i++) { - dai_props[i].cpus = dlcs + (3 * i) + 0; - dai_props[i].codecs = dlcs + (3 * i) + 1; - dai_props[i].platforms = dlcs + (3 * i) + 2; - - dai_link[i].cpus = dai_props[i].cpus; - dai_link[i].num_cpus = 1; - dai_link[i].codecs = dai_props[i].codecs; - dai_link[i].num_codecs = 1; - dai_link[i].platforms = dai_props[i].platforms; - dai_link[i].num_platforms = 1; - } - priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; @@ -648,6 +644,38 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, card->codec_conf = cconf; card->num_configs = li->conf; + for (i = 0; i < li->link; i++) { + if (li->num[i].cpus) { + /* Normal CPU */ + dai_props[i].cpus = + dai_link[i].cpus = dlcs; + dai_props[i].num.cpus = + dai_link[i].num_cpus = li->num[i].cpus; + + dlcs += li->num[i].cpus; + } + + if (li->num[i].codecs) { + /* Normal Codec */ + dai_props[i].codecs = + dai_link[i].codecs = dlcs; + dai_props[i].num.codecs = + dai_link[i].num_codecs = li->num[i].codecs; + + dlcs += li->num[i].codecs; + } + + if (li->num[i].platforms) { + /* Have Platform */ + dai_props[i].platforms = + dai_link[i].platforms = dlcs; + dai_props[i].num.platforms = + dai_link[i].num_platforms = li->num[i].platforms; + + dlcs += li->num[i].platforms; + } + } + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_init_priv); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 9a05f44fc3a9..1fad570338b3 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -499,6 +499,17 @@ static int simple_count_noml(struct asoc_simple_priv *priv, struct device_node *codec, struct link_info *li, bool is_top) { + if (li->link >= SNDRV_MINOR_DEVICES) { + struct device *dev = simple_priv_to_dev(priv); + + dev_err(dev, "too many links\n"); + return -EINVAL; + } + + li->num[li->link].cpus = 1; + li->num[li->link].codecs = 1; + li->num[li->link].platforms = 1; + li->link += 1; li->dais += 2; @@ -510,10 +521,25 @@ static int simple_count_dpcm(struct asoc_simple_priv *priv, struct device_node *codec, struct link_info *li, bool is_top) { + if (li->link >= SNDRV_MINOR_DEVICES) { + struct device *dev = simple_priv_to_dev(priv); + + dev_err(dev, "too many links\n"); + return -EINVAL; + } + if (li->cpu) { + li->num[li->link].cpus = 1; + li->num[li->link].codecs = 1; + li->num[li->link].platforms = 1; + li->link++; /* CPU-dummy */ li->dais++; } else { + li->num[li->link].cpus = 1; + li->num[li->link].codecs = 1; + li->num[li->link].platforms = 1; + li->link++; /* dummy-Codec */ li->dais++; li->conf++; @@ -575,6 +601,10 @@ static void simple_get_dais_count(struct asoc_simple_priv *priv, * => 1 ccnf = 1xdummy-Codec */ if (!top) { + li->num[0].cpus = 1; + li->num[0].codecs = 1; + li->num[0].platforms = 1; + li->link = 1; li->dais = 2; li->conf = 0; -- cgit v1.2.3-70-g09d2 From 205eb17eddb473c3159743c7d3aaf68db37b7231 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 1 Apr 2021 13:15:33 +0900 Subject: ASoC: simple-card-utils: share dummy DAI and reduce memory Current simple-card / audio-graph creates 1xCPU + 1xCodec + 1xPlatform for all dai_link, but some of them is not needed. For example Platform is not needed for DPCM BE case. Moreover, we can share snd-soc-dummy DAI for CPU-dummy / dummy-Codec in DPCM. This patch adds dummy DAI and share it when DPCM case, I beliave it can contribute to reduce memory. By this patch, CPU-dummy / dummy-CPU are set at asoc_simple_init_priv(), thus, its settings are no longer needed at DPCM detecting timing on simple-card / audio-graph. Moreover, we can remove triky Platform settings code for DPCM BE, because un-needed Platform is not created. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87tuoqod22.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 1 + sound/soc/generic/audio-graph-card.c | 12 +----------- sound/soc/generic/simple-card-utils.c | 30 +++++++++++++++++++++++------- sound/soc/generic/simple-card.c | 12 +----------- 4 files changed, 26 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 475f8cb14492..6635283a8160 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -62,6 +62,7 @@ struct asoc_simple_priv { struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; struct snd_soc_dai_link_component *dlcs; + struct snd_soc_dai_link_component dummy; struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; const struct snd_soc_ops *ops; diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 5fe53c5efcbf..7be2c2cb253e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -240,9 +240,6 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, int is_single_links = 0; /* Codec is dummy */ - codecs->of_node = NULL; - codecs->dai_name = "snd-soc-dummy-dai"; - codecs->name = "snd-soc-dummy"; /* FE settings */ dai_link->dynamic = 1; @@ -281,13 +278,11 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, /* card->num_links includes Codec */ asoc_simple_canonicalize_cpu(dai_link, is_single_links); + asoc_simple_canonicalize_platform(dai_link); } else { struct snd_soc_codec_conf *cconf; /* CPU is dummy */ - cpus->of_node = NULL; - cpus->dai_name = "snd-soc-dummy-dai"; - cpus->name = "snd-soc-dummy"; /* BE settings */ dai_link->no_pcm = 1; @@ -328,8 +323,6 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, graph_parse_convert(dev, ep, &dai_props->adata); graph_parse_mclk_fs(top, ep, dai_props); - asoc_simple_canonicalize_platform(dai_link); - ret = asoc_simple_parse_tdm(ep, dai); if (ret) goto out_put_node; @@ -659,15 +652,12 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, if (li->cpu) { li->num[li->link].cpus = 1; - li->num[li->link].codecs = 1; li->num[li->link].platforms = 1; li->link++; /* 1xCPU-dummy */ li->dais++; /* 1xCPU */ } else { - li->num[li->link].cpus = 1; li->num[li->link].codecs = 1; - li->num[li->link].platforms = 1; li->link++; /* 1xdummy-Codec */ li->conf++; /* 1xdummy-Codec */ diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 1606b9bc6b71..64b1ff5bafda 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -404,13 +404,6 @@ void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link) /* Assumes platform == cpu */ if (!dai_link->platforms->of_node) dai_link->platforms->of_node = dai_link->cpus->of_node; - - /* - * DPCM BE can be no platform. - * Alloced memory will be waste, but not leak. - */ - if (!dai_link->platforms->of_node) - dai_link->num_platforms = 0; } EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform); @@ -633,6 +626,11 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, return -ENOMEM; } + /* dummy CPU/Codec */ + priv->dummy.of_node = NULL; + priv->dummy.dai_name = "snd-soc-dummy-dai"; + priv->dummy.name = "snd-soc-dummy"; + priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; @@ -653,6 +651,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dai_link[i].num_cpus = li->num[i].cpus; dlcs += li->num[i].cpus; + } else { + /* DPCM Be's CPU = dummy */ + dai_props[i].cpus = + dai_link[i].cpus = &priv->dummy; + dai_props[i].num.cpus = + dai_link[i].num_cpus = 1; } if (li->num[i].codecs) { @@ -663,6 +667,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dai_link[i].num_codecs = li->num[i].codecs; dlcs += li->num[i].codecs; + } else { + /* DPCM Fe's Codec = dummy */ + dai_props[i].codecs = + dai_link[i].codecs = &priv->dummy; + dai_props[i].num.codecs = + dai_link[i].num_codecs = 1; } if (li->num[i].platforms) { @@ -673,6 +683,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dai_link[i].num_platforms = li->num[i].platforms; dlcs += li->num[i].platforms; + } else { + /* Doesn't have Platform */ + dai_props[i].platforms = + dai_link[i].platforms = NULL; + dai_props[i].num.platforms = + dai_link[i].num_platforms = 0; } } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 1fad570338b3..9132c0ea05af 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -141,9 +141,6 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, int is_single_links = 0; /* Codec is dummy */ - codecs->of_node = NULL; - codecs->dai_name = "snd-soc-dummy-dai"; - codecs->name = "snd-soc-dummy"; /* FE settings */ dai_link->dynamic = 1; @@ -167,13 +164,11 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, goto out_put_node; asoc_simple_canonicalize_cpu(dai_link, is_single_links); + asoc_simple_canonicalize_platform(dai_link); } else { struct snd_soc_codec_conf *cconf; /* CPU is dummy */ - cpus->of_node = NULL; - cpus->dai_name = "snd-soc-dummy-dai"; - cpus->name = "snd-soc-dummy"; /* BE settings */ dai_link->no_pcm = 1; @@ -211,8 +206,6 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, simple_parse_convert(dev, np, &dai_props->adata); simple_parse_mclk_fs(top, np, codec, dai_props, prefix); - asoc_simple_canonicalize_platform(dai_link); - ret = asoc_simple_parse_tdm(np, dai); if (ret) goto out_put_node; @@ -530,15 +523,12 @@ static int simple_count_dpcm(struct asoc_simple_priv *priv, if (li->cpu) { li->num[li->link].cpus = 1; - li->num[li->link].codecs = 1; li->num[li->link].platforms = 1; li->link++; /* CPU-dummy */ li->dais++; } else { - li->num[li->link].cpus = 1; li->num[li->link].codecs = 1; - li->num[li->link].platforms = 1; li->link++; /* dummy-Codec */ li->dais++; -- cgit v1.2.3-70-g09d2 From ca6a0122557faa4fa01d6dbfa742870c33c46218 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 9 Apr 2021 15:01:17 -0700 Subject: ASoC: soc-acpi: add new fields for mach_params We currently have an ugly way of handling the SOF nocodec mode, with blatant violations between layers. To create the nocodec card, let's add two new fields and the existing mach_params structure, that way there will be no differences with regular cards. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Daniel Baluta Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20210409220121.1542362-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index c45075024c30..2f3fa385c092 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -63,6 +63,8 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver * @link_mask: links enabled on the board * @links: array of link _ADR descriptors, null terminated + * @num_dai_drivers: number of elements in @dai_drivers + * @dai_drivers: pointer to dai_drivers, used e.g. in nocodec mode */ struct snd_soc_acpi_mach_params { u32 acpi_ipc_irq_index; @@ -72,6 +74,8 @@ struct snd_soc_acpi_mach_params { bool common_hdmi_codec_drv; u32 link_mask; const struct snd_soc_acpi_link_adr *links; + u32 num_dai_drivers; + struct snd_soc_dai_driver *dai_drivers; }; /** -- cgit v1.2.3-70-g09d2 From 4c1cc83fcc7e02f6f6f76da2ea66af86a95fd675 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 9 Apr 2021 15:01:21 -0700 Subject: ASOC: SOF: simplify nocodec mode Replace ugly #if (!IS_ENABLED) by if (!IS_ENABLED), remove cross-module dependencies and use classic mechanism to pass information to the machine driver. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Daniel Baluta Reviewed-by: Kai Vehmanen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20210409220121.1542362-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 3 --- sound/soc/sof/nocodec.c | 39 +++++++++++++++++++++------------------ sound/soc/sof/sof-audio.c | 32 ++++++++++++++------------------ 3 files changed, 35 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/sound/sof.h b/include/sound/sof.h index b93bb8038080..502ed9b8d6a1 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -100,9 +100,6 @@ struct sof_dev_desc { const struct snd_sof_dsp_ops *ops; }; -int sof_nocodec_setup(struct device *dev, const struct snd_sof_dsp_ops *ops, - int (*pcm_dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params)); int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd); #endif diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 3b9bb2e83a86..356497fe4f4c 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -20,16 +20,14 @@ static struct snd_soc_card sof_nocodec_card = { }; static int sof_nocodec_bes_setup(struct device *dev, - const struct snd_sof_dsp_ops *ops, + struct snd_soc_dai_driver *drv, struct snd_soc_dai_link *links, - int link_num, struct snd_soc_card *card, - int (*pcm_dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params)) + int link_num, struct snd_soc_card *card) { struct snd_soc_dai_link_component *dlc; int i; - if (!ops || !links || !card) + if (!drv || !links || !card) return -EINVAL; /* set up BE dai_links */ @@ -55,16 +53,16 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].id = i; links[i].no_pcm = 1; - links[i].cpus->dai_name = ops->drv[i].name; - links[i].platforms->name = dev_name(dev); + links[i].cpus->dai_name = drv[i].name; + links[i].platforms->name = dev_name(dev->parent); links[i].codecs->dai_name = "snd-soc-dummy-dai"; links[i].codecs->name = "snd-soc-dummy"; - if (ops->drv[i].playback.channels_min) + if (drv[i].playback.channels_min) links[i].dpcm_playback = 1; - if (ops->drv[i].capture.channels_min) + if (drv[i].capture.channels_min) links[i].dpcm_capture = 1; - links[i].be_hw_params_fixup = pcm_dai_link_fixup; + links[i].be_hw_params_fixup = sof_pcm_dai_link_fixup; } card->dai_link = links; @@ -73,29 +71,34 @@ static int sof_nocodec_bes_setup(struct device *dev, return 0; } -int sof_nocodec_setup(struct device *dev, const struct snd_sof_dsp_ops *ops, - int (*pcm_dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params)) +static int sof_nocodec_setup(struct device *dev, + u32 num_dai_drivers, + struct snd_soc_dai_driver *dai_drivers) { struct snd_soc_dai_link *links; /* create dummy BE dai_links */ - links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * - ops->num_drv, GFP_KERNEL); + links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * num_dai_drivers, GFP_KERNEL); if (!links) return -ENOMEM; - return sof_nocodec_bes_setup(dev, ops, links, ops->num_drv, - &sof_nocodec_card, pcm_dai_link_fixup); + return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); } -EXPORT_SYMBOL(sof_nocodec_setup); static int sof_nocodec_probe(struct platform_device *pdev) { struct snd_soc_card *card = &sof_nocodec_card; + struct snd_soc_acpi_mach *mach; + int ret; card->dev = &pdev->dev; card->topology_shortname_created = true; + mach = pdev->dev.platform_data; + + ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, + mach->mach_params.dai_drivers); + if (ret < 0) + return ret; return devm_snd_soc_register_card(&pdev->dev, card); } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 5d13bafd4736..0d0d47dc0246 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -468,24 +468,24 @@ int sof_machine_check(struct snd_sof_dev *sdev) struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; struct snd_soc_acpi_mach *mach; - int ret; -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { - /* find machine */ - snd_sof_machine_select(sdev); - if (sof_pdata->machine) { - snd_sof_set_mach_params(sof_pdata->machine, sdev); - return 0; + /* find machine */ + snd_sof_machine_select(sdev); + if (sof_pdata->machine) { + snd_sof_set_mach_params(sof_pdata->machine, sdev); + return 0; + } + + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) { + dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; + } + } else { + dev_warn(sdev->dev, "Force to use nocodec mode\n"); } -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) - dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); - return -ENODEV; -#endif -#else - dev_warn(sdev->dev, "Force to use nocodec mode\n"); -#endif /* select nocodec mode */ dev_warn(sdev->dev, "Using nocodec machine driver\n"); mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL); @@ -495,10 +495,6 @@ int sof_machine_check(struct snd_sof_dev *sdev) mach->drv_name = "sof-nocodec"; sof_pdata->tplg_filename = desc->nocodec_tplg_filename; - ret = sof_nocodec_setup(sdev->dev, desc->ops, sof_pcm_dai_link_fixup); - if (ret < 0) - return ret; - sof_pdata->machine = mach; snd_sof_set_mach_params(sof_pdata->machine, sdev); -- cgit v1.2.3-70-g09d2 From f899006d558546a8ee39c93f816eb3847c5bc6c0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:04 +0900 Subject: ASoC: simple-card-utils: remove li->dais/li->conf li->dais is same as number of CPU + Codec, li->conf is same as number of Codec when dummy-Codec. li->dais/li->conf are no longer needed. This patch removes these. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87sg3wwfa3.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 2 -- sound/soc/generic/audio-graph-card.c | 10 +--------- sound/soc/generic/simple-card-utils.c | 14 ++++++++++---- sound/soc/generic/simple-card.c | 11 +---------- 4 files changed, 12 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 6635283a8160..da9d7e3665a8 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -75,9 +75,7 @@ struct asoc_simple_priv { #define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i)) struct link_info { - int dais; /* number of dai */ int link; /* number of link */ - int conf; /* number of codec_conf */ int cpu; /* turn for CPU / Codec */ struct prop_nums num[SNDRV_MINOR_DEVICES]; }; diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 5ce5b7d62ee0..3a5bef2a91d3 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -552,7 +552,7 @@ int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) memset(&li, 0, sizeof(li)); graph_get_dais_count(priv, &li); - if (!li.link || !li.dais) + if (!li.link) return -EINVAL; ret = asoc_simple_init_priv(priv, &li); @@ -622,7 +622,6 @@ static int graph_count_noml(struct asoc_simple_priv *priv, li->num[li->link].platforms = 1; li->link += 1; /* 1xCPU-Codec */ - li->dais += 2; /* 1xCPU + 1xCodec */ dev_dbg(dev, "Count As Normal\n"); @@ -646,13 +645,10 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, li->num[li->link].platforms = 1; li->link++; /* 1xCPU-dummy */ - li->dais++; /* 1xCPU */ } else { li->num[li->link].codecs = 1; li->link++; /* 1xdummy-Codec */ - li->conf++; /* 1xdummy-Codec */ - li->dais++; /* 1xCodec */ } dev_dbg(dev, "Count As DPCM\n"); @@ -663,8 +659,6 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, static void graph_get_dais_count(struct asoc_simple_priv *priv, struct link_info *li) { - struct device *dev = simple_priv_to_dev(priv); - /* * link_num : number of links. * CPU-Codec / CPU-dummy / dummy-Codec @@ -714,8 +708,6 @@ static void graph_get_dais_count(struct asoc_simple_priv *priv, graph_for_each_link(priv, li, graph_count_noml, graph_count_dpcm); - dev_dbg(dev, "link %d, dais %d, ccnf %d\n", - li->link, li->dais, li->conf); } int audio_graph_card_probe(struct snd_soc_card *card) diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index ca74dfa58458..59b4fb2bd586 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -597,7 +597,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct asoc_simple_dai *dais; struct snd_soc_dai_link_component *dlcs; struct snd_soc_codec_conf *cconf = NULL; - int i, dai_num = 0, dlc_num = 0; + int i, dai_num = 0, dlc_num = 0, cnf_num = 0; dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL); @@ -613,6 +613,9 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dai_num += cc; dlc_num += cc + li->num[i].platforms; + + if (!li->num[i].cpus) + cnf_num += li->num[i].codecs; } dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL); @@ -620,12 +623,15 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, if (!dais || !dlcs) return -ENOMEM; - if (li->conf) { - cconf = devm_kcalloc(dev, li->conf, sizeof(*cconf), GFP_KERNEL); + if (cnf_num) { + cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL); if (!cconf) return -ENOMEM; } + dev_dbg(dev, "link %d, dais %d, ccnf %d\n", + li->link, dai_num, cnf_num); + /* dummy CPU/Codec */ priv->dummy.of_node = NULL; priv->dummy.dai_name = "snd-soc-dummy-dai"; @@ -640,7 +646,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, card->dai_link = priv->dai_link; card->num_links = li->link; card->codec_conf = cconf; - card->num_configs = li->conf; + card->num_configs = cnf_num; for (i = 0; i < li->link; i++) { if (li->num[i].cpus) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 30da8272fbc0..7665bbf2a6c0 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -495,7 +495,6 @@ static int simple_count_noml(struct asoc_simple_priv *priv, li->num[li->link].platforms = 1; li->link += 1; - li->dais += 2; return 0; } @@ -517,13 +516,10 @@ static int simple_count_dpcm(struct asoc_simple_priv *priv, li->num[li->link].platforms = 1; li->link++; /* CPU-dummy */ - li->dais++; } else { li->num[li->link].codecs = 1; li->link++; /* dummy-Codec */ - li->dais++; - li->conf++; } return 0; @@ -587,17 +583,12 @@ static void simple_get_dais_count(struct asoc_simple_priv *priv, li->num[0].platforms = 1; li->link = 1; - li->dais = 2; - li->conf = 0; return; } simple_for_each_link(priv, li, simple_count_noml, simple_count_dpcm); - - dev_dbg(dev, "link %d, dais %d, ccnf %d\n", - li->link, li->dais, li->conf); } static int simple_soc_probe(struct snd_soc_card *card) @@ -637,7 +628,7 @@ static int asoc_simple_probe(struct platform_device *pdev) memset(&li, 0, sizeof(li)); simple_get_dais_count(priv, &li); - if (!li.link || !li.dais) + if (!li.link) return -EINVAL; ret = asoc_simple_init_priv(priv, &li); -- cgit v1.2.3-70-g09d2 From fafc05aadd4b6ce5c161135de9d3a653fc054543 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:09 +0900 Subject: ASoC: simple-card-utils: use for_each_prop_xxx() ASoC is now supporting multi DAI, but, current simple-card / audio-graph are assuming fixed single DAI. This patch uses for_each_prop_xxx() to support multi DAI. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87r1jgwf9y.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 43 ++++++++++-- sound/soc/generic/simple-card-utils.c | 126 ++++++++++++++++++++-------------- 2 files changed, 113 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index da9d7e3665a8..de40f09d226f 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -74,6 +74,34 @@ struct asoc_simple_priv { #define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev) #define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i)) +#define for_each_prop_dlc_cpus(props, i, cpu) \ + for ((i) = 0; \ + ((i) < (props)->num.cpus) && ((cpu) = &(props)->cpus[i]); \ + (i)++) +#define for_each_prop_dlc_codecs(props, i, codec) \ + for ((i) = 0; \ + ((i) < (props)->num.codecs) && ((codec) = &(props)->codecs[i]); \ + (i)++) +#define for_each_prop_dlc_platforms(props, i, platform) \ + for ((i) = 0; \ + ((i) < (props)->num.platforms) && ((platform) = &(props)->platforms[i]); \ + (i)++) +#define for_each_prop_codec_conf(props, i, conf) \ + for ((i) = 0; \ + ((i) < (props)->num.codecs) && \ + (props)->codec_conf && \ + ((conf) = &(props)->codec_conf[i]); \ + (i)++) + +#define for_each_prop_dai_cpu(props, i, cpu) \ + for ((i) = 0; \ + ((i) < (props)->num.cpus) && ((cpu) = &(props)->cpu_dai[i]); \ + (i)++) +#define for_each_prop_dai_codec(props, i, codec) \ + for ((i) = 0; \ + ((i) < (props)->num.codecs) && ((codec) = &(props)->codec_dai[i]); \ + (i)++) + struct link_info { int link; /* number of link */ int cpu; /* turn for CPU / Codec */ @@ -192,11 +220,16 @@ static inline void asoc_simple_debug_info(struct asoc_simple_priv *priv) for (i = 0; i < card->num_links; i++) { struct simple_dai_props *props = simple_priv_to_props(priv, i); struct snd_soc_dai_link *link = simple_priv_to_link(priv, i); + struct asoc_simple_dai *dai; + struct snd_soc_codec_conf *cnf; + int j; dev_dbg(dev, "DAI%d\n", i); - asoc_simple_debug_dai(priv, "cpu", props->cpu_dai); - asoc_simple_debug_dai(priv, "codec", props->codec_dai); + for_each_prop_dai_cpu(props, j, dai) + asoc_simple_debug_dai(priv, "cpu", dai); + for_each_prop_dai_codec(props, j, dai) + asoc_simple_debug_dai(priv, "codec", dai); if (link->name) dev_dbg(dev, "dai name = %s\n", link->name); @@ -209,9 +242,9 @@ static inline void asoc_simple_debug_info(struct asoc_simple_priv *priv) if (props->adata.convert_channels) dev_dbg(dev, "convert_channels = %d\n", props->adata.convert_channels); - if (props->codec_conf && props->codec_conf->name_prefix) - dev_dbg(dev, "name prefix = %s\n", - props->codec_conf->name_prefix); + for_each_prop_codec_conf(props, j, cnf) + if (cnf->name_prefix) + dev_dbg(dev, "name prefix = %s\n", cnf->name_prefix); if (props->mclk_fs) dev_dbg(dev, "mclk-fs = %d\n", props->mclk_fs); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 59b4fb2bd586..fad9c7f37d2b 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -195,17 +195,37 @@ int asoc_simple_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct asoc_simple_dai *dai; + int i1, i2, i; int ret; - ret = asoc_simple_clk_enable(dai_props->cpu_dai); - if (ret) - return ret; + for_each_prop_dai_cpu(props, i1, dai) { + ret = asoc_simple_clk_enable(dai); + if (ret) + goto cpu_err; + } + + for_each_prop_dai_codec(props, i2, dai) { + ret = asoc_simple_clk_enable(dai); + if (ret) + goto codec_err; + } - ret = asoc_simple_clk_enable(dai_props->codec_dai); - if (ret) - asoc_simple_clk_disable(dai_props->cpu_dai); + return 0; +codec_err: + for_each_prop_dai_codec(props, i, dai) { + if (i >= i2) + break; + asoc_simple_clk_disable(dai); + } +cpu_err: + for_each_prop_dai_cpu(props, i, dai) { + if (i >= i1) + break; + asoc_simple_clk_disable(dai); + } return ret; } EXPORT_SYMBOL_GPL(asoc_simple_startup); @@ -216,17 +236,19 @@ void asoc_simple_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct asoc_simple_dai *dai; + int i; - if (dai_props->mclk_fs) { + if (props->mclk_fs) { snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); } - asoc_simple_clk_disable(dai_props->cpu_dai); - - asoc_simple_clk_disable(dai_props->codec_dai); + for_each_prop_dai_cpu(props, i, dai) + asoc_simple_clk_disable(dai); + for_each_prop_dai_codec(props, i, dai) + asoc_simple_clk_disable(dai); } EXPORT_SYMBOL_GPL(asoc_simple_shutdown); @@ -249,41 +271,41 @@ int asoc_simple_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct asoc_simple_dai *pdai; + struct snd_soc_dai *sdai; struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); unsigned int mclk, mclk_fs = 0; - int ret; + int i, ret; - if (dai_props->mclk_fs) - mclk_fs = dai_props->mclk_fs; + if (props->mclk_fs) + mclk_fs = props->mclk_fs; if (mclk_fs) { mclk = params_rate(params) * mclk_fs; - ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk); - if (ret < 0) - return ret; - - ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); - if (ret && ret != -ENOTSUPP) - goto err; - - ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, - SND_SOC_CLOCK_OUT); - if (ret && ret != -ENOTSUPP) - goto err; + for_each_prop_dai_codec(props, i, pdai) { + ret = asoc_simple_set_clk_rate(pdai, mclk); + if (ret < 0) + return ret; + } + for_each_prop_dai_cpu(props, i, pdai) { + ret = asoc_simple_set_clk_rate(pdai, mclk); + if (ret < 0) + return ret; + } + for_each_rtd_codec_dais(rtd, i, sdai) { + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + for_each_rtd_cpu_dais(rtd, i, sdai) { + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + return ret; + } } return 0; -err: - return ret; } EXPORT_SYMBOL_GPL(asoc_simple_hw_params); @@ -378,20 +400,22 @@ static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd, int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd) { struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - int ret; - - ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, 0), - dai_props->codec_dai); - if (ret < 0) - return ret; + struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct asoc_simple_dai *dai; + int i, ret; - ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, 0), - dai_props->cpu_dai); - if (ret < 0) - return ret; + for_each_prop_dai_codec(props, i, dai) { + ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai); + if (ret < 0) + return ret; + } + for_each_prop_dai_cpu(props, i, dai) { + ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai); + if (ret < 0) + return ret; + } - ret = asoc_simple_init_dai_link_params(rtd, dai_props); + ret = asoc_simple_init_dai_link_params(rtd, props); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From e25704f84ca2b586e8e65d1b2ab686205b3076fe Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:13 +0900 Subject: ASoC: simple-card-utils: remove asoc_simple_parse_xxx() ASoC is now supporting multi DAI, but, current simple-card / audio-graph are assuming fixed single DAI. Now, asoc_simple_parse_xxx() macro is assuming single DAI. To support multi-CPU/Codec, this patch unpack asoc_simple_parse_xxx() macro, and uses "&dai_link->cpus[i]" instead of "dai_link->cpus". Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87pmz0wf9u.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 11 ----------- sound/soc/generic/audio-graph-card.c | 16 ++++++++-------- sound/soc/generic/simple-card.c | 18 +++++++++--------- 3 files changed, 17 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index de40f09d226f..23f17ccc7908 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -120,10 +120,6 @@ int asoc_simple_set_dailink_name(struct device *dev, int asoc_simple_parse_card_name(struct snd_soc_card *card, char *prefix); -#define asoc_simple_parse_clk_cpu(dev, node, dai_link, simple_dai) \ - asoc_simple_parse_clk(dev, node, simple_dai, dai_link->cpus) -#define asoc_simple_parse_clk_codec(dev, node, dai_link, simple_dai) \ - asoc_simple_parse_clk(dev, node, simple_dai, dai_link->codecs) int asoc_simple_parse_clk(struct device *dev, struct device_node *node, struct asoc_simple_dai *simple_dai, @@ -136,13 +132,6 @@ int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd); int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); -#define asoc_simple_parse_cpu(node, dai_link, is_single_link) \ - asoc_simple_parse_dai(node, dai_link->cpus, is_single_link) -#define asoc_simple_parse_codec(node, dai_link) \ - asoc_simple_parse_dai(node, dai_link->codecs, NULL) -#define asoc_simple_parse_platform(node, dai_link) \ - asoc_simple_parse_dai(node, dai_link->platforms, NULL) - #define asoc_simple_parse_tdm(np, dai) \ snd_soc_of_parse_tdm_slot(np, &(dai)->tx_slot_mask, \ &(dai)->rx_slot_mask, \ diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 3a5bef2a91d3..145f8a19c9e8 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -247,11 +247,11 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, dai = dai_props->cpu_dai; - ret = asoc_simple_parse_cpu(ep, dai_link, &is_single_links); + ret = asoc_simple_parse_dai(ep, dai_link->cpus, &is_single_links); if (ret) goto out_put_node; - ret = asoc_simple_parse_clk_cpu(dev, ep, dai_link, dai); + ret = asoc_simple_parse_clk(dev, ep, dai, dai_link->cpus); if (ret < 0) goto out_put_node; @@ -290,11 +290,11 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, dai = dai_props->codec_dai; cconf = dai_props->codec_conf; - ret = asoc_simple_parse_codec(ep, dai_link); + ret = asoc_simple_parse_dai(ep, dai_link->codecs, NULL); if (ret < 0) goto out_put_node; - ret = asoc_simple_parse_clk_codec(dev, ep, dai_link, dai); + ret = asoc_simple_parse_clk(dev, ep, dai, dai_link->codecs); if (ret < 0) goto out_put_node; @@ -371,11 +371,11 @@ static int graph_dai_link_of(struct asoc_simple_priv *priv, if (ret < 0) return ret; - ret = asoc_simple_parse_cpu(cpu_ep, dai_link, &single_cpu); + ret = asoc_simple_parse_dai(cpu_ep, dai_link->cpus, &single_cpu); if (ret < 0) return ret; - ret = asoc_simple_parse_codec(codec_ep, dai_link); + ret = asoc_simple_parse_dai(codec_ep, dai_link->codecs, NULL); if (ret < 0) return ret; @@ -387,11 +387,11 @@ static int graph_dai_link_of(struct asoc_simple_priv *priv, if (ret < 0) return ret; - ret = asoc_simple_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); + ret = asoc_simple_parse_clk(dev, cpu_ep, cpu_dai, dai_link->cpus); if (ret < 0) return ret; - ret = asoc_simple_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); + ret = asoc_simple_parse_clk(dev, codec_ep, codec_dai, dai_link->codecs); if (ret < 0) return ret; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 7665bbf2a6c0..6a55b8672166 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -148,11 +148,11 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, dai = dai_props->cpu_dai; - ret = asoc_simple_parse_cpu(np, dai_link, &is_single_links); + ret = asoc_simple_parse_dai(np, dai_link->cpus, &is_single_links); if (ret) goto out_put_node; - ret = asoc_simple_parse_clk_cpu(dev, np, dai_link, dai); + ret = asoc_simple_parse_clk(dev, np, dai, dai_link->cpus); if (ret < 0) goto out_put_node; @@ -176,11 +176,11 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, dai = dai_props->codec_dai; cconf = dai_props->codec_conf; - ret = asoc_simple_parse_codec(np, dai_link); + ret = asoc_simple_parse_dai(np, dai_link->codecs, NULL); if (ret < 0) goto out_put_node; - ret = asoc_simple_parse_clk_codec(dev, np, dai_link, dai); + ret = asoc_simple_parse_clk(dev, np, dai, dai_link->codecs); if (ret < 0) goto out_put_node; @@ -260,15 +260,15 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv, simple_parse_mclk_fs(top, cpu, codec, dai_props, prefix); - ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu); + ret = asoc_simple_parse_dai(cpu, dai_link->cpus, &single_cpu); if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_parse_codec(codec, dai_link); + ret = asoc_simple_parse_dai(codec, dai_link->codecs, NULL); if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_parse_platform(plat, dai_link); + ret = asoc_simple_parse_dai(plat, dai_link->platforms, NULL); if (ret < 0) goto dai_link_of_err; @@ -280,11 +280,11 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv, if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); + ret = asoc_simple_parse_clk(dev, cpu, cpu_dai, dai_link->cpus); if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); + ret = asoc_simple_parse_clk(dev, codec, codec_dai, dai_link->codecs); if (ret < 0) goto dai_link_of_err; -- cgit v1.2.3-70-g09d2 From ac813c625ad5c3ee98a99e1b37659a0d85178978 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:23 +0900 Subject: ASoC: simple-card-utils: indicate dai_fmt if exist link->dai_fmt might be 0. Don't indicate it in such case when debugging. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87mtu4wf9k.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 23f17ccc7908..080fe7eb560f 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -222,9 +222,8 @@ static inline void asoc_simple_debug_info(struct asoc_simple_priv *priv) if (link->name) dev_dbg(dev, "dai name = %s\n", link->name); - - dev_dbg(dev, "dai format = %04x\n", link->dai_fmt); - + if (link->dai_fmt) + dev_dbg(dev, "dai format = %04x\n", link->dai_fmt); if (props->adata.convert_rate) dev_dbg(dev, "convert_rate = %d\n", props->adata.convert_rate); -- cgit v1.2.3-70-g09d2 From 40d8cbe70e71be170e0a4fe6ab112d9aaa9cfb18 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:27 +0900 Subject: ASoC: simple-card-utils: indicate missing CPU/Codec numbers for debug Now ALSA is supporting multi-CPU/Codec, thus, we want to know number of CPU/Codec when debugging. This patch indicates it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87lf9owf9g.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 080fe7eb560f..e366e432c475 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -215,8 +215,10 @@ static inline void asoc_simple_debug_info(struct asoc_simple_priv *priv) dev_dbg(dev, "DAI%d\n", i); + dev_dbg(dev, "cpu num = %d\n", link->num_cpus); for_each_prop_dai_cpu(props, j, dai) asoc_simple_debug_dai(priv, "cpu", dai); + dev_dbg(dev, "codec num = %d\n", link->num_codecs); for_each_prop_dai_codec(props, j, dai) asoc_simple_debug_dai(priv, "codec", dai); -- cgit v1.2.3-70-g09d2 From 9830d3e99f51fc1c1c6ab8be7778fd205af198ad Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:32 +0900 Subject: ASoC: simple-card-utils: add simple_props_to_xxx() macro We shouldn't use dai_props->cpus/codecs/cpu_dai/codec_dai/codec_conf directly, because these are array to supporting multi CPU/Codec/Platform. This patch adds asoc_link_to_xxx() macro for it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87k0p8wf9b.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index e366e432c475..78b6cf0194d2 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -74,32 +74,45 @@ struct asoc_simple_priv { #define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev) #define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i)) +#define simple_props_to_dlc_cpu(props, i) ((props)->cpus + i) +#define simple_props_to_dlc_codec(props, i) ((props)->codecs + i) +#define simple_props_to_dlc_platform(props, i) ((props)->platforms + i) + +#define simple_props_to_dai_cpu(props, i) ((props)->cpu_dai + i) +#define simple_props_to_dai_codec(props, i) ((props)->codec_dai + i) +#define simple_props_to_codec_conf(props, i) ((props)->codec_conf + i) + #define for_each_prop_dlc_cpus(props, i, cpu) \ for ((i) = 0; \ - ((i) < (props)->num.cpus) && ((cpu) = &(props)->cpus[i]); \ + ((i) < (props)->num.cpus) && \ + ((cpu) = simple_props_to_dlc_cpu(props, i)); \ (i)++) -#define for_each_prop_dlc_codecs(props, i, codec) \ +#define for_each_prop_dlc_codecs(props, i, codec) \ for ((i) = 0; \ - ((i) < (props)->num.codecs) && ((codec) = &(props)->codecs[i]); \ + ((i) < (props)->num.codecs) && \ + ((codec) = simple_props_to_dlc_codec(props, i)); \ (i)++) #define for_each_prop_dlc_platforms(props, i, platform) \ for ((i) = 0; \ - ((i) < (props)->num.platforms) && ((platform) = &(props)->platforms[i]); \ + ((i) < (props)->num.platforms) && \ + ((platform) = simple_props_to_dlc_platform(props, i)); \ (i)++) #define for_each_prop_codec_conf(props, i, conf) \ for ((i) = 0; \ ((i) < (props)->num.codecs) && \ (props)->codec_conf && \ - ((conf) = &(props)->codec_conf[i]); \ + ((conf) = simple_props_to_codec_conf(props, i)); \ (i)++) #define for_each_prop_dai_cpu(props, i, cpu) \ for ((i) = 0; \ - ((i) < (props)->num.cpus) && ((cpu) = &(props)->cpu_dai[i]); \ + ((i) < (props)->num.cpus) && \ + ((cpu) = simple_props_to_dai_cpu(props, i)); \ (i)++) #define for_each_prop_dai_codec(props, i, codec) \ for ((i) = 0; \ - ((i) < (props)->num.codecs) && ((codec) = &(props)->codec_dai[i]); \ + ((i) < (props)->num.codecs) && \ + ((codec) = simple_props_to_dai_codec(props, i)); \ (i)++) struct link_info { -- cgit v1.2.3-70-g09d2 From c826ec0391c83f06354a4ebb25c7b2480c18f33a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:45 +0900 Subject: ASoC: simple-card-utils: multi support at asoc_simple_canonicalize_cpu/platform() Current asoc_simple_canonicalize_cpu/platform() is assuming single CPU, single Platform, but we want to support Multi support. This patch is prepare for it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87im4swf8y.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 7 ++++--- sound/soc/generic/audio-graph-card.c | 9 +++++---- sound/soc/generic/simple-card-utils.c | 11 ++++++----- sound/soc/generic/simple-card.c | 9 +++++---- 4 files changed, 20 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 78b6cf0194d2..cccd9987a1b4 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -151,9 +151,10 @@ int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, &(dai)->slots, \ &(dai)->slot_width); -void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link); -void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link, - int is_single_links); +void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms, + struct snd_soc_dai_link_component *cpus); +void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, + int is_single_links); int asoc_simple_clean_reference(struct snd_soc_card *card); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 145f8a19c9e8..e6b64fdbdc76 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -226,6 +226,7 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, struct asoc_simple_dai *dai; struct snd_soc_dai_link_component *cpus = dai_link->cpus; struct snd_soc_dai_link_component *codecs = dai_link->codecs; + struct snd_soc_dai_link_component *platforms = dai_link->platforms; int ret; port = of_get_parent(ep); @@ -276,8 +277,8 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, dai_link->no_pcm = 1; /* card->num_links includes Codec */ - asoc_simple_canonicalize_cpu(dai_link, is_single_links); - asoc_simple_canonicalize_platform(dai_link); + asoc_simple_canonicalize_cpu(cpus, is_single_links); + asoc_simple_canonicalize_platform(platforms, cpus); } else { struct snd_soc_codec_conf *cconf; @@ -405,8 +406,8 @@ static int graph_dai_link_of(struct asoc_simple_priv *priv, dai_link->ops = &graph_ops; dai_link->init = asoc_simple_dai_init; - asoc_simple_canonicalize_cpu(dai_link, single_cpu); - asoc_simple_canonicalize_platform(dai_link); + asoc_simple_canonicalize_cpu(dai_link->cpus, single_cpu); + asoc_simple_canonicalize_platform(dai_link->platforms, dai_link->cpus); return 0; } diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 6efe3757eff8..a15956c25858 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -423,15 +423,16 @@ int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd) } EXPORT_SYMBOL_GPL(asoc_simple_dai_init); -void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link) +void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms, + struct snd_soc_dai_link_component *cpus) { /* Assumes platform == cpu */ - if (!dai_link->platforms->of_node) - dai_link->platforms->of_node = dai_link->cpus->of_node; + if (!platforms->of_node) + platforms->of_node = cpus->of_node; } EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform); -void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link, +void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, int is_single_links) { /* @@ -444,7 +445,7 @@ void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link, * fmt_multiple_name() */ if (is_single_links) - dai_link->cpus->dai_name = NULL; + cpus->dai_name = NULL; } EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6a55b8672166..06b2d6a7f48d 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -124,6 +124,7 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, struct asoc_simple_dai *dai; struct snd_soc_dai_link_component *cpus = dai_link->cpus; struct snd_soc_dai_link_component *codecs = dai_link->codecs; + struct snd_soc_dai_link_component *platforms = dai_link->platforms; struct device_node *top = dev->of_node; struct device_node *node = of_get_parent(np); char *prefix = ""; @@ -162,8 +163,8 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, if (ret < 0) goto out_put_node; - asoc_simple_canonicalize_cpu(dai_link, is_single_links); - asoc_simple_canonicalize_platform(dai_link); + asoc_simple_canonicalize_cpu(cpus, is_single_links); + asoc_simple_canonicalize_platform(platforms, cpus); } else { struct snd_soc_codec_conf *cconf; @@ -298,8 +299,8 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv, dai_link->ops = &simple_ops; dai_link->init = asoc_simple_dai_init; - asoc_simple_canonicalize_cpu(dai_link, single_cpu); - asoc_simple_canonicalize_platform(dai_link); + asoc_simple_canonicalize_cpu(dai_link->cpus, single_cpu); + asoc_simple_canonicalize_platform(dai_link->platforms, dai_link->cpus); dai_link_of_err: of_node_put(plat); -- cgit v1.2.3-70-g09d2 From 33cd6b191f1cdb5f332717a80ce26f661f53e924 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:50 +0900 Subject: ASoC: simple-card-utils: tidyup debug info for clock simple-card / audio-graph can use clock as dai->clk or dai->sysclk. These related information should be indicated at same position. This patch tidyup it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87h7kcwf8t.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index cccd9987a1b4..bf068803eb1f 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -191,12 +191,6 @@ static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv, if (dai->name) dev_dbg(dev, "%s dai name = %s\n", name, dai->name); - if (dai->sysclk) - dev_dbg(dev, "%s sysclk = %d\n", - name, dai->sysclk); - - dev_dbg(dev, "%s direction = %s\n", - name, dai->clk_direction ? "OUT" : "IN"); if (dai->slots) dev_dbg(dev, "%s slots = %d\n", name, dai->slots); @@ -208,6 +202,12 @@ static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv, dev_dbg(dev, "%s rx slot mask = %d\n", name, dai->rx_slot_mask); if (dai->clk) dev_dbg(dev, "%s clk %luHz\n", name, clk_get_rate(dai->clk)); + if (dai->sysclk) + dev_dbg(dev, "%s sysclk = %dHz\n", + name, dai->sysclk); + if (dai->clk || dai->sysclk) + dev_dbg(dev, "%s direction = %s\n", + name, dai->clk_direction ? "OUT" : "IN"); } static inline void asoc_simple_debug_info(struct asoc_simple_priv *priv) -- cgit v1.2.3-70-g09d2 From 3919249e80995ed5f125f94d05fcb6171f79e732 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:52:55 +0900 Subject: ASoC: simple-card-utils: tidyup dev_dbg() to use 1 line We can use 100 char now for 1 line. This patch tidyup unreadable dev_dbg() message. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87fszwwf8o.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index bf068803eb1f..20c22bffe091 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -241,17 +241,14 @@ static inline void asoc_simple_debug_info(struct asoc_simple_priv *priv) if (link->dai_fmt) dev_dbg(dev, "dai format = %04x\n", link->dai_fmt); if (props->adata.convert_rate) - dev_dbg(dev, "convert_rate = %d\n", - props->adata.convert_rate); + dev_dbg(dev, "convert_rate = %d\n", props->adata.convert_rate); if (props->adata.convert_channels) - dev_dbg(dev, "convert_channels = %d\n", - props->adata.convert_channels); + dev_dbg(dev, "convert_channels = %d\n", props->adata.convert_channels); for_each_prop_codec_conf(props, j, cnf) if (cnf->name_prefix) dev_dbg(dev, "name prefix = %s\n", cnf->name_prefix); if (props->mclk_fs) - dev_dbg(dev, "mclk-fs = %d\n", - props->mclk_fs); + dev_dbg(dev, "mclk-fs = %d\n", props->mclk_fs); } } #else -- cgit v1.2.3-70-g09d2 From fcfd763bef4ff7f6371790979a6ceac9c4ac425a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 12 Apr 2021 08:53:00 +0900 Subject: ASoC: simple-card-utils: tidyup asoc_simple_parse_convert() dev is not used. This patch removes it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87eefgwf8j.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 3 +-- sound/soc/generic/audio-graph-card.c | 10 +++++----- sound/soc/generic/simple-card-utils.c | 3 +-- sound/soc/generic/simple-card.c | 8 ++++---- 4 files changed, 11 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 20c22bffe091..fac3b832d982 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -160,8 +160,7 @@ int asoc_simple_clean_reference(struct snd_soc_card *card); void asoc_simple_convert_fixup(struct asoc_simple_data *data, struct snd_pcm_hw_params *params); -void asoc_simple_parse_convert(struct device *dev, - struct device_node *np, char *prefix, +void asoc_simple_parse_convert(struct device_node *np, char *prefix, struct asoc_simple_data *data); int asoc_simple_parse_routing(struct snd_soc_card *card, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index e6b64fdbdc76..0582fe296471 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -180,11 +180,11 @@ static void graph_parse_convert(struct device *dev, struct device_node *ports = of_get_parent(port); struct device_node *node = of_graph_get_port_parent(ep); - asoc_simple_parse_convert(dev, top, NULL, adata); - asoc_simple_parse_convert(dev, node, PREFIX, adata); - asoc_simple_parse_convert(dev, ports, NULL, adata); - asoc_simple_parse_convert(dev, port, NULL, adata); - asoc_simple_parse_convert(dev, ep, NULL, adata); + asoc_simple_parse_convert(top, NULL, adata); + asoc_simple_parse_convert(node, PREFIX, adata); + asoc_simple_parse_convert(ports, NULL, adata); + asoc_simple_parse_convert(port, NULL, adata); + asoc_simple_parse_convert(ep, NULL, adata); of_node_put(port); of_node_put(ports); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index a15956c25858..e1b7b30a4c8c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -32,8 +32,7 @@ void asoc_simple_convert_fixup(struct asoc_simple_data *data, } EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup); -void asoc_simple_parse_convert(struct device *dev, - struct device_node *np, +void asoc_simple_parse_convert(struct device_node *np, char *prefix, struct asoc_simple_data *data) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 06b2d6a7f48d..bf5ddf1ea65f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -84,10 +84,10 @@ static void simple_parse_convert(struct device *dev, struct device_node *top = dev->of_node; struct device_node *node = of_get_parent(np); - asoc_simple_parse_convert(dev, top, PREFIX, adata); - asoc_simple_parse_convert(dev, node, PREFIX, adata); - asoc_simple_parse_convert(dev, node, NULL, adata); - asoc_simple_parse_convert(dev, np, NULL, adata); + asoc_simple_parse_convert(top, PREFIX, adata); + asoc_simple_parse_convert(node, PREFIX, adata); + asoc_simple_parse_convert(node, NULL, adata); + asoc_simple_parse_convert(np, NULL, adata); of_node_put(node); } -- cgit v1.2.3-70-g09d2 From 8f1a16818a08047c83bc6e29efc07b15fd11fa29 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 16 Apr 2021 11:00:26 +0900 Subject: ASoC: soc-utils: add snd_soc_component_is_dummy() There is snd_soc_dai_is_dummy(), but not for component. This patch adds it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87zgxzxa2t.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 1 + sound/soc/soc-core.c | 2 +- sound/soc/soc-utils.c | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 722cfab28d29..8c4d6830597f 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -338,6 +338,7 @@ static inline int snd_soc_component_cache_sync( void snd_soc_component_set_aux(struct snd_soc_component *component, struct snd_soc_aux_dev *aux); int snd_soc_component_init(struct snd_soc_component *component); +int snd_soc_component_is_dummy(struct snd_soc_component *component); /* component IO */ unsigned int snd_soc_component_read(struct snd_soc_component *component, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 236e075b9e57..762fb5c23f21 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1169,7 +1169,7 @@ static int soc_probe_component(struct snd_soc_card *card, int probed = 0; int ret; - if (!strcmp(component->name, "snd-soc-dummy")) + if (snd_soc_component_is_dummy(component)) return 0; if (component->card) { diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index f27f94ca064b..98383fd76224 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -131,6 +131,12 @@ int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) return 0; } +int snd_soc_component_is_dummy(struct snd_soc_component *component) +{ + return ((component->driver == &dummy_platform) || + (component->driver == &dummy_codec)); +} + static int snd_soc_dummy_probe(struct platform_device *pdev) { int ret; -- cgit v1.2.3-70-g09d2 From 343e55e71877415a23372388b3e0c59a9bba42f6 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 16 Apr 2021 09:11:47 +0200 Subject: ASoC: simple-card-utils: Increase maximum number of links to 128 On Tegra186 and later, the number of links can go up to 72, so bump the maximum number of links to the next power of two (128). Fixes: f2138aed231c ("ASoC: simple-card-utils: enable flexible CPU/Codec/Platform") Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20210416071147.2149109-2-thierry.reding@gmail.com Reviewed-by: Jon Hunter Tested-by: Jon Hunter Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 4 +++- sound/soc/generic/audio-graph-card.c | 4 ++-- sound/soc/generic/simple-card.c | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index fac3b832d982..e318a2d4ac44 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -115,10 +115,12 @@ struct asoc_simple_priv { ((codec) = simple_props_to_dai_codec(props, i)); \ (i)++) +#define SNDRV_MAX_LINKS 128 + struct link_info { int link; /* number of link */ int cpu; /* turn for CPU / Codec */ - struct prop_nums num[SNDRV_MINOR_DEVICES]; + struct prop_nums num[SNDRV_MAX_LINKS]; }; int asoc_simple_parse_daifmt(struct device *dev, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 080c1a22bfc2..c7369beee805 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -619,7 +619,7 @@ static int graph_count_noml(struct asoc_simple_priv *priv, { struct device *dev = simple_priv_to_dev(priv); - if (li->link >= SNDRV_MINOR_DEVICES) { + if (li->link >= SNDRV_MAX_LINKS) { dev_err(dev, "too many links\n"); return -EINVAL; } @@ -642,7 +642,7 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, { struct device *dev = simple_priv_to_dev(priv); - if (li->link >= SNDRV_MINOR_DEVICES) { + if (li->link >= SNDRV_MAX_LINKS) { dev_err(dev, "too many links\n"); return -EINVAL; } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index e05539b5f997..8b9964d25757 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -482,7 +482,7 @@ static int simple_count_noml(struct asoc_simple_priv *priv, struct device_node *codec, struct link_info *li, bool is_top) { - if (li->link >= SNDRV_MINOR_DEVICES) { + if (li->link >= SNDRV_MAX_LINKS) { struct device *dev = simple_priv_to_dev(priv); dev_err(dev, "too many links\n"); @@ -503,7 +503,7 @@ static int simple_count_dpcm(struct asoc_simple_priv *priv, struct device_node *codec, struct link_info *li, bool is_top) { - if (li->link >= SNDRV_MINOR_DEVICES) { + if (li->link >= SNDRV_MAX_LINKS) { struct device *dev = simple_priv_to_dev(priv); dev_err(dev, "too many links\n"); -- cgit v1.2.3-70-g09d2 From 1a456b1c6be13514a8fc5c1a99e6763f491d17e9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 19 Apr 2021 11:02:19 +0900 Subject: ASoC: audio-graph: move audio_graph_card_probe() to simple-card-utils.c audio-graph-card2 can reuse audio_graph_card_probe(). This patch moves it to simple-card-utils.c. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87zgxv3uc4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/graph_card.h | 2 -- include/sound/simple_card_utils.h | 3 +++ sound/soc/generic/audio-graph-card.c | 19 +------------------ sound/soc/generic/simple-card-utils.c | 17 +++++++++++++++++ sound/soc/tegra/tegra_audio_graph_card.c | 2 +- 5 files changed, 22 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h index 013784467bec..9c2feb792742 100644 --- a/include/sound/graph_card.h +++ b/include/sound/graph_card.h @@ -9,8 +9,6 @@ #include -int audio_graph_card_probe(struct snd_soc_card *card); - int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); int audio_graph_remove(struct platform_device *pdev); diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index e318a2d4ac44..656c7e30b6e4 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -178,6 +178,9 @@ int asoc_simple_init_jack(struct snd_soc_card *card, int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct link_info *li); +int asoc_graph_card_probe(struct snd_soc_card *card); + + #ifdef DEBUG static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv, char *name, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index e45a560aa9b0..6cac167a4e2b 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -721,23 +721,6 @@ static int graph_get_dais_count(struct asoc_simple_priv *priv, graph_count_dpcm); } -int audio_graph_card_probe(struct snd_soc_card *card) -{ - struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); - int ret; - - ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL); - if (ret < 0) - return ret; - - ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL); - if (ret < 0) - return ret; - - return 0; -} -EXPORT_SYMBOL_GPL(audio_graph_card_probe); - static int graph_probe(struct platform_device *pdev) { struct asoc_simple_priv *priv; @@ -752,7 +735,7 @@ static int graph_probe(struct platform_device *pdev) card = simple_priv_to_card(priv); card->dapm_widgets = graph_dapm_widgets; card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); - card->probe = audio_graph_card_probe; + card->probe = asoc_graph_card_probe; if (of_device_get_match_data(dev)) priv->dpcm_selectable = 1; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index e1b7b30a4c8c..ed2cad6d9ac1 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -740,6 +740,23 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, } EXPORT_SYMBOL_GPL(asoc_simple_init_priv); +int asoc_graph_card_probe(struct snd_soc_card *card) +{ + struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); + int ret; + + ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL); + if (ret < 0) + return ret; + + ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_graph_card_probe); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto "); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/tegra/tegra_audio_graph_card.c b/sound/soc/tegra/tegra_audio_graph_card.c index 47b319504c8c..35d008b5d373 100644 --- a/sound/soc/tegra/tegra_audio_graph_card.c +++ b/sound/soc/tegra/tegra_audio_graph_card.c @@ -184,7 +184,7 @@ static int tegra_audio_graph_card_probe(struct snd_soc_card *card) return PTR_ERR(priv->clk_plla_out0); } - return audio_graph_card_probe(card); + return asoc_graph_card_probe(card); } static int tegra_audio_graph_probe(struct platform_device *pdev) -- cgit v1.2.3-70-g09d2 From f6fcc820e0c96664e2f21c0d6bb60630243ef36a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 19 Apr 2021 11:02:25 +0900 Subject: ASoC: audio-graph: move audio_graph_remove() to simple-card-utils.c audio-graph-card2 can reuse audio_graph_remove() / asoc_simple_remove(). This patch moves it to simple-card-utils.c. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87y2df3uby.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/graph_card.h | 2 -- include/sound/simple_card_utils.h | 2 +- sound/soc/generic/audio-graph-card.c | 10 +--------- sound/soc/generic/simple-card-utils.c | 8 ++++++++ sound/soc/generic/simple-card.c | 7 ------- sound/soc/tegra/tegra_audio_graph_card.c | 2 +- 6 files changed, 11 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h index 9c2feb792742..6f10bfb0d5ee 100644 --- a/include/sound/graph_card.h +++ b/include/sound/graph_card.h @@ -11,6 +11,4 @@ int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); -int audio_graph_remove(struct platform_device *pdev); - #endif /* __GRAPH_CARD_H */ diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 656c7e30b6e4..51b3b485a92e 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -177,10 +177,10 @@ int asoc_simple_init_jack(struct snd_soc_card *card, int is_hp, char *prefix, char *pin); int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct link_info *li); +int asoc_simple_remove(struct platform_device *pdev); int asoc_graph_card_probe(struct snd_soc_card *card); - #ifdef DEBUG static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv, char *name, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 6cac167a4e2b..976c1ea31a98 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -743,14 +743,6 @@ static int graph_probe(struct platform_device *pdev) return audio_graph_parse_of(priv, dev); } -int audio_graph_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return asoc_simple_clean_reference(card); -} -EXPORT_SYMBOL_GPL(audio_graph_remove); - static const struct of_device_id graph_of_match[] = { { .compatible = "audio-graph-card", }, { .compatible = "audio-graph-scu-card", @@ -766,7 +758,7 @@ static struct platform_driver graph_card = { .of_match_table = graph_of_match, }, .probe = graph_probe, - .remove = audio_graph_remove, + .remove = asoc_simple_remove, }; module_platform_driver(graph_card); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index ed2cad6d9ac1..fa1247f0dda1 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -740,6 +740,14 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, } EXPORT_SYMBOL_GPL(asoc_simple_init_priv); +int asoc_simple_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return asoc_simple_clean_reference(card); +} +EXPORT_SYMBOL_GPL(asoc_simple_remove); + int asoc_graph_card_probe(struct snd_soc_card *card) { struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index ca27cb9ff9e1..a1373be4558f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -708,13 +708,6 @@ err: return ret; } -static int asoc_simple_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return asoc_simple_clean_reference(card); -} - static const struct of_device_id simple_of_match[] = { { .compatible = "simple-audio-card", }, { .compatible = "simple-scu-audio-card", diff --git a/sound/soc/tegra/tegra_audio_graph_card.c b/sound/soc/tegra/tegra_audio_graph_card.c index 35d008b5d373..1f2c5018bf5a 100644 --- a/sound/soc/tegra/tegra_audio_graph_card.c +++ b/sound/soc/tegra/tegra_audio_graph_card.c @@ -244,7 +244,7 @@ static struct platform_driver tegra_audio_graph_card = { .of_match_table = graph_of_tegra_match, }, .probe = tegra_audio_graph_probe, - .remove = audio_graph_remove, + .remove = asoc_simple_remove, }; module_platform_driver(tegra_audio_graph_card); -- cgit v1.2.3-70-g09d2