public inbox for dmaengine@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination
@ 2026-03-03 10:24 Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 1/5] dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec() Nuno Sá via B4 Relay
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-03-03 10:24 UTC (permalink / raw)
  To: dmaengine; +Cc: Lars-Peter Clausen, Vinod Koul, Frank Li

This series adds support for cyclic transfers in the .device_prep_peripheral_dma_vec()
callback and implements graceful termination of cyclic transfers using the
DMA_PREP_LOAD_EOT flag. Using DMA_PREP_REPEAT and DMA_PREP_LOAD_EOT is
based on the discussion in [1].

Currently, the only way to stop a cyclic transfer is through brute force using
.device_terminate_all(), which terminates all pending transfers. This series
introduces a mechanism to gracefully terminate individual cyclic transfers when
a new transfer flagged with DMA_PREP_LOAD_EOT is queued.

We need two different approaches:

1. Software-managed cyclic transfers: These generate EOT (End-Of-Transfer)
   interrupts for each cycle. Hence, termination can be handled directly
   in the interrupt handler when the EOT interrupt fires, making the
   transition to the next transfer straightforward.

2. Hardware-managed cyclic transfers: These are optimized to avoid interrupt
   overhead by suppressing EOT interrupts. Since there are no EOT interrupts,
   termination must be detected at SOF (Start-Of-Frame) when new transfers
   are being considered. The transfer is marked for termination and the
   hardware is configured to end the current cycle gracefully.

For HW-managed cyclic mode, the series handles both scatter-gather and non-SG
variants. With SG support, the last segment flags are modified to trigger EOT.
Without SG, the CYCLIC flag is cleared to allow natural completion. A workaround
is included for older IP cores (pre-4.6.a) that can prefetch data incorrectly
when clearing the CYCLIC flag, requiring a core disable/enable cycle.

Frank, I opted to not go with your style suggestion for axi_dmac_get_next_desc()
given that it seems to boil down to preference preference (there's no
80 col limit cross) and I do have a slight preference for the current
style. So, unless Vinod jumps in with some preference, I would prefer to
keep it.

Another meaningful change is that ADI hdl team will start to use
semantic versioning for the hdl cores [2]. So increasing the minor number to
6 will lead to 4.6.0 instead of 4.6.a.

[1]: https://lore.kernel.org/dmaengine/ZhJW9JEqN2wrejvC@matsya/
[2]: https://github.com/analogdevicesinc/hdl/pull/1831

---
Changes in v2:
- All patches but 1:
  * Be consistent with git subject.
- Patch 4:
  * Refactor commit message to avoid "we".
- Patch 5:
  * Use semantic versioning for the condition detecting
    hw_cyclic_hotfix. 
- Link to v1: https://lore.kernel.org/r/20260127-axi-dac-cyclic-support-v1-0-b32daca4b3c7@analog.com

---
Nuno Sá (5):
      dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec()
      dmaengine: dma-axi-dmac: Add cyclic transfers in .device_prep_peripheral_dma_vec()
      dmaengine: dma-axi-dmac: Add helper for getting next desc
      dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers
      dmaengine: dma-axi-dmac: Gracefully terminate HW cyclic transfers

 drivers/dma/dma-axi-dmac.c | 170 +++++++++++++++++++++++++++++++++++++++------
 include/linux/dmaengine.h  |   3 +-
 2 files changed, 151 insertions(+), 22 deletions(-)
---
base-commit: c8e9b1d9febc83ee94944695a07cfd40a1b29743
change-id: 20260126-axi-dac-cyclic-support-a06721b2e107
--

