diff options
| author | Nuno Sá <nuno.sa@analog.com> | 2026-03-03 13:25:03 +0300 |
|---|---|---|
| committer | Vinod Koul <vkoul@kernel.org> | 2026-03-09 10:28:21 +0300 |
| commit | ca3bf200dea50fada92ec371e9e294b18a589676 (patch) | |
| tree | a59dfc9ddf195214fa3c8ff2c92db7497833d81a | |
| parent | c60990ba1fb2a6c1ff2789e610aa130f3047a2ff (diff) | |
| download | linux-ca3bf200dea50fada92ec371e9e294b18a589676.tar.xz | |
dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers
As of now, to terminate a cyclic transfer, one pretty much needs to use
brute force and terminate all transfers with .device_terminate_all().
With this change, when a cyclic transfer terminates (and generates an
EOT interrupt), look at any new pending transfer with the DMA_PREP_LOAD_EOT
flag set. If there is one, the current cyclic transfer is terminated and
the next one is enqueued. If the flag is not set, that transfer is ignored.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
Link: https://patch.msgid.link/20260303-axi-dac-cyclic-support-v2-4-0db27b4be95a@analog.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
| -rw-r--r-- | drivers/dma/dma-axi-dmac.c | 34 |
1 files changed, 33 insertions, 1 deletions
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index a47e08d3dbbc..e9814d725322 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -233,6 +233,11 @@ static struct axi_dmac_desc *axi_dmac_get_next_desc(struct axi_dmac *dmac, struct virt_dma_desc *vdesc; struct axi_dmac_desc *desc; + /* + * It means a SW cyclic transfer is in place so we should just return + * the same descriptor. SW cyclic transfer termination is handled + * in axi_dmac_transfer_done(). + */ if (chan->next_desc) return chan->next_desc; @@ -411,6 +416,32 @@ static void axi_dmac_compute_residue(struct axi_dmac_chan *chan, } } +static bool axi_dmac_handle_cyclic_eot(struct axi_dmac_chan *chan, + struct axi_dmac_desc *active) +{ + struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev; + struct virt_dma_desc *vdesc; + + /* wrap around */ + active->num_completed = 0; + + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return false; + if (!(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) { + dev_warn(dev, "Discarding non EOT transfer after cyclic\n"); + list_del(&vdesc->node); + return false; + } + + /* then let's end the cyclic transfer */ + chan->next_desc = NULL; + list_del(&active->vdesc.node); + vchan_cookie_complete(&active->vdesc); + + return true; +} + static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, unsigned int completed_transfers) { @@ -458,7 +489,8 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, if (active->num_completed == active->num_sgs || sg->partial_len) { if (active->cyclic) { - active->num_completed = 0; /* wrap around */ + /* keep start_next as is, if already true... */ + start_next |= axi_dmac_handle_cyclic_eot(chan, active); } else { list_del(&active->vdesc.node); vchan_cookie_complete(&active->vdesc); |
