* [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