Thanks!
- Nuno Sá



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 1/5] dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec()
  2026-03-03 10:24 [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Nuno Sá via B4 Relay
@ 2026-03-03 10:25 ` Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 2/5] dmaengine: dma-axi-dmac: Add cyclic transfers in .device_prep_peripheral_dma_vec() Nuno Sá via B4 Relay
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-03-03 10:25 UTC (permalink / raw)
  To: dmaengine; +Cc: Lars-Peter Clausen, Vinod Koul, Frank Li

From: Nuno Sá <nuno.sa@analog.com>

Document that the DMA_PREP_REPEAT flag can be used with the
dmaengine_prep_peripheral_dma_vec() to mark a transfer as cyclic similar
to dmaengine_prep_dma_cyclic().

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 include/linux/dmaengine.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 99efe2b9b4ea..b3d251c9734e 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -996,7 +996,8 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
  * @vecs: The array of DMA vectors that should be transferred
  * @nents: The number of DMA vectors in the array
  * @dir: Specifies the direction of the data transfer
- * @flags: DMA engine flags
+ * @flags: DMA engine flags - DMA_PREP_REPEAT can be used to mark a cyclic
+ *         DMA transfer
  */
 static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
 	struct dma_chan *chan, const struct dma_vec *vecs, size_t nents,

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 2/5] dmaengine: dma-axi-dmac: Add cyclic transfers in .device_prep_peripheral_dma_vec()
  2026-03-03 10:24 [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 1/5] dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec() Nuno Sá via B4 Relay
@ 2026-03-03 10:25 ` Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 3/5] dmaengine: dma-axi-dmac: Add helper for getting next desc Nuno Sá via B4 Relay
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-03-03 10:25 UTC (permalink / raw)
  To: dmaengine; +Cc: Lars-Peter Clausen, Vinod Koul, Frank Li

From: Nuno Sá <nuno.sa@analog.com>

Add support for cyclic transfers by checking the DMA_PREP_REPEAT
flag. If the flag is set, close the loop and clear the flag for the last
segment (the same done for .device_prep_dma_cyclic().

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 drivers/dma/dma-axi-dmac.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index eb65872c5d5c..79c01b7d9732 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -657,7 +657,12 @@ axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs,
 					      vecs[i].len, dsg);
 	}
 
-	desc->cyclic = false;
+	desc->cyclic = flags & DMA_PREP_REPEAT;
+	if (desc->cyclic) {
+		/* Chain the last descriptor to the first, and remove its "last" flag */
+		desc->sg[num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_LAST;
+		desc->sg[num_sgs - 1].hw->next_sg_addr = desc->sg[0].hw_phys;
+	}
 
 	return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
 }

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 3/5] dmaengine: dma-axi-dmac: Add helper for getting next desc
  2026-03-03 10:24 [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 1/5] dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec() Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 2/5] dmaengine: dma-axi-dmac: Add cyclic transfers in .device_prep_peripheral_dma_vec() Nuno Sá via B4 Relay
