Linux IIO development
 help / color / mirror / Atom feed
* [PATCH v2] iio: buffer-dmaengine: Add support for cyclic DMA transfers
@ 2026-07-15 11:24 Nuno Sá via B4 Relay
  2026-07-15 14:23 ` Andy Shevchenko
  0 siblings, 1 reply; 7+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-07-15 11:24 UTC (permalink / raw)
  To: linux-iio; +Cc: Jonathan Cameron, David Lechner, Andy Shevchenko

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

Allow buffer blocks flagged as cyclic to be submitted as repeating DMA
transfers. For cyclic blocks, use DMA_PREP_REPEAT so the engine keeps
replaying the descriptor.

This is useful for output buffers where the same data should be driven
continuously without userspace having to requeue it. Examples include
continuous RF transmit paths replaying a calibration, test or beacon
pattern.

Skip installing the completion callback for cyclic blocks. Since the
transfer is continuously replayed, the callback would fire on every
period, throwing off the block refcount.

Because nothing prevents a new cyclic transfer from replacing an
already active cyclic one, always set DMA_PREP_LOAD_EOT so the engine
correctly terminates the active transfer before loading the new
descriptor.

Limit the DMA buffer queue to one cyclic DMABUF at a time. There is
currently no known use case for queueing multiple cyclic blocks, and
cyclic blocks stay referenced until the buffer is disabled.

Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
Changes in v2:
* Improved commit message for usecase;
* Safety cap cyclic transfers to one  (see v1 for context).
* Link to v1: https://lore.kernel.org/linux-iio/20260611-iio-dma-cyclic-buffer-support-v1-1-bcf00e8d802c@analog.com/
---
 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 41 ++++++++++++++++++++--
 1 file changed, 38 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 98acce909854..639bac135539 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -56,6 +56,23 @@ static void iio_dmaengine_buffer_block_done(void *data,
 	iio_dma_buffer_block_done(block);
 }
 
+/*
+ * Cyclic transfers run until abort. Cap active cyclic blocks to one until there
+ * is a use case for more.
+ */
+static bool iio_dmaengine_buffer_has_active_cyclic(struct dmaengine_buffer *dmaengine_buffer)
+{
+	struct iio_dma_buffer_block *block;
+
+	guard(spinlock_irqsave)(&dmaengine_buffer->queue.list_lock);
+	list_for_each_entry(block, &dmaengine_buffer->active, head) {
+		if (block->cyclic)
+			return true;
+	}
+
+	return false;
+}
+
 static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
 					     struct iio_dma_buffer_block *block)
 {
@@ -79,7 +96,14 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
 	else
 		dma_dir = DMA_MEM_TO_DEV;
 
+	if (block->cyclic && iio_dmaengine_buffer_has_active_cyclic(dmaengine_buffer)) {
+		dev_err(queue->dev, "cyclic DMA transfer already active\n");
+		return -EBUSY;
+	}
+
 	if (block->sg_table) {
+		unsigned long flags;
+
 		sgl = block->sg_table->sgl;
 		nents = sg_nents_for_len(sgl, block->bytes_used);
 		if (nents < 0)
@@ -99,9 +123,18 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
 			sgl = sg_next(sgl);
 		}
 
+		if (block->cyclic)
+			flags = DMA_PREP_REPEAT;
+		else
+			flags = DMA_PREP_INTERRUPT;
+
+		/*
+		 * A new transfer may need to end an already active cyclic transfer
+		 * before it can run, so always set the EOT flag.
+		 */
 		desc = dmaengine_prep_peripheral_dma_vec(dmaengine_buffer->chan,
 							 vecs, nents, dma_dir,
-							 DMA_PREP_INTERRUPT);
+							 flags | DMA_PREP_LOAD_EOT);
 		kfree(vecs);
 	} else {
 		max_size = min(block->size, dmaengine_buffer->max_size);
@@ -122,8 +155,10 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
 	if (!desc)
 		return -ENOMEM;
 
-	desc->callback_result = iio_dmaengine_buffer_block_done;
-	desc->callback_param = block;
+	if (!block->cyclic) {
+		desc->callback_result = iio_dmaengine_buffer_block_done;
+		desc->callback_param = block;
+	}
 
 	cookie = dmaengine_submit(desc);
 	if (dma_submit_error(cookie))

---
base-commit: aa58ecc73466d0cb8c418de98e2225490bf600e3
change-id: 20260714-iio-dma-cyclic-02ad77c2cc40
--

Thanks!
- Nuno Sá



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

end of thread, other threads:[~2026-07-20 13:41 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 11:24 [PATCH v2] iio: buffer-dmaengine: Add support for cyclic DMA transfers Nuno Sá via B4 Relay
2026-07-15 14:23 ` Andy Shevchenko
2026-07-15 15:13   ` Nuno Sá
2026-07-15 16:09     ` Andy Shevchenko
2026-07-19  2:00       ` Jonathan Cameron
2026-07-19  2:03         ` Jonathan Cameron
2026-07-20 13:43           ` Nuno Sá

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