ASoC: Add support for AN7581 PCM driver
Subsystems (3)
v4 cover ASoC: Add support for AN7581 PCM driver
The Airoha AN7581 SoC provides a PCM interface for VoIP applications, supporting playback and capture at sample rates of 8 kHz and 16 kHz. The driver configures the PCM bit clock to 2.048 MHz and supports 8-bit and 16-bit samples, with up to 32 channels.
Add YAML and driver. This has been verified with loopback and confirmed by Airoha with ZSI voice chip.
Changes v4:
- Add review by tag for DT patch
- Address shashiko bot comments
- Better protext tx pos
- Set DMA mask for coherent DMA
- Use more managed resource
- Increase users in startup
Changes v3:
- Out of RFC
- Add Documentation patch
- Drop SLIC testing code
- Add MAINTAINERS entry
- Follow normal pattern for commit title
- Improve commit description
Changes v2:
- Rentirely rework IRQ handling
- Fix problem for timeslow configuration
- Better support duplex mode
- Better handle descriptor tracking
- Move to Soc directory
- General code fixup and cleanup
Christian Marangi (2):
ASoC: dt-bindings: airoha: Add AN7581 PCM controller
ASoC: airoha: Add support for AN7581 PCM driver
.../bindings/sound/airoha,an7581-pcm.yaml | 52 + MAINTAINERS | 7 + sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/airoha/Kconfig | 10 + sound/soc/airoha/Makefile | 3 + sound/soc/airoha/an7581-pcm.c | 904 ++++++++++++++++++ 7 files changed, 978 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/airoha,an7581-pcm.yaml create mode 100644 sound/soc/airoha/Kconfig create mode 100644 sound/soc/airoha/Makefile create mode 100644 sound/soc/airoha/an7581-pcm.c
Signature · 1 line
2.55.0
PATCH v4 1/2 ASoC: dt-bindings: airoha: Add AN7581 PCM controller
The Airoha AN7581 SoC provides a PCM interface for VoIP applications, supporting playback and capture at sample rates of 8 kHz and 16 kHz. The driver configures the PCM bit clock to 2.048 MHz and supports 8-bit and 16-bit samples, with up to 32 channels.
Document the related compatible and required interrupt/reset and provide an example for it.
--- .../bindings/sound/airoha,an7581-pcm.yaml | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/airoha,an7581-pcm.yaml
Documentation/devicetree/bindings/sound/airoha,an7581-pcm.yaml new file +52 -0
@@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/airoha,an7581-pcm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha PCM controller for AN7581 SoC + +maintainers: + - Christian Marangi <[email protected]> + +properties: + compatible: + const: airoha,an7581-pcm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + + reset-names: + const: pcm1 + +required: + - compatible + - reg + - interrupts + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/reset/airoha,en7581-reset.h> + + pcm@1fbd0000 { + compatible = "airoha,an7581-pcm"; + reg = <0x1fbd0000 0x10000>; + + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; + resets = <&scuclk EN7581_PCM1_RST>; + reset-names = "pcm1"; + }; + +...
Signature · 1 line
2.55.0
PATCH v4 2/2 ASoC: airoha: Add support for AN7581 PCM driver
In reply to Christian MarangiThe Airoha AN7581 SoC provides a PCM interface for VoIP applications, supporting playback and capture at sample rates of 8 kHz and 16 kHz. The driver configures the PCM bit clock to 2.048 MHz and supports 8-bit and 16-bit samples, with up to 32 channels.
Add an ASoC platform driver for the AN7581 PCM controller configured for ZSI attached voice chip.
--- MAINTAINERS | 7 + sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/airoha/Kconfig | 10 + sound/soc/airoha/Makefile | 3 + sound/soc/airoha/an7581-pcm.c | 904 ++++++++++++++++++++++++++++++++++ 6 files changed, 926 insertions(+) create mode 100644 sound/soc/airoha/Kconfig create mode 100644 sound/soc/airoha/Makefile create mode 100644 sound/soc/airoha/an7581-pcm.c
MAINTAINERS +7 -0
@@ -775,6 +775,13 @@ F: Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml F: Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml F: sound/soc/mediatek/an7581/* +AIROHA PCM CONTROLLER DRIVER +M: Christian Marangi <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/devicetree/bindings/sound/airoha,an7581-pcm.yaml +F: sound/soc/airoha/* + AIROHA SPI SNFI DRIVER M: Lorenzo Bianconi <[email protected]> M: Ray Liu <[email protected]>
sound/soc/Kconfig +1 -0
@@ -103,6 +103,7 @@ config SND_SOC_USB # All the supported SoCs source "sound/soc/adi/Kconfig" +source "sound/soc/airoha/Kconfig" source "sound/soc/amd/Kconfig" source "sound/soc/apple/Kconfig" source "sound/soc/atmel/Kconfig"
sound/soc/Makefile +1 -0
@@ -46,6 +46,7 @@ obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ obj-$(CONFIG_SND_SOC) += apple/ obj-$(CONFIG_SND_SOC) += adi/ +obj-$(CONFIG_SND_SOC) += airoha/ obj-$(CONFIG_SND_SOC) += amd/ obj-$(CONFIG_SND_SOC) += atmel/ obj-$(CONFIG_SND_SOC) += au1x/
sound/soc/airoha/Kconfig new file +10 -0
@@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SND_SOC_AN7581_PCM + tristate "PCM support for Airoha AN7581 chip" + depends on ARCH_AIROHA || COMPILE_TEST + help + This adds PCM driver for Airoha AN7581 boards + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N".
sound/soc/airoha/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SND_SOC_AN7581_PCM) += an7581-pcm.o
sound/soc/airoha/an7581-pcm.c new file +904 -0
904 lines not on this page · raw message
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v4 2/2] ASoC: airoha: Add support for AN7581 PCM driver
Thank you for your contribution! Sashiko AI review found 12 potential issue(s) to consider: - [High] ASoC: airoha: an7581_pcm_fe_hw_params logic error permanently rejects first stream - [High] ASoC…
Thank you for your contribution! Sashiko AI review found 12 potential issue(s) to consider:
- [High] ASoC: airoha: an7581_pcm_fe_hw_params logic error permanently rejects first stream
- [High] ASoC: airoha: probe unconditionally fails due to IRQF_ONESHOT without thread_fn
- [High] ASoC: airoha: unbounded tx_pos/rx_pos accumulation breaks ALSA pointer tracking
- [High] ASoC: airoha: missing return on constraint failure in startup corrupts state
- [High] ASoC: airoha: Use-After-Free of DMA descriptors on driver unbind due to devres ordering
- [High] ASoC: airoha: Use-After-Free of ALSA runtime due to missing IRQ synchronization in shutdown
- [High] sound: airoha: missing interrupt status acknowledgment causes IRQ storm
- [High] sound: soc: airoha: inverted users check in hw_params breaks all streams
- [High] sound: soc: airoha: early exit in IRQ handler stalls stream
- [Medium] ASoC: airoha: premature return in IRQ handler stalls DMA on underrun
- [Medium] ALSA: airoha: missing endianness conversion for hardware DMA descriptors
- [Medium] ASoC: airoha: incorrect PCM interleaved format advertised for non-interleaved hardware --
commit d46acbcb8f477a3db86ab3f295bc95f3a191a323
Author: Christian Marangi <[email protected]>
ASoC: airoha: Add support for AN7581 PCM driver
The Airoha AN7581 SoC provides a PCM interface for VoIP applications, supporting playback and capture at sample rates of 8 kHz and 16 kHz. Add an ASoC platform driver for the AN7581 PCM controller configured for ZSI attached voice chip.
sound/soc/airoha/an7581-pcm.c new file +1 -0
@@ -0,0 +1,904 @@ +[ ... ]
Review
Quoted 5 lines
(quoted diff) +5
-0+struct an7581_pcm_desc { + u32 status; + u32 channel_mask; + u32 dma_addr; +} __packed;
[Severity: Medium]
Will this work correctly on big-endian architectures? Since the descriptors are shared with the hardware DMA, using native u32 instead of explicit __le32 means the data will be written and read backwards if the kernel is compiled for a Big Endian target. Should we use __le32 and explicit cpu_to_le32() conversions?
Quoted 2 lines
(quoted diff) +2
-0+ +struct an7581_pcm_priv {
[ ... ]
Review
Quoted 5 lines
(quoted diff) +5
-0+static const struct snd_pcm_hardware an7581_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_U16_LE,
[Severity: Medium]
Is it correct to advertise SNDRV_PCM_INFO_INTERLEAVED here? Later in an7581_pcm_fe_hw_params(), the hardware is configured with a per-channel memory offset, indicating that the hardware expects a non-interleaved layout:
regmap_update_bits(priv->map, AN7581_PCM_CHBFOSR, AN7581_PCM_DESC_CHBF_OFFSET, FIELD_PREP(AN7581_PCM_DESC_CHBF_OFFSET, AN7581_PCM_FRAMES_PER_DESC * bit_width / 8));
If userspace writes interleaved samples as advertised, might the hardware misinterpret the audio data?
[ ... ]
Quoted 3 lines
(quoted diff) +3
-0+static int an7581_pcm_fe_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{
[ ... ]
Review
Quoted 6 lines
(quoted diff) +6
-0+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + dev_err(rtd->card->dev, "snd_pcm_hw_constraint_integer failed\n"); + + /* Cache stream substream for IRQ handling */ + priv->stream[substream->stream] = substream;
[Severity: High]
Does this code inadvertently continue execution and modify driver state when snd_pcm_hw_constraint_integer() fails? By missing a return statement, the function proceeds to cache the substream pointer and increment priv->users before returning the error code. Because the startup failed, ALSA will abort and never call shutdown, permanently leaking the users count and leaving a dangling substream pointer in priv->stream that could cause a Use-After-Free during the next interrupt.
[ ... ]
Quoted 3 lines
(quoted diff) +3
-0+static void an7581_pcm_fe_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{
[ ... ]
Review
Quoted 3 lines
(quoted diff) +3
-0+ priv->stream[substream->stream] = NULL; + + usleep_range(10000, 20000);
[Severity: High]
Is it possible for a concurrent interrupt handler to execute here? By clearing the stream pointer without calling synchronize_irq(), an already executing handler on another CPU might have already passed the NULL check. Once this function returns, ALSA will free the substream runtime. If the delayed IRQ handler then resumes, could it dereference the freed runtime pointer?
[ ... ]
Quoted 4 lines
(quoted diff) +4
-0+static int an7581_pcm_fe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{
[ ... ]
Review
Quoted 8 lines
(quoted diff) +8
-0+ mutex_lock(&priv->user_lock); + if (priv->users <= 1 && + (priv->current_rate != rate || + priv->current_channels != channels)) { + dev_err(rtd->card->dev, "playback/capture rates/channels must match in full-duplex mode\n"); + mutex_unlock(&priv->user_lock); + return -EBUSY; + }
[Severity: High]
Does this inverted logic unconditionally reject the first stream? Since an7581_pcm_fe_startup() increments priv->users, it will be exactly 1 for the first stream. The condition priv->users <= 1 evaluates to true, and since the uninitialized priv->current_rate is 0, it will mismatch the requested rate. This causes the driver to return -EBUSY immediately, preventing hardware setup. Should this check be for priv->users > 1 instead?
[ ... ]
Quoted 3 lines
(quoted diff) +3
-0+static snd_pcm_uframes_t an7581_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{
[ ... ]
Review
Quoted 9 lines
(quoted diff) +9
-0+ spin_lock_irqsave(&priv->desc_lock, flags); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = bytes_to_frames(runtime, priv->tx_pos); + else + pos = bytes_to_frames(runtime, priv->rx_pos); + spin_unlock_irqrestore(&priv->desc_lock, flags); + + return pos % runtime->buffer_size; +}
[Severity: High]
Could the continuously accumulating tx_pos and rx_pos cause a discontinuous jump when the 32-bit integers wrap? After accumulating 4GB (which wraps to 0), the value is almost certainly not an exact multiple of the ALSA buffer_size. Applying modulo here will cause the ALSA frame pointer to jump backwards unexpectedly, triggering an XRUN and halting the audio stream. Should the byte counters be bounded or wrapped explicitly at the buffer boundary instead?
[ ... ]
Review
Quoted 8 lines
(quoted diff) +8
-0+static irqreturn_t an7581_pcm_irq_handler(int irq, void *data) +{ + struct snd_pcm_substream *substream; + struct an7581_pcm_priv *priv = data; + unsigned long flags; + u32 status, isr = 0; + + regmap_read(priv->map, AN7581_PCM_ISR, &isr);
[Severity: High]
Does this handler leave the interrupt asserted? After reading the AN7581_PCM_ISR register, there is no corresponding regmap_write() to clear the asserted status bits. Without a write-1-to-clear acknowledgment, the interrupt line may remain asserted, potentially leading to an unrecoverable interrupt storm.
Quoted 3 lines
(quoted diff) +3
-0+ + /* Scan descriptor on receiving RX desc update interrupt */ + if (isr & AN7581_PCM_IMR_RDESC_UPDATE_INT) {
[ ... ]
Quoted 15 lines
(quoted diff) +15
-0+ desc->status |= AN7581_PCM_DESC_STATUS_OWNERSHIP_DMA; + + if (snd_pcm_capture_hw_avail(runtime) < runtime->period_size) + goto handle_tx; + + rx_poll = true; + } + + if (rx_poll) + regmap_write(priv->map, AN7581_PCM_RPDR, AN7581_PCM_RX_POLLING); + } + +handle_tx: + /* Scan descriptor on receiving TX desc update interrupt */ + if (isr & AN7581_PCM_IMR_TDESC_UPDATE_INT) {
[ ... ]
Review
Quoted 7 lines
(quoted diff) +7
-0+ desc->status |= AN7581_PCM_DESC_STATUS_OWNERSHIP_DMA; + + if (snd_pcm_playback_hw_avail(runtime) < runtime->period_size) + return IRQ_HANDLED; + + tx_poll = true; + }
[Severity: High]
Could these early exits abandon completed descriptors and stall the stream? The loop iterates over descriptors that the hardware has already completed. If the driver returns early here (or jumps to handle_tx) due to an ALSA underrun, it breaks out of the loop and skips processing the rest of the completed descriptors. This leaves them permanently owned by the CPU, stalls the DMA, and misses the wakeup write to the polling register. Because ALSA core already handles overruns and underruns safely, shouldn't the driver always process all completed descriptors without aborting the loop?
[ ... ]
Quoted 2 lines
(quoted diff) +2
-0+static int an7581_pcm_probe(struct platform_device *pdev) +{
[ ... ]
Review
Quoted 4 lines
(quoted diff) +4
-0+ ret = devm_request_threaded_irq(dev, irq, an7581_pcm_irq_handler, + NULL, IRQF_ONESHOT, dev_name(dev), priv); + if (ret) + return ret;
[Severity: High]
Is it valid to pass IRQF_ONESHOT with a NULL thread_fn? The core IRQ subsystem rejects this combination because it doesn't make sense to keep the interrupt masked for a thread that doesn't exist. This will cause devm_request_threaded_irq() to unconditionally return -EINVAL, causing probe to fail every time.
Quoted 4 lines
(quoted diff) +4
-0+ + spin_lock_init(&priv->desc_lock); + mutex_init(&priv->user_lock); +
[ ... ]
Review
Quoted 3 lines
(quoted diff) +3
-0+ priv->tx_descs = dmam_alloc_coherent(dev, sizeof(struct an7581_pcm_desc) * + AN7581_PCM_TX_DESCRIPTORS, &priv->tx_dma, + GFP_KERNEL);
[Severity: High]
Could this allocation order lead to a Use-After-Free during driver unbind? Since devres frees resources in reverse order (LIFO), priv->tx_descs and priv->rx_descs will be freed before the IRQ is released. Because an7581_pcm_remove() doesn't call synchronize_irq(), an executing interrupt handler might access the DMA descriptors after devres has already freed them.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH v4 2/2] ASoC: airoha: Add support for AN7581 PCM driver
In reply to Christian MarangiQuoted 21 lines · Christian Marangi
The Airoha AN7581 SoC provides a PCM interface for VoIP applications, supporting playback and capture at sample rates of 8 kHz and 16 kHz. The driver configures the PCM bit clock to 2.048 MHz and supports 8-bit and 16-bit samples, with up to 32 channels.
Add an ASoC platform driver for the AN7581 PCM controller configured for ZSI attached voice chip.
Signed-off-by Christian Marangi <[email protected]>--- MAINTAINERS | 7 + sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/airoha/Kconfig | 10 + sound/soc/airoha/Makefile | 3 + sound/soc/airoha/an7581-pcm.c | 904 ++++++++++++++++++++++++++++++++++ 6 files changed, 926 insertions(+) create mode 100644 sound/soc/airoha/Kconfig create mode 100644 sound/soc/airoha/Makefile create mode 100644 sound/soc/airoha/an7581-pcm.c
[...]
Quoted 6 lines
sound/soc/airoha/an7581-pcm.c new file +0
-0@@ -0,0 +1,904 @@
[...]
sound/soc/airoha/an7581-pcm.c line 186
Quoted 4 lines
sound/soc/airoha/an7581-pcm.c new file +4
-0+ +struct an7581_pcm_priv { +struct regmap *map; +struct reset_control *reset;
This doesn't seem to be used anywhere other than _probe? Could be a local variable there.
regards
Philipp