@ 2026-03-03 10:25 ` Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 4/5] dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers Nuno Sá via B4 Relay
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-03-03 10:25 UTC (permalink / raw)
  To: dmaengine; +Cc: Lars-Peter Clausen, Vinod Koul, Frank Li

From: Nuno Sá <nuno.sa@analog.com>

Add a new helper for getting the next valid struct axi_dmac_desc. This
will be extended in follow up patches to support to gracefully terminate
cyclic transfers.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 drivers/dma/dma-axi-dmac.c | 33 +++++++++++++++++++++++----------
 1 file changed, 23 insertions(+), 10 deletions(-)

diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index 79c01b7d9732..a47e08d3dbbc 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -227,10 +227,29 @@ static bool axi_dmac_check_addr(struct axi_dmac_chan *chan, dma_addr_t addr)
 	return true;
 }
 
+static struct axi_dmac_desc *axi_dmac_get_next_desc(struct axi_dmac *dmac,
+						    struct axi_dmac_chan *chan)
+{
+	struct virt_dma_desc *vdesc;
+	struct axi_dmac_desc *desc;
+
+	if (chan->next_desc)
+		return chan->next_desc;
+
+	vdesc = vchan_next_desc(&chan->vchan);
+	if (!vdesc)
+		return NULL;
+
+	list_move_tail(&vdesc->node, &chan->active_descs);
+	desc = to_axi_dmac_desc(vdesc);
+	chan->next_desc = desc;
+
+	return desc;
+}
+
 static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
 {
 	struct axi_dmac *dmac = chan_to_axi_dmac(chan);
-	struct virt_dma_desc *vdesc;
 	struct axi_dmac_desc *desc;
 	struct axi_dmac_sg *sg;
 	unsigned int flags = 0;
@@ -240,16 +259,10 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
 	if (val) /* Queue is full, wait for the next SOT IRQ */
 		return;
 
-	desc = chan->next_desc;
+	desc = axi_dmac_get_next_desc(dmac, chan);
+	if (!desc)
+		return;
 
-	if (!desc) {
-		vdesc = vchan_next_desc(&chan->vchan);
-		if (!vdesc)
-			return;
-		list_move_tail(&vdesc->node, &chan->active_descs);
-		desc = to_axi_dmac_desc(vdesc);
-		chan->next_desc = desc;
-	}
 	sg = &desc->sg[desc->num_submitted];
 
 	/* Already queued in cyclic mode. Wait for it to finish */

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 4/5] dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers
  2026-03-03 10:24 [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Nuno Sá via B4 Relay
                   ` (2 preceding siblings ...)
  2026-03-03 10:25 ` [PATCH v2 3/5] dmaengine: dma-axi-dmac: Add helper for getting next desc Nuno Sá via B4 Relay
@ 2026-03-03 10:25 ` Nuno Sá via B4 Relay
  2026-03-03 10:25 ` [PATCH v2 5/5] dmaengine: dma-axi-dmac: Gracefully terminate HW " Nuno Sá via B4 Relay
  2026-03-09  7:46 ` [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Vinod Koul
  5 siblings, 0 replies; 10+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-03-03 10:25 UTC (permalink / raw)
  To: dmaengine; +Cc: Lars-Peter Clausen, Vinod Koul

From: Nuno Sá <nuno.sa@analog.com>

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>
---
 drivers/dma/dma-axi-dmac.c | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

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);

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 5/5] dmaengine: dma-axi-dmac: Gracefully terminate HW cyclic transfers
  2026-03-03 10:24 [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Nuno Sá via B4 Relay
                   ` (3 preceding siblings ...)
  2026-03-03 10:25 ` [PATCH v2 4/5] dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers Nuno Sá via B4 Relay
@ 2026-03-03 10:25 ` Nuno Sá via B4 Relay
  2026-03-09  7:46 ` [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Vinod Koul
  5 siblings, 0 replies; 10+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-03-03 10:25 UTC (permalink / raw)
  To: dmaengine; +Cc: Lars-Peter Clausen, Vinod Koul

From: Nuno Sá <nuno.sa@analog.com>

Add support for gracefully terminating hardware cyclic DMA transfers when
a new transfer is queued and is flagged with DMA_PREP_LOAD_EOT. Without
this, cyclic transfers would continue indefinitely until we brute force
it with .device_terminate_all().

When a new descriptor is queued while a cyclic transfer is active, mark
the cyclic transfer for termination. For hardware with scatter-gather
support, modify the last segment flags to trigger end-of-transfer. For
non-SG hardware, clear the CYCLIC flag to allow natural completion.

Older IP core versions (pre-4.6.a) can prefetch data when clearing the
CYCLIC flag, causing corruption in the next transfer. Work around this
by disabling and re-enabling the core to flush prefetched data.

The cyclic_eot flag tracks transfers marked for termination, preventing
new transfers from starting until the cyclic one completes. Non-EOT
transfers submitted after cyclic transfers are discarded with a warning.

Also note that for hardware cyclic transfers not using SG, we need to
make sure that chan->next_desc is also set to NULL (so we can look at
possible EOT transfers) and we also need to move the queue check to
after axi_dmac_get_next_desc() because with hardware based cyclic
transfers we might get the queue marked as full and hence we would not
be able to check for cyclic termination.

Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
 drivers/dma/dma-axi-dmac.c | 104 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 91 insertions(+), 13 deletions(-)

diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index e9814d725322..45c2c8e4bc45 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -134,6 +134,7 @@ struct axi_dmac_desc {
 	struct axi_dmac_chan *chan;
 
 	bool cyclic;
+	bool cyclic_eot;
 	bool have_partial_xfer;
 
 	unsigned int num_submitted;
@@ -162,6 +163,7 @@ struct axi_dmac_chan {
 	bool hw_cyclic;
 	bool hw_2d;
 	bool hw_sg;
+	bool hw_cyclic_hotfix;
 };
 
 struct axi_dmac {
@@ -227,11 +229,26 @@ static bool axi_dmac_check_addr(struct axi_dmac_chan *chan, dma_addr_t addr)
 	return true;
 }
 
+static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan)
+{
+	return list_first_entry_or_null(&chan->active_descs,
+					struct axi_dmac_desc, vdesc.node);
+}
+
 static struct axi_dmac_desc *axi_dmac_get_next_desc(struct axi_dmac *dmac,
 						    struct axi_dmac_chan *chan)
 {
+	struct axi_dmac_desc *active = axi_dmac_active_desc(chan);
 	struct virt_dma_desc *vdesc;
 	struct axi_dmac_desc *desc;
+	unsigned int val;
+
+	/*
+	 * Just play safe and ignore any SOF if we have an active cyclic transfer
+	 * flagged to end. We'll start it as soon as the current cyclic one ends.
+	 */
+	if (active && active->cyclic_eot)
+		return NULL;
 
 	/*
 	 * It means a SW cyclic transfer is in place so we should just return
@@ -245,11 +262,43 @@ static struct axi_dmac_desc *axi_dmac_get_next_desc(struct axi_dmac *dmac,
 	if (!vdesc)
 		return NULL;
 
+	if (active && active->cyclic && !(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) {
+		struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev;
+
+		dev_warn(dev, "Discarding non EOT transfer after cyclic\n");
+		list_del(&vdesc->node);
+		return NULL;
+	}
+
 	list_move_tail(&vdesc->node, &chan->active_descs);
 	desc = to_axi_dmac_desc(vdesc);
 	chan->next_desc = desc;
 
-	return desc;
+	if (!active || !active->cyclic)
+		return desc;
+
+	active->cyclic_eot = true;
+
+	if (chan->hw_sg) {
+		unsigned long flags = AXI_DMAC_HW_FLAG_IRQ | AXI_DMAC_HW_FLAG_LAST;
+		/*
+		 * Let's then stop the current cyclic transfer by making sure we
+		 * get an EOT interrupt and to open the cyclic loop by marking
+		 * the last segment.
+		 */
+		active->sg[active->num_sgs - 1].hw->flags = flags;
+		return NULL;
+	}
+
+	/*
+	 * Clear the cyclic bit if there's no Scatter-Gather HW so that we get
+	 * at the end of the transfer.
+	 */
+	val = axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS);
+	val &= ~AXI_DMAC_FLAG_CYCLIC;
+	axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, val);
+
+	return NULL;
 }
 
 static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
