[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260128-dma_ll_comlib-v1-1-1b1fa2c671f9@nxp.com>
Date: Wed, 28 Jan 2026 13:05:20 -0500
From: Frank Li <Frank.Li@....com>
To: Vinod Koul <vkoul@...nel.org>
Cc: linux-kernel@...r.kernel.org, dmaengine@...r.kernel.org,
imx@...ts.linux.dev, joy.zou@....com, Frank Li <Frank.Li@....com>
Subject: [PATCH RFC 01/12] dmaengine: Extend virt_chan for link list based
DMA engines
Many DMA engines (such as fsl-edma, at-hdmac, and ste-dma40) use
linked-list descriptors for data transfers and share a large amount of
common logic. Add a basic framework to support these link list based DMA
engines and prepare for a common library.
Introduce vchan_dma_ll_terminate_all() as the first shared helper.
Additional common functionality will be added in follow-up patches.
Signed-off-by: Frank Li <Frank.Li@....com>
---
drivers/dma/Kconfig | 4 +++
drivers/dma/Makefile | 1 +
drivers/dma/fsl-edma-common.c | 35 ++++++++++++++++++-------
drivers/dma/ll-dma.c | 61 +++++++++++++++++++++++++++++++++++++++++++
drivers/dma/virt-dma.h | 19 ++++++++++++++
5 files changed, 111 insertions(+), 9 deletions(-)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 8bb0a119ecd48a6695404d43fce225987c9c69ff..5a61907d4d9631e61cf0c44d4104983e9113f28f 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -47,6 +47,9 @@ config DMA_ENGINE
config DMA_VIRTUAL_CHANNELS
tristate
+config DMA_LINKLIST
+ tristate
+
config DMA_ACPI
def_bool y
depends on ACPI
@@ -221,6 +224,7 @@ config FSL_EDMA
depends on HAS_IOMEM
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
+ select DMA_LINKLIST
help
Support the Freescale eDMA engine with programmable channel
multiplexing capability for DMA request sources(slot).
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index a54d7688392b1a0e956fa5d23633507f52f017d9..f1db081a8d2487968f0ca110b80706901f9903ae 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -6,6 +6,7 @@ subdir-ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
#core
obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
+obj-$(CONFIG_DMA_LINKLIST) += ll-dma.o
obj-$(CONFIG_DMA_ACPI) += acpi-dma.o
obj-$(CONFIG_DMA_OF) += of-dma.o
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index c4ac63d9612ce9f1f5826a2186938a785ed529d1..396ff6dfa99a150f9ce34effd64534e3d8e8576b 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -236,16 +236,11 @@ void fsl_edma_free_desc(struct virt_dma_desc *vdesc)
int fsl_edma_terminate_all(struct dma_chan *chan)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
- unsigned long flags;
- LIST_HEAD(head);
+ int ret;
- spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
- fsl_edma_disable_request(fsl_chan);
- fsl_chan->edesc = NULL;
- fsl_chan->status = DMA_COMPLETE;
- vchan_get_all_descriptors(&fsl_chan->vchan, &head);
- spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
- vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+ ret = vchan_dma_ll_terminate_all(chan);
+ if (ret)
+ return ret;
if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_PD)
pm_runtime_allow(fsl_chan->pd_dev);
@@ -830,6 +825,21 @@ void fsl_edma_issue_pending(struct dma_chan *chan)
spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
}
+static int fsl_edma_ll_stop(struct dma_chan *chan)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+
+ fsl_edma_disable_request(fsl_chan);
+ fsl_chan->edesc = NULL;
+ fsl_chan->status = DMA_COMPLETE;
+
+ return 0;
+}
+
+static const struct dma_linklist_ops fsl_edma_ll_ops = {
+ .stop = fsl_edma_ll_stop,
+};
+
int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
@@ -838,6 +848,13 @@ int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK)
clk_prepare_enable(fsl_chan->clk);
+ ret = vchan_dma_ll_init(to_virt_chan(chan), &fsl_edma_ll_ops,
+ fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_TCD64 ?
+ sizeof(struct fsl_edma_hw_tcd64) : sizeof(struct fsl_edma_hw_tcd),
+ 32, 0);
+ if (ret)
+ return ret;
+
fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_TCD64 ?
sizeof(struct fsl_edma_hw_tcd64) : sizeof(struct fsl_edma_hw_tcd),
diff --git a/drivers/dma/ll-dma.c b/drivers/dma/ll-dma.c
new file mode 100644
index 0000000000000000000000000000000000000000..3845cca7926eb71f008cb98d8c622cb28a2369a5
--- /dev/null
+++ b/drivers/dma/ll-dma.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Common library for DMA Controller, which use Link List DMA descriptor.
+ *
+ * For the DMA controller, which DMA descriptor work as Link List, there are
+ * field, which point to next DMA descriptor.
+ *
+ * Each DMA descriptor's size is the same.
+ *
+ * ┌──────┐ ┌──────┐ ┌──────┐
+ * │ │ ┌─►│ │ ┌─►│ │
+ * │ │ │ │ │ │ │ │
+ * ├──────┤ │ ├──────┤ │ ├──────┤
+ * │ Next ├─┘ │ Next ├─┘ │ Next │
+ * └──────┘ └──────┘ └──────┘
+ *
+ */
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+
+int vchan_dma_ll_init(struct virt_dma_chan *vc,
+ const struct dma_linklist_ops *ops, size_t size,
+ size_t align, size_t boundary)
+{
+ if (!ops || !ops->stop)
+ return -EINVAL;
+
+ vc->ll.ops = ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vchan_dma_ll_init);
+
+int vchan_dma_ll_terminate_all(struct dma_chan *chan)
+{
+ struct virt_dma_chan *vchan = to_virt_chan(chan);
+ LIST_HEAD(head);
+ int ret;
+
+ scoped_guard(spinlock_irqsave, &vchan->lock) {
+ ret = vchan->ll.ops->stop(chan);
+ if (ret)
+ return ret;
+
+ vchan_get_all_descriptors(vchan, &head);
+ }
+
+ vchan_dma_desc_free_list(vchan, &head);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vchan_dma_ll_terminate_all);
+
+MODULE_AUTHOR("Frank Li");
+MODULE_DESCRIPTION("Common library for Link List DMA Descriptor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
index 59d9eabc8b6744a439aeed3114164c5203341a6c..081eb910d0b0cd2b60232736587c698fff787cb9 100644
--- a/drivers/dma/virt-dma.h
+++ b/drivers/dma/virt-dma.h
@@ -19,11 +19,23 @@ struct virt_dma_desc {
struct list_head node;
};
+struct dma_linklist_ops {
+ int (*stop)(struct dma_chan *chan);
+};
+
+struct dma_linklist {
+ const struct dma_linklist_ops *ops;
+};
+
struct virt_dma_chan {
struct dma_chan chan;
struct tasklet_struct task;
void (*desc_free)(struct virt_dma_desc *);
+#if IS_ENABLED(CONFIG_DMA_LINKLIST)
+ struct dma_linklist ll;
+#endif
+
spinlock_t lock;
/* protected by vc.lock */
@@ -234,4 +246,11 @@ static inline void vchan_synchronize(struct virt_dma_chan *vc)
vchan_dma_desc_free_list(vc, &head);
}
+#if IS_ENABLED(CONFIG_DMA_LINKLIST)
+int vchan_dma_ll_init(struct virt_dma_chan *vc,
+ const struct dma_linklist_ops *ops, size_t size,
+ size_t align, size_t boundary);
+int vchan_dma_ll_terminate_all(struct dma_chan *chan);
+#endif
+
#endif
--
2.34.1
Powered by blists - more mailing lists