mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 05:31:37 -04:00
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>
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user