@@ -260,14 +309,14 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
 	unsigned int flags = 0;
 	unsigned int val;
 
-	val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER);
-	if (val) /* Queue is full, wait for the next SOT IRQ */
-		return;
-
 	desc = axi_dmac_get_next_desc(dmac, chan);
 	if (!desc)
 		return;
 
+	val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER);
+	if (val) /* Queue is full, wait for the next SOT IRQ */
+		return;
+
 	sg = &desc->sg[desc->num_submitted];
 
 	/* Already queued in cyclic mode. Wait for it to finish */
@@ -309,10 +358,12 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
 	 * call, enable hw cyclic mode to avoid unnecessary interrupts.
 	 */
 	if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback) {
-		if (chan->hw_sg)
+		if (chan->hw_sg) {
 			desc->sg[desc->num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_IRQ;
-		else if (desc->num_sgs == 1)
+		} else if (desc->num_sgs == 1) {
+			chan->next_desc = NULL;
 			flags |= AXI_DMAC_FLAG_CYCLIC;
+		}
 	}
 
 	if (chan->hw_partial_xfer)
@@ -330,12 +381,6 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
 	axi_dmac_write(dmac, AXI_DMAC_REG_START_TRANSFER, 1);
 }
 
-static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan)
-{
-	return list_first_entry_or_null(&chan->active_descs,
-		struct axi_dmac_desc, vdesc.node);
-}
-
 static inline unsigned int axi_dmac_total_sg_bytes(struct axi_dmac_chan *chan,
 	struct axi_dmac_sg *sg)
 {
@@ -425,6 +470,35 @@ static bool axi_dmac_handle_cyclic_eot(struct axi_dmac_chan *chan,
 	/* wrap around */
 	active->num_completed = 0;
 
+	if (active->cyclic_eot) {
+		/*
+		 * It means an HW cyclic transfer was marked to stop. And we
+		 * know we have something to schedule, so start the next
+		 * transfer now the cyclic one is done.
+		 */
+		list_del(&active->vdesc.node);
+		vchan_cookie_complete(&active->vdesc);
+
+		if (chan->hw_cyclic_hotfix) {
+			struct axi_dmac *dmac = chan_to_axi_dmac(chan);
+			/*
+			 * In older IP cores, ending a cyclic transfer by clearing
+			 * the CYCLIC flag does not guarantee a graceful end.
+			 * It can happen that some data (of the next frame) is
+			 * already prefetched and will be wrongly visible in the
+			 * next transfer. To workaround this, we need to reenable
+			 * the core so everything is flushed. Newer cores handles
+			 * this correctly and do not require this "hotfix". The
+			 * SG IP also does not require this.
+			 */
+			dev_dbg(dev, "HW cyclic hotfix\n");
+			axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
+			axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE);
+		}
+
+		return true;
+	}
+
 	vdesc = vchan_next_desc(&chan->vchan);
 	if (!vdesc)
 		return false;
@@ -460,6 +534,7 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan,
 	if (chan->hw_sg) {
 		if (active->cyclic) {
 			vchan_cyclic_callback(&active->vdesc);
+			start_next = axi_dmac_handle_cyclic_eot(chan, active);
 		} else {
 			list_del(&active->vdesc.node);
 			vchan_cookie_complete(&active->vdesc);
@@ -1103,6 +1178,9 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
 		chan->length_align_mask = chan->address_align_mask;
 	}
 
+	if (version < ADI_AXI_PCORE_VER(4, 6, 0) && !chan->hw_sg)
+		chan->hw_cyclic_hotfix = true;
+
 	return 0;
 }
 

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination
  2026-03-03 10:24 [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Nuno Sá via B4 Relay
                   ` (4 preceding siblings ...)
  2026-03-03 10:25 ` [PATCH v2 5/5] dmaengine: dma-axi-dmac: Gracefully terminate HW " Nuno Sá via B4 Relay
@ 2026-03-09  7:46 ` Vinod Koul
  2026-03-09 13:30   ` Nuno Sá
  5 siblings, 1 reply; 10+ messages in thread
From: Vinod Koul @ 2026-03-09  7:46 UTC (permalink / raw)
  To: dmaengine, Nuno Sá; +Cc: Lars-Peter Clausen, Frank Li


On Tue, 03 Mar 2026 10:24:59 +0000, Nuno Sá wrote:
> This series adds support for cyclic transfers in the .device_prep_peripheral_dma_vec()
> callback and implements graceful termination of cyclic transfers using the
> DMA_PREP_LOAD_EOT flag. Using DMA_PREP_REPEAT and DMA_PREP_LOAD_EOT is
> based on the discussion in [1].
> 
> Currently, the only way to stop a cyclic transfer is through brute force using
> .device_terminate_all(), which terminates all pending transfers. This series
> introduces a mechanism to gracefully terminate individual cyclic transfers when
> a new transfer flagged with DMA_PREP_LOAD_EOT is queued.
> 
> [...]

Applied, thanks!

[1/5] dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec()
      commit: 5f88899ec7531e1680b1003f32584d7da5922902
[2/5] dmaengine: dma-axi-dmac: Add cyclic transfers in .device_prep_peripheral_dma_vec()
      commit: ac85913ab71e0de9827b7f8f7fccb9f20943c02f
[3/5] dmaengine: dma-axi-dmac: Add helper for getting next desc
      commit: c60990ba1fb2a6c1ff2789e610aa130f3047a2ff
[4/5] dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers
      commit: ca3bf200dea50fada92ec371e9e294b18a589676
[5/5] dmaengine: dma-axi-dmac: Gracefully terminate HW cyclic transfers
      commit: f1d201e7e4e7646e55ce4946f0adec4b035ffb4b

Best regards,
-- 
~Vinod



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination
  2026-03-09  7:46 ` [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Vinod Koul
@ 2026-03-09 13:30   ` Nuno Sá
  2026-03-17 10:09     ` Vinod Koul
  0 siblings, 1 reply; 10+ messages in thread
From: Nuno Sá @ 2026-03-09 13:30 UTC (permalink / raw)
  To: Vinod Koul, dmaengine, Nuno Sá; +Cc: Lars-Peter Clausen, Frank Li

On Mon, 2026-03-09 at 08:46 +0100, Vinod Koul wrote:
> 
> On Tue, 03 Mar 2026 10:24:59 +0000, Nuno Sá wrote:
> > This series adds support for cyclic transfers in the .device_prep_peripheral_dma_vec()
> > callback and implements graceful termination of cyclic transfers using the
> > DMA_PREP_LOAD_EOT flag. Using DMA_PREP_REPEAT and DMA_PREP_LOAD_EOT is
> > based on the discussion in [1].
> > 
> > Currently, the only way to stop a cyclic transfer is through brute force using
> > .device_terminate_all(), which terminates all pending transfers. This series
> > introduces a mechanism to gracefully terminate individual cyclic transfers when
> > a new transfer flagged with DMA_PREP_LOAD_EOT is queued.
> > 
> > [...]
> 
> Applied, thanks!
> 
> [1/5] dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec()
>       commit: 5f88899ec7531e1680b1003f32584d7da5922902
> [2/5] dmaengine: dma-axi-dmac: Add cyclic transfers in .device_prep_peripheral_dma_vec()
>       commit: ac85913ab71e0de9827b7f8f7fccb9f20943c02f
> [3/5] dmaengine: dma-axi-dmac: Add helper for getting next desc
>       commit: c60990ba1fb2a6c1ff2789e610aa130f3047a2ff
> [4/5] dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers
>       commit: ca3bf200dea50fada92ec371e9e294b18a589676
> [5/5] dmaengine: dma-axi-dmac: Gracefully terminate HW cyclic transfers
>       commit: f1d201e7e4e7646e55ce4946f0adec4b035ffb4b
> 
> Best regards,

Hi Vinod,

Thanks for applying the patches. Since I have you here and if you have 5 min I would like to
ask you for some clarifications. It seems there's a bit of a confusion regarding src_addr_widths
and dst_addr_widths. For instance the docs say the following:

" bit mask of src addr widths the channel supports.
Width is specified in bytes, e.g. for a channel supporting
a width of 4 the mask should have BIT(4) set."

And I suspect that BIT(4) is leading into some confusion. Like, if I have a width of 4, then my
mask should look like 0x04 and not 0x20, right? Like the code in [1] looks suspicious to me... And
it seems that pattern is followed in a lot of other places. If I look at [2], then it looks more
with what I would expect.

Like, if the correct way is 1), then it means that 64bytes is not really possible right now given
that BIT(64) is UB and that looks a bit limitating and odd to me. That and given that the AXI_DMAC
might also suffer from a, possible bug, made me want to clarify this.

Thanks!
- Nuno Sá

[1]: https://elixir.bootlin.com/linux/v7.0-rc2/source/drivers/dma/tegra210-adma.c#L1166
[2]: https://elixir.bootlin.com/linux/v7.0-rc2/source/drivers/dma/sh/rcar-dmac.c#L1841

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination
  2026-03-09 13:30   ` Nuno Sá
@ 2026-03-17 10:09     ` Vinod Koul
  2026-03-23  9:34       ` Nuno Sá
  0 siblings, 1 reply; 10+ messages in thread
From: Vinod Koul @ 2026-03-17 10:09 UTC (permalink / raw)
  To: Nuno Sá; +Cc: dmaengine, Nuno Sá, Lars-Peter Clausen, Frank Li

On 09-03-26, 13:30, Nuno Sá wrote:
> On Mon, 2026-03-09 at 08:46 +0100, Vinod Koul wrote:
> Thanks for applying the patches. Since I have you here and if you have 5 min I would like to
> ask you for some clarifications. It seems there's a bit of a confusion regarding src_addr_widths
> and dst_addr_widths. For instance the docs say the following:
> 
> " bit mask of src addr widths the channel supports.
> Width is specified in bytes, e.g. for a channel supporting
> a width of 4 the mask should have BIT(4) set."
> 
> And I suspect that BIT(4) is leading into some confusion. Like, if I have a width of 4, then my
> mask should look like 0x04 and not 0x20, right? Like the code in [1] looks suspicious to me... And
> it seems that pattern is followed in a lot of other places. If I look at [2], then it looks more
> with what I would expect.
> 
> Like, if the correct way is 1), then it means that 64bytes is not really possible right now given
> that BIT(64) is UB and that looks a bit limitating and odd to me. That and given that the AXI_DMAC
> might also suffer from a, possible bug, made me want to clarify this.

If you look at the field it documents "@src_addr_width: this is the width in bytes of the source (RX)
 * register where DMA data shall be read. If the source is memory this
 * may be ignored depending on architecture. Legal values: 1, 2, 3, 4,
 * 8, 16, 32, 64, 128."

We cant have bitmask as 64bit wont work! So I guess lets fix it

-- 
~Vinod

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination
  2026-03-17 10:09     ` Vinod Koul
@ 2026-03-23  9:34       ` Nuno Sá
  0 siblings, 0 replies; 10+ messages in thread
From: Nuno Sá @ 2026-03-23  9:34 UTC (permalink / raw)
  To: Vinod Koul; +Cc: dmaengine, Nuno Sá, Lars-Peter Clausen, Frank Li

On Tue, 2026-03-17 at 15:39 +0530, Vinod Koul wrote:
> On 09-03-26, 13:30, Nuno Sá wrote:
> > On Mon, 2026-03-09 at 08:46 +0100, Vinod Koul wrote:
> > Thanks for applying the patches. Since I have you here and if you have 5 min I would like to
> > ask you for some clarifications. It seems there's a bit of a confusion regarding src_addr_widths
> > and dst_addr_widths. For instance the docs say the following:
> > 
> > " bit mask of src addr widths the channel supports.
> > Width is specified in bytes, e.g. for a channel supporting
> > a width of 4 the mask should have BIT(4) set."
> > 
> > And I suspect that BIT(4) is leading into some confusion. Like, if I have a width of 4, then my
> > mask should look like 0x04 and not 0x20, right? Like the code in [1] looks suspicious to me...
> > And
> > it seems that pattern is followed in a lot of other places. If I look at [2], then it looks more
> > with what I would expect.
> > 
> > Like, if the correct way is 1), then it means that 64bytes is not really possible right now
> > given
> > that BIT(64) is UB and that looks a bit limitating and odd to me. That and given that the
> > AXI_DMAC
> > might also suffer from a, possible bug, made me want to clarify this.
> 
> If you look at the field it documents "@src_addr_width: this is the width in bytes of the source
> (RX)
>  * register where DMA data shall be read. If the source is memory this
>  * may be ignored depending on architecture. Legal values: 1, 2, 3, 4,
>  * 8, 16, 32, 64, 128."

Yeah, I figured. It seems some drivers are using the enum directly as the mask instead for BIT(x).

> 
> We cant have bitmask as 64bit wont work! So I guess lets fix it

I do have a working patch locally. I'll send it later this week.

- Nuno Sá


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-03-23  9:33 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-03 10:24 [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Nuno Sá via B4 Relay
2026-03-03 10:25 ` [PATCH v2 1/5] dmaengine: Document cyclic transfer for dmaengine_prep_peripheral_dma_vec() Nuno Sá via B4 Relay
2026-03-03 10:25 ` [PATCH v2 2/5] dmaengine: dma-axi-dmac: Add cyclic transfers in .device_prep_peripheral_dma_vec() Nuno Sá via B4 Relay
2026-03-03 10:25 ` [PATCH v2 3/5] dmaengine: dma-axi-dmac: Add helper for getting next desc Nuno Sá via B4 Relay
2026-03-03 10:25 ` [PATCH v2 4/5] dmaengine: dma-axi-dmac: Gracefully terminate SW cyclic transfers Nuno Sá via B4 Relay
2026-03-03 10:25 ` [PATCH v2 5/5] dmaengine: dma-axi-dmac: Gracefully terminate HW " Nuno Sá via B4 Relay
2026-03-09  7:46 ` [PATCH v2 0/5] dmaengine: dma-axi-dmac: Add cyclic transfer support and graceful termination Vinod Koul
2026-03-09 13:30   ` Nuno Sá
2026-03-17 10:09     ` Vinod Koul
2026-03-23  9:34       ` Nuno Sá

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox