From 446a7d6934730dfe261b9f1ae359de603008593b Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Mon, 10 Aug 2026 15:33:22 +0200 Subject: [PATCH] arch/arm/rp23xx: Clear the DMA callback before invoking it. The completion handler did: dmach->callback(...); dmach->callback = NULL; /* after the call */ so a callback that registers itself again -- which is the only way for a driver to keep a channel running continuously -- has that registration wiped the moment it returns. The channel transfers one more block and then goes deaf, with no error raised anywhere and nothing in the registers to say why. A PWM audio driver hit this and worked around it by restarting from a thread instead, which put a millisecond of silence into every buffer boundary. Lift the callback and its argument out first and clear them before the call. One-shot behaviour is unchanged for every existing user, since none of them re-register from inside the call; the difference is only that one which does now survives. Tested on a Pimoroni Pico Plus 2 W (pimoroni-pico-plus-2-w:nsh) with a local test: a memory-to-memory transfer whose callback starts the next one, 100 times. Master gives 1 completion of 100; this change gives 100. The existing users (SPI, I2S, CYW43439, WS2812) do not start a transfer from inside the callback, so they do not change. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli --- arch/arm/src/rp23xx/rp23xx_dmac.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/arch/arm/src/rp23xx/rp23xx_dmac.c b/arch/arm/src/rp23xx/rp23xx_dmac.c index 942fbbecc25..6936f6d8b28 100644 --- a/arch/arm/src/rp23xx/rp23xx_dmac.c +++ b/arch/arm/src/rp23xx/rp23xx_dmac.c @@ -103,6 +103,8 @@ static struct dma_channel_s g_dmach[RP23XX_DMA_NCHANNELS]; static int rp23xx_dmac_interrupt(int irq, void *context, void *arg) { struct dma_channel_s *dmach; + dma_callback_t callback; + void *cbarg; int result = OK; unsigned int ch; uint32_t stat; @@ -130,15 +132,22 @@ static int rp23xx_dmac_interrupt(int irq, void *context, void *arg) dmach = &g_dmach[ch]; + /* Clear the registration before the call, so that the callback can + * register itself again. + */ + + callback = dmach->callback; + cbarg = dmach->arg; + + dmach->callback = NULL; + dmach->arg = NULL; + /* Call the DMA completion callback */ - if (dmach->callback) + if (callback) { - dmach->callback((DMA_HANDLE)dmach, result, dmach->arg); - dmach->callback = NULL; + callback((DMA_HANDLE)dmach, result, cbarg); } - - dmach->arg = NULL; } return OK;