* [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements
@ 2026-01-06 11:34 Alain Volmat
2026-01-06 11:34 ` [PATCH v2 01/12] media: stm32: dcmi: Switch from __maybe_unused to pm_ptr() Alain Volmat
` (13 more replies)
0 siblings, 14 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
This series improve stability of the capture by fixing the
handling of the overrun which was leading to captured
frame corruption.
Locking within the driver is also simplified and the way
DMA is handled is reworked allowing to avoid having a
specific handling for the JPEG data.
Performances of capture can now be increased via the usage
of a DMA->MDMA chaining which allows for capture of higher
resolution / framerate.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
Changes in v2:
- Fix pm_sleep_ptr -> pm_ptr to avoid unused function warning
- Fix typo / remove useless comment in binding
- Link to v1: https://lore.kernel.org/r/20251218-stm32-dcmi-dma-chaining-v1-0-39948ca6cbf6@foss.st.com
---
Alain Volmat (12):
media: stm32: dcmi: Switch from __maybe_unused to pm_ptr()
media: stm32: dcmi: perform dmaengine_slave_config at probe
media: stm32: dcmi: only create dma descriptor once at buf_prepare
media: stm32: dcmi: stop the dma transfer on overrun
media: stm32: dcmi: rework spin_lock calls
media: stm32: dcmi: perform all dma handling within irq_thread
media: stm32: dcmi: use dmaengine_terminate_async in irq context
media: stm32: dcmi: continuous mode capture in JPEG
dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties
media: stm32: dcmi: addition of DMA-MDMA chaining support
ARM: dts: stm32: add sram node within stm32mp151.dtsi
ARM: dts: stm32: enable DCMI DMA-MDMA chaining on stm32mp157c-ev1.dts
.../devicetree/bindings/media/st,stm32-dcmi.yaml | 11 +-
arch/arm/boot/dts/st/stm32mp151.dtsi | 8 +
arch/arm/boot/dts/st/stm32mp157c-ev1.dts | 15 +
drivers/media/platform/st/stm32/stm32-dcmi.c | 475 +++++++++++++--------
4 files changed, 341 insertions(+), 168 deletions(-)
---
base-commit: f7231cff1f3ff8259bef02dc4999bc132abf29cf
change-id: 20251213-stm32-dcmi-dma-chaining-9ea1da83007d
Best regards,
--
Alain Volmat <alain.volmat@foss.st.com>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 01/12] media: stm32: dcmi: Switch from __maybe_unused to pm_ptr()
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 02/12] media: stm32: dcmi: perform dmaengine_slave_config at probe Alain Volmat
` (12 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Letting the compiler remove these functions when the kernel is built
without CONFIG_PM_SLEEP support is simpler and less heavier for builds
than the use of __maybe_unused attributes.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
Changes in v2:
- use pm_ptr instead of pm_sleep_ptr use
SYSTEM_SLEEP_PM_OPS / RUNTIME_PM_OPS macros
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 13762861b769..619b0d981211 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -2092,7 +2092,7 @@ static void dcmi_remove(struct platform_device *pdev)
dma_release_channel(dcmi->dma_chan);
}
-static __maybe_unused int dcmi_runtime_suspend(struct device *dev)
+static int dcmi_runtime_suspend(struct device *dev)
{
struct stm32_dcmi *dcmi = dev_get_drvdata(dev);
@@ -2101,7 +2101,7 @@ static __maybe_unused int dcmi_runtime_suspend(struct device *dev)
return 0;
}
-static __maybe_unused int dcmi_runtime_resume(struct device *dev)
+static int dcmi_runtime_resume(struct device *dev)
{
struct stm32_dcmi *dcmi = dev_get_drvdata(dev);
int ret;
@@ -2113,7 +2113,7 @@ static __maybe_unused int dcmi_runtime_resume(struct device *dev)
return ret;
}
-static __maybe_unused int dcmi_suspend(struct device *dev)
+static int dcmi_suspend(struct device *dev)
{
/* disable clock */
pm_runtime_force_suspend(dev);
@@ -2124,7 +2124,7 @@ static __maybe_unused int dcmi_suspend(struct device *dev)
return 0;
}
-static __maybe_unused int dcmi_resume(struct device *dev)
+static int dcmi_resume(struct device *dev)
{
/* restore pinctl default state */
pinctrl_pm_select_default_state(dev);
@@ -2136,9 +2136,8 @@ static __maybe_unused int dcmi_resume(struct device *dev)
}
static const struct dev_pm_ops dcmi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume)
- SET_RUNTIME_PM_OPS(dcmi_runtime_suspend,
- dcmi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume)
+ RUNTIME_PM_OPS(dcmi_runtime_suspend, dcmi_runtime_resume, NULL)
};
static struct platform_driver stm32_dcmi_driver = {
@@ -2147,7 +2146,7 @@ static struct platform_driver stm32_dcmi_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(stm32_dcmi_of_match),
- .pm = &dcmi_pm_ops,
+ .pm = pm_ptr(&dcmi_pm_ops),
},
};
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 02/12] media: stm32: dcmi: perform dmaengine_slave_config at probe
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
2026-01-06 11:34 ` [PATCH v2 01/12] media: stm32: dcmi: Switch from __maybe_unused to pm_ptr() Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 03/12] media: stm32: dcmi: only create dma descriptor once at buf_prepare Alain Volmat
` (11 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Perform the dma channel configuration at probe time right after
the channel allocation since this is fixed for the whole lifetime
of the driver.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 32 +++++++++++++---------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 619b0d981211..787c5d778db2 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -301,23 +301,6 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
struct dcmi_buf *buf)
{
struct dma_async_tx_descriptor *desc = NULL;
- struct dma_slave_config config;
- int ret;
-
- memset(&config, 0, sizeof(config));
-
- config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR;
- config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.dst_maxburst = 4;
-
- /* Configure DMA channel */
- ret = dmaengine_slave_config(dcmi->dma_chan, &config);
- if (ret < 0) {
- dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n",
- __func__, ret);
- return ret;
- }
/*
* Avoid call of dmaengine_terminate_sync() between
@@ -1888,6 +1871,7 @@ static int dcmi_probe(struct platform_device *pdev)
struct vb2_queue *q;
struct dma_chan *chan;
struct dma_slave_caps caps;
+ struct dma_slave_config dma_config;
struct clk *mclk;
int ret = 0;
@@ -1954,6 +1938,19 @@ static int dcmi_probe(struct platform_device *pdev)
if (!ret && caps.max_sg_burst)
dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES;
+ memset(&dma_config, 0, sizeof(dma_config));
+
+ dma_config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR;
+ dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ /* Configure DMA channel */
+ ret = dmaengine_slave_config(chan, &dma_config);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n",
+ __func__, ret);
+ goto err_dma_slave_config;
+ }
+
spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
mutex_init(&dcmi->dma_lock);
@@ -2072,6 +2069,7 @@ static int dcmi_probe(struct platform_device *pdev)
v4l2_device_unregister(&dcmi->v4l2_dev);
err_media_device_cleanup:
media_device_cleanup(&dcmi->mdev);
+err_dma_slave_config:
dma_release_channel(dcmi->dma_chan);
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 03/12] media: stm32: dcmi: only create dma descriptor once at buf_prepare
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
2026-01-06 11:34 ` [PATCH v2 01/12] media: stm32: dcmi: Switch from __maybe_unused to pm_ptr() Alain Volmat
2026-01-06 11:34 ` [PATCH v2 02/12] media: stm32: dcmi: perform dmaengine_slave_config at probe Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 04/12] media: stm32: dcmi: stop the dma transfer on overrun Alain Volmat
` (10 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Perform the dmaengine prep_slave_sg call within buf_prepare and
mark the descriptor as reusable in order to avoid having to redo
this at every start of the dma.
This also allow to remove the mutex used by the driver to protect
dma descriptors related piece of codes.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 78 ++++++++++++++++------------
1 file changed, 45 insertions(+), 33 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 787c5d778db2..0874974bbad4 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -113,6 +113,7 @@ struct dcmi_buf {
struct vb2_v4l2_buffer vb;
bool prepared;
struct sg_table sgt;
+ struct dma_async_tx_descriptor *dma_desc;
size_t size;
struct list_head list;
};
@@ -163,9 +164,6 @@ struct stm32_dcmi {
int overrun_count;
int buffers_count;
- /* Ensure DMA operations atomicity */
- struct mutex dma_lock;
-
struct media_device mdev;
struct media_pad vid_cap_pad;
struct media_pipeline pipeline;
@@ -300,39 +298,13 @@ static void dcmi_dma_callback(void *param)
static int dcmi_start_dma(struct stm32_dcmi *dcmi,
struct dcmi_buf *buf)
{
- struct dma_async_tx_descriptor *desc = NULL;
-
- /*
- * Avoid call of dmaengine_terminate_sync() between
- * dmaengine_prep_slave_single() and dmaengine_submit()
- * by locking the whole DMA submission sequence
- */
- mutex_lock(&dcmi->dma_lock);
-
- /* Prepare a DMA transaction */
- desc = dmaengine_prep_slave_sg(dcmi->dma_chan, buf->sgt.sgl, buf->sgt.nents,
- DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
- if (!desc) {
- dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__);
- mutex_unlock(&dcmi->dma_lock);
- return -EINVAL;
- }
-
- /* Set completion callback routine for notification */
- desc->callback = dcmi_dma_callback;
- desc->callback_param = dcmi;
-
/* Push current DMA transaction in the pending queue */
- dcmi->dma_cookie = dmaengine_submit(desc);
+ dcmi->dma_cookie = dmaengine_submit(buf->dma_desc);
if (dma_submit_error(dcmi->dma_cookie)) {
dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__);
- mutex_unlock(&dcmi->dma_lock);
return -ENXIO;
}
- mutex_unlock(&dcmi->dma_lock);
-
dma_async_issue_pending(dcmi->dma_chan);
return 0;
@@ -547,6 +519,31 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
size -= bytes;
}
+ /* Prepare a DMA transaction */
+ buf->dma_desc = dmaengine_prep_slave_sg(dcmi->dma_chan,
+ buf->sgt.sgl,
+ buf->sgt.nents,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!buf->dma_desc) {
+ dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+
+ /* Set completion callback routine for notification */
+ buf->dma_desc->callback = dcmi_dma_callback;
+ buf->dma_desc->callback_param = dcmi;
+
+ /* Mark the descriptor as reusable to avoid having to prepare it */
+ ret = dmaengine_desc_set_reuse(buf->dma_desc);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: DMA dmaengine_desc_set_reuse failed\n", __func__);
+ dmaengine_desc_free(buf->dma_desc);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+
buf->prepared = true;
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
@@ -555,6 +552,23 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
return 0;
}
+static void dcmi_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
+ int ret;
+
+ if (!buf->prepared)
+ return;
+
+ ret = dmaengine_desc_free(buf->dma_desc);
+ if (ret)
+ dev_err(dcmi->dev, "%s: Failed to free the mdma descriptor (0x%x)\n",
+ __func__, ret);
+ sg_free_table(&buf->sgt);
+}
+
static void dcmi_buf_queue(struct vb2_buffer *vb)
{
struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue);
@@ -859,9 +873,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
spin_unlock_irq(&dcmi->irqlock);
/* Stop all pending DMA operations */
- mutex_lock(&dcmi->dma_lock);
dmaengine_terminate_sync(dcmi->dma_chan);
- mutex_unlock(&dcmi->dma_lock);
pm_runtime_put(dcmi->dev);
@@ -878,6 +890,7 @@ static const struct vb2_ops dcmi_video_qops = {
.queue_setup = dcmi_queue_setup,
.buf_init = dcmi_buf_init,
.buf_prepare = dcmi_buf_prepare,
+ .buf_cleanup = dcmi_buf_cleanup,
.buf_queue = dcmi_buf_queue,
.start_streaming = dcmi_start_streaming,
.stop_streaming = dcmi_stop_streaming,
@@ -1953,7 +1966,6 @@ static int dcmi_probe(struct platform_device *pdev)
spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
- mutex_init(&dcmi->dma_lock);
init_completion(&dcmi->complete);
INIT_LIST_HEAD(&dcmi->buffers);
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 04/12] media: stm32: dcmi: stop the dma transfer on overrun
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (2 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 03/12] media: stm32: dcmi: only create dma descriptor once at buf_prepare Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 05/12] media: stm32: dcmi: rework spin_lock calls Alain Volmat
` (9 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Ensure to stop the dma transfer whenever receiving a overrun
to avoid having a buffer partially filled with a frame and
partially with the next frame.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 0874974bbad4..20f5b9ab6a1c 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -402,9 +402,21 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
spin_lock_irq(&dcmi->irqlock);
if (dcmi->misr & IT_OVR) {
+ /* Disable capture */
+ reg_clear(dcmi->regs, DCMI_CR, CR_CAPTURE);
+
dcmi->overrun_count++;
+
if (dcmi->overrun_count > OVERRUN_ERROR_THRESHOLD)
dcmi->errors_count++;
+
+ spin_unlock_irq(&dcmi->irqlock);
+ dmaengine_terminate_sync(dcmi->dma_chan);
+
+ if (dcmi_restart_capture(dcmi))
+ dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__);
+
+ return IRQ_HANDLED;
}
if (dcmi->misr & IT_ERR)
dcmi->errors_count++;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 05/12] media: stm32: dcmi: rework spin_lock calls
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (3 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 04/12] media: stm32: dcmi: stop the dma transfer on overrun Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 06/12] media: stm32: dcmi: perform all dma handling within irq_thread Alain Volmat
` (8 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Rework of the spin_lock calls in preparation of the rework
of the data handling of the driver. Keep it straight forward
with basically spin_lock protection around everything except
dmaengine calls that might sleep (ex: synchronize / terminate)
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 20f5b9ab6a1c..2fec10d13913 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -227,25 +227,18 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
{
struct dcmi_buf *buf;
- spin_lock_irq(&dcmi->irqlock);
-
- if (dcmi->state != RUNNING) {
- spin_unlock_irq(&dcmi->irqlock);
+ if (dcmi->state != RUNNING)
return -EINVAL;
- }
/* Restart a new DMA transfer with next buffer */
if (list_empty(&dcmi->buffers)) {
dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n");
dcmi->state = WAIT_FOR_BUFFER;
- spin_unlock_irq(&dcmi->irqlock);
return 0;
}
buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list);
dcmi->active = buf;
- spin_unlock_irq(&dcmi->irqlock);
-
return dcmi_start_capture(dcmi, buf);
}
@@ -370,8 +363,10 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
* buffer payload.
*/
+ spin_unlock_irq(&dcmi->irqlock);
/* Drain DMA */
dmaengine_synchronize(dcmi->dma_chan);
+ spin_lock_irq(&dcmi->irqlock);
/* Get DMA residue to get JPEG size */
status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
@@ -386,8 +381,10 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
dcmi_buffer_done(dcmi, buf, 0, -EIO);
}
+ spin_unlock_irq(&dcmi->irqlock);
/* Abort DMA operation */
dmaengine_terminate_sync(dcmi->dma_chan);
+ spin_lock_irq(&dcmi->irqlock);
/* Restart capture */
if (dcmi_restart_capture(dcmi))
@@ -413,8 +410,10 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
spin_unlock_irq(&dcmi->irqlock);
dmaengine_terminate_sync(dcmi->dma_chan);
+ spin_lock_irq(&dcmi->irqlock);
if (dcmi_restart_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__);
+ spin_unlock_irq(&dcmi->irqlock);
return IRQ_HANDLED;
}
@@ -424,8 +423,8 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG &&
dcmi->misr & IT_FRAME) {
/* JPEG received */
- spin_unlock_irq(&dcmi->irqlock);
dcmi_process_jpeg(dcmi);
+ spin_unlock_irq(&dcmi->irqlock);
return IRQ_HANDLED;
}
@@ -599,11 +598,9 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
buf->vb.vb2_buf.index);
- spin_unlock_irq(&dcmi->irqlock);
if (dcmi_start_capture(dcmi, buf))
dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
__func__);
- return;
}
spin_unlock_irq(&dcmi->irqlock);
@@ -812,11 +809,11 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
dev_dbg(dcmi->dev, "Start streaming, starting capture\n");
- spin_unlock_irq(&dcmi->irqlock);
ret = dcmi_start_capture(dcmi, buf);
if (ret) {
dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n",
__func__);
+ spin_unlock_irq(&dcmi->irqlock);
goto err_pipeline_stop;
}
@@ -826,6 +823,8 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
else
reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR);
+ spin_unlock_irq(&dcmi->irqlock);
+
return 0;
err_pipeline_stop:
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 06/12] media: stm32: dcmi: perform all dma handling within irq_thread
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (4 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 05/12] media: stm32: dcmi: rework spin_lock calls Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 07/12] media: stm32: dcmi: use dmaengine_terminate_async in irq context Alain Volmat
` (7 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Move all the type of frame handling within the dcmi_irq_thread
handler and do not rely on dma_callback as previously.
This simplifies the code by having only a single path for both
compressed and uncompressed data while also making the system
more reactive since irq_handler have more chances to be called
faster than the dma completion callback. Indeed, in case of the
dma completion callback, this run as a tasklet created by the
dma framework upon getting an interrupt from the dma and run
at a lower priority level than other irq handlers.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 97 ++++++----------------------
1 file changed, 18 insertions(+), 79 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 2fec10d13913..66bb79038c54 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -227,8 +227,9 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
{
struct dcmi_buf *buf;
+ /* Nothing to do if we are not running */
if (dcmi->state != RUNNING)
- return -EINVAL;
+ return 0;
/* Restart a new DMA transfer with next buffer */
if (list_empty(&dcmi->buffers)) {
@@ -242,52 +243,6 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
return dcmi_start_capture(dcmi, buf);
}
-static void dcmi_dma_callback(void *param)
-{
- struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
- struct dma_tx_state state;
- enum dma_status status;
- struct dcmi_buf *buf = dcmi->active;
-
- spin_lock_irq(&dcmi->irqlock);
-
- /* Check DMA status */
- status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
-
- switch (status) {
- case DMA_IN_PROGRESS:
- dev_dbg(dcmi->dev, "%s: Received DMA_IN_PROGRESS\n", __func__);
- break;
- case DMA_PAUSED:
- dev_err(dcmi->dev, "%s: Received DMA_PAUSED\n", __func__);
- break;
- case DMA_ERROR:
- dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__);
-
- /* Return buffer to V4L2 in error state */
- dcmi_buffer_done(dcmi, buf, 0, -EIO);
- break;
- case DMA_COMPLETE:
- dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
-
- /* Return buffer to V4L2 */
- dcmi_buffer_done(dcmi, buf, buf->size, 0);
-
- spin_unlock_irq(&dcmi->irqlock);
-
- /* Restart capture */
- if (dcmi_restart_capture(dcmi))
- dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n",
- __func__);
- return;
- default:
- dev_err(dcmi->dev, "%s: Received unknown status\n", __func__);
- break;
- }
-
- spin_unlock_irq(&dcmi->irqlock);
-}
-
static int dcmi_start_dma(struct stm32_dcmi *dcmi,
struct dcmi_buf *buf)
{
@@ -344,7 +299,7 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi)
reg_set(dcmi->regs, DCMI_CR, CR_CROP);
}
-static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
+static void dcmi_process_frame(struct stm32_dcmi *dcmi)
{
struct dma_tx_state state;
enum dma_status status;
@@ -354,13 +309,11 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
return;
/*
- * Because of variable JPEG buffer size sent by sensor,
- * DMA transfer never completes due to transfer size never reached.
- * In order to ensure that all the JPEG data are transferred
- * in active buffer memory, DMA is drained.
- * Then DMA tx status gives the amount of data transferred
- * to memory, which is then returned to V4L2 through the active
- * buffer payload.
+ * At the time FRAME interrupt is received, all dma req have been sent to the DMA,
+ * however DMA might still be transferring data hence first synchronize prior to
+ * getting the status of the DMA transfer.
+ * Then DMA tx status gives the amount of data transferred to memory, which is then
+ * returned to V4L2 through the active buffer payload.
*/
spin_unlock_irq(&dcmi->irqlock);
@@ -368,16 +321,16 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
dmaengine_synchronize(dcmi->dma_chan);
spin_lock_irq(&dcmi->irqlock);
- /* Get DMA residue to get JPEG size */
+ /* Get DMA status and residue size */
status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
if (status != DMA_ERROR && state.residue < buf->size) {
- /* Return JPEG buffer to V4L2 with received JPEG buffer size */
+ /* Return buffer to V4L2 with received data size */
dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0);
} else {
dcmi->errors_count++;
- dev_err(dcmi->dev, "%s: Cannot get JPEG size from DMA\n",
- __func__);
- /* Return JPEG buffer to V4L2 in ERROR state */
+ dev_err(dcmi->dev, "%s: DMA error. status: 0x%x, residue: %d\n",
+ __func__, status, state.residue);
+ /* Return buffer to V4L2 in ERROR state */
dcmi_buffer_done(dcmi, buf, 0, -EIO);
}
@@ -385,11 +338,6 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
/* Abort DMA operation */
dmaengine_terminate_sync(dcmi->dma_chan);
spin_lock_irq(&dcmi->irqlock);
-
- /* Restart capture */
- if (dcmi_restart_capture(dcmi))
- dev_err(dcmi->dev, "%s: Cannot restart capture on JPEG received\n",
- __func__);
}
static irqreturn_t dcmi_irq_thread(int irq, void *arg)
@@ -420,12 +368,10 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
if (dcmi->misr & IT_ERR)
dcmi->errors_count++;
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG &&
- dcmi->misr & IT_FRAME) {
- /* JPEG received */
- dcmi_process_jpeg(dcmi);
- spin_unlock_irq(&dcmi->irqlock);
- return IRQ_HANDLED;
+ if (dcmi->misr & IT_FRAME) {
+ dcmi_process_frame(dcmi);
+ if (dcmi_restart_capture(dcmi))
+ dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__);
}
spin_unlock_irq(&dcmi->irqlock);
@@ -542,10 +488,6 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
return -EIO;
}
- /* Set completion callback routine for notification */
- buf->dma_desc->callback = dcmi_dma_callback;
- buf->dma_desc->callback_param = dcmi;
-
/* Mark the descriptor as reusable to avoid having to prepare it */
ret = dmaengine_desc_set_reuse(buf->dma_desc);
if (ret) {
@@ -818,10 +760,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
}
/* Enable interruptions */
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
- reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
- else
- reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR);
+ reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
spin_unlock_irq(&dcmi->irqlock);
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 07/12] media: stm32: dcmi: use dmaengine_terminate_async in irq context
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (5 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 06/12] media: stm32: dcmi: perform all dma handling within irq_thread Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 08/12] media: stm32: dcmi: continuous mode capture in JPEG Alain Volmat
` (6 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Whenever receiving an OVERRUN event or an end of frame, the driver stops
currently ongoing DMA transfer since the DCMI stops sending data to dma.
Not doing this would lead to having DMA & DCMI no more synchronized in term
of expected data to be copied.
Since this is done in irq handler context, it is not possible to make any
call that would lead to scheduling hence dmaengine_terminate_sync are not
possible.
Since the dcmi driver is NOT using dma callbacks, it is possible thus to
call instead dmaengine_terminate_async (aka without synchronize) and call
again right after a new dmaengine_submit to setup again a new transfer.
And since this is now a dmaengine_submit_async, there is no need to release
the spinlock around calls to the dmaengine_submit_async.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 66bb79038c54..6ca0ffcd97a3 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -334,10 +334,8 @@ static void dcmi_process_frame(struct stm32_dcmi *dcmi)
dcmi_buffer_done(dcmi, buf, 0, -EIO);
}
- spin_unlock_irq(&dcmi->irqlock);
/* Abort DMA operation */
- dmaengine_terminate_sync(dcmi->dma_chan);
- spin_lock_irq(&dcmi->irqlock);
+ dmaengine_terminate_async(dcmi->dma_chan);
}
static irqreturn_t dcmi_irq_thread(int irq, void *arg)
@@ -355,10 +353,8 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
if (dcmi->overrun_count > OVERRUN_ERROR_THRESHOLD)
dcmi->errors_count++;
- spin_unlock_irq(&dcmi->irqlock);
- dmaengine_terminate_sync(dcmi->dma_chan);
+ dmaengine_terminate_async(dcmi->dma_chan);
- spin_lock_irq(&dcmi->irqlock);
if (dcmi_restart_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__);
spin_unlock_irq(&dcmi->irqlock);
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 08/12] media: stm32: dcmi: continuous mode capture in JPEG
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (6 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 07/12] media: stm32: dcmi: use dmaengine_terminate_async in irq context Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 09/12] dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties Alain Volmat
` (5 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Overall cleanup done allows to now have the JPEG handling
done in the same way as other formats in continuous mode,
allowing to achieve a faster framerate in all resolutions.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 6ca0ffcd97a3..97683daf179d 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -717,10 +717,6 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
if (dcmi->do_crop)
dcmi_set_crop(dcmi);
- /* Enable jpeg capture */
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
- reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */
-
/* Enable dcmi */
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 09/12] dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (7 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 08/12] media: stm32: dcmi: continuous mode capture in JPEG Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-14 21:52 ` Rob Herring (Arm)
2026-01-06 11:34 ` [PATCH v2 10/12] media: stm32: dcmi: addition of DMA-MDMA chaining support Alain Volmat
` (4 subsequent siblings)
13 siblings, 1 reply; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Add properties update and new sram property necessary in order
to enable the DMA-MDMA chaining.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
Change in v2:
- correct typo (phandles -> phandle)
- remove explanation about generic binding sram.yaml
---
Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml
index 34147127192f..979360e9099e 100644
--- a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml
+++ b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml
@@ -27,11 +27,14 @@ properties:
- const: mclk
dmas:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
dma-names:
items:
- const: tx
+ - const: mdma_tx
+ minItems: 1
resets:
maxItems: 1
@@ -40,6 +43,12 @@ properties:
minItems: 1
maxItems: 2
+ sram:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ phandle to a reserved SRAM region which is used as temporary
+ storage memory between DMA and MDMA engines.
+
port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 10/12] media: stm32: dcmi: addition of DMA-MDMA chaining support
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (8 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 09/12] dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 11/12] ARM: dts: stm32: add sram node within stm32mp151.dtsi Alain Volmat
` (3 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Add possibility to rely on an additional MDMA channel and chain
the DMA and MDMA channels allowing to achieve faster capture.
Indeed, on the MP15 platform, the MDMA engine has an higher
bandwidth to the DDR than the DMA engine. Relying on that
it is possible to capture frames from the DCMI IP into the DDR
by using two channels as follow:
DCMI -> (DMA) -> SRAM -> (MDMA) -> DDR
The DMA is able by himself to trigger a MDMA request hence, once
properly configured, the DCMI IP can simply trigger the DMA in order
to have the data pushed up to the DDR (via the SRAM and a MDMA
channel).
This behavior is detailed in the document
Documentation/arch/arm/stm32/stm32-dma-mdma-chaining.rst
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmi.c | 250 +++++++++++++++++++++++----
1 file changed, 220 insertions(+), 30 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 97683daf179d..e5663fbe6422 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -15,6 +15,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
+#include <linux/genalloc.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -113,7 +114,9 @@ struct dcmi_buf {
struct vb2_v4l2_buffer vb;
bool prepared;
struct sg_table sgt;
+ struct sg_table sgt_mdma;
struct dma_async_tx_descriptor *dma_desc;
+ struct dma_async_tx_descriptor *mdma_desc;
size_t size;
struct list_head list;
};
@@ -159,6 +162,15 @@ struct stm32_dcmi {
struct dma_chan *dma_chan;
dma_cookie_t dma_cookie;
u32 dma_max_burst;
+
+ /* Elements for the MDMA - DMA chaining */
+ struct gen_pool *sram_pool;
+ struct dma_chan *mdma_chan;
+ void *sram_buf;
+ u32 sram_buf_size;
+ dma_addr_t sram_dma_buf;
+ dma_cookie_t mdma_cookie;
+
u32 misr;
int errors_count;
int overrun_count;
@@ -247,12 +259,22 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
struct dcmi_buf *buf)
{
/* Push current DMA transaction in the pending queue */
+ if (dcmi->mdma_chan) {
+ dcmi->mdma_cookie = dmaengine_submit(buf->mdma_desc);
+ if (dma_submit_error(dcmi->mdma_cookie)) {
+ dev_err(dcmi->dev, "%s: MDMA submission failed\n", __func__);
+ return -ENXIO;
+ }
+ }
+
dcmi->dma_cookie = dmaengine_submit(buf->dma_desc);
if (dma_submit_error(dcmi->dma_cookie)) {
dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__);
return -ENXIO;
}
+ if (dcmi->mdma_chan)
+ dma_async_issue_pending(dcmi->mdma_chan);
dma_async_issue_pending(dcmi->dma_chan);
return 0;
@@ -301,7 +323,9 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi)
static void dcmi_process_frame(struct stm32_dcmi *dcmi)
{
- struct dma_tx_state state;
+ struct dma_tx_state state, state_dma;
+ size_t bytes_used;
+
enum dma_status status;
struct dcmi_buf *buf = dcmi->active;
@@ -309,23 +333,36 @@ static void dcmi_process_frame(struct stm32_dcmi *dcmi)
return;
/*
- * At the time FRAME interrupt is received, all dma req have been sent to the DMA,
- * however DMA might still be transferring data hence first synchronize prior to
- * getting the status of the DMA transfer.
- * Then DMA tx status gives the amount of data transferred to memory, which is then
- * returned to V4L2 through the active buffer payload.
+ * Pause the DMA transfer, leading to trigger of the MDMA channel read while
+ * keeping a valid residue value on the dma channel
*/
+ if (dcmi->mdma_chan) {
+ spin_unlock_irq(&dcmi->irqlock);
+ dmaengine_pause(dcmi->dma_chan);
+ spin_lock_irq(&dcmi->irqlock);
+
+ do {
+ status = dmaengine_tx_status(dcmi->mdma_chan, dcmi->mdma_cookie, &state);
+ cpu_relax();
+ } while (status != DMA_ERROR && status != DMA_COMPLETE &&
+ state.residue && state.in_flight_bytes);
+ } else {
+ status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
+ }
- spin_unlock_irq(&dcmi->irqlock);
- /* Drain DMA */
- dmaengine_synchronize(dcmi->dma_chan);
- spin_lock_irq(&dcmi->irqlock);
-
- /* Get DMA status and residue size */
- status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state);
if (status != DMA_ERROR && state.residue < buf->size) {
+ bytes_used = buf->size - state.residue;
+
+ if (state.residue && dcmi->mdma_chan) {
+ dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state_dma);
+ /* Getting full size residue equal to no residue */
+ if (state_dma.residue == dcmi->sram_buf_size)
+ state_dma.residue = 0;
+ bytes_used -= state_dma.residue;
+ }
+
/* Return buffer to V4L2 with received data size */
- dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0);
+ dcmi_buffer_done(dcmi, buf, bytes_used, 0);
} else {
dcmi->errors_count++;
dev_err(dcmi->dev, "%s: DMA error. status: 0x%x, residue: %d\n",
@@ -336,6 +373,8 @@ static void dcmi_process_frame(struct stm32_dcmi *dcmi)
/* Abort DMA operation */
dmaengine_terminate_async(dcmi->dma_chan);
+ if (dcmi->mdma_chan)
+ dmaengine_terminate_async(dcmi->mdma_chan);
}
static irqreturn_t dcmi_irq_thread(int irq, void *arg)
@@ -354,13 +393,15 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
dcmi->errors_count++;
dmaengine_terminate_async(dcmi->dma_chan);
+ if (dcmi->mdma_chan)
+ dmaengine_terminate_async(dcmi->mdma_chan);
if (dcmi_restart_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture\n", __func__);
spin_unlock_irq(&dcmi->irqlock);
-
return IRQ_HANDLED;
}
+
if (dcmi->misr & IT_ERR)
dcmi->errors_count++;
@@ -447,28 +488,70 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
vb2_set_plane_payload(vb, 0, size);
if (!buf->prepared) {
+ u32 max_size = dcmi->dma_max_burst;
+ unsigned int dma_nents;
+
/* Get memory addresses */
buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
- if (buf->size > dcmi->dma_max_burst)
- num_sgs = DIV_ROUND_UP(buf->size, dcmi->dma_max_burst);
+ if (dcmi->mdma_chan)
+ max_size = dcmi->sram_buf_size / 2;
- ret = sg_alloc_table(&buf->sgt, num_sgs, GFP_ATOMIC);
+ if (buf->size > max_size)
+ num_sgs = DIV_ROUND_UP(buf->size, max_size);
+
+ /*
+ * If we use MDMA chaining, DMA is used in cyclic mode and the scatterlist
+ * maximum size is thus 2
+ */
+ dma_nents = num_sgs;
+ if (dcmi->mdma_chan)
+ dma_nents = min_t(unsigned int, num_sgs, 2);
+
+ ret = sg_alloc_table(&buf->sgt, dma_nents, GFP_ATOMIC);
if (ret) {
- dev_err(dcmi->dev, "sg table alloc failed\n");
+ dev_err(dcmi->dev, "sg table alloc failed for DMA\n");
return ret;
}
+ if (dcmi->mdma_chan) {
+ ret = sg_alloc_table(&buf->sgt_mdma, num_sgs, GFP_ATOMIC);
+ if (ret) {
+ dev_err(dcmi->dev, "sg table alloc failed for MDMA\n");
+ return ret;
+ }
+ }
+
dma_buf = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n",
vb->index, &dma_buf, buf->size);
- for_each_sg(buf->sgt.sgl, sg, num_sgs, i) {
- size_t bytes = min_t(size_t, size, dcmi->dma_max_burst);
+ for_each_sg(buf->sgt.sgl, sg, dma_nents, i) {
+ size_t bytes = min_t(size_t, size, max_size);
+
+ if (!dcmi->mdma_chan) {
+ sg_dma_address(sg) = dma_buf;
+ dma_buf += bytes;
+ } else {
+ /* Targets the beginning = first half of the sram_buf */
+ sg_dma_address(sg) = dcmi->sram_dma_buf;
+ /*
+ * Targets the second half of the sram_bubf
+ * for odd indexes of the item of the sg_list
+ */
+ if (i & 1)
+ sg->dma_address += (dcmi->sram_buf_size / 2);
+ }
+ /*
+ * In case of DMA-MDMA chaining, since the DMA is working in cyclic mode,
+ * we need to provide two linked-list node of same size in order to have
+ * a correct residue value computed.
+ */
+ if (!dcmi->mdma_chan)
+ sg_dma_len(sg) = bytes;
+ else
+ sg_dma_len(sg) = dcmi->sram_buf_size / 2;
- sg_dma_address(sg) = dma_buf;
- sg_dma_len(sg) = bytes;
- dma_buf += bytes;
size -= bytes;
}
@@ -480,6 +563,8 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
DMA_PREP_INTERRUPT);
if (!buf->dma_desc) {
dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__);
+ if (dcmi->mdma_chan)
+ sg_free_table(&buf->sgt_mdma);
sg_free_table(&buf->sgt);
return -EIO;
}
@@ -489,10 +574,48 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
if (ret) {
dev_err(dcmi->dev, "%s: DMA dmaengine_desc_set_reuse failed\n", __func__);
dmaengine_desc_free(buf->dma_desc);
+ if (dcmi->mdma_chan)
+ sg_free_table(&buf->sgt_mdma);
sg_free_table(&buf->sgt);
return -EIO;
}
+ if (dcmi->mdma_chan) {
+ size = dcmi->fmt.fmt.pix.sizeimage;
+ for_each_sg(buf->sgt_mdma.sgl, sg, num_sgs, i) {
+ size_t bytes = min_t(size_t, size, max_size);
+
+ sg_dma_address(sg) = dma_buf;
+ sg_dma_len(sg) = bytes;
+ dma_buf += bytes;
+ size -= bytes;
+ }
+
+ buf->mdma_desc = dmaengine_prep_slave_sg(dcmi->mdma_chan, buf->sgt_mdma.sgl,
+ buf->sgt_mdma.nents,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!buf->mdma_desc) {
+ dev_err(dcmi->dev, "%s: failed dmaengine_prep_slave_sg for MDMA\n",
+ __func__);
+ dmaengine_desc_free(buf->dma_desc);
+ sg_free_table(&buf->sgt_mdma);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+
+ ret = dmaengine_desc_set_reuse(buf->mdma_desc);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: failed to set reuse for mdma desc\n",
+ __func__);
+ dmaengine_desc_free(buf->mdma_desc);
+ dmaengine_desc_free(buf->dma_desc);
+ sg_free_table(&buf->sgt_mdma);
+ sg_free_table(&buf->sgt);
+ return -EIO;
+ }
+ }
+
buf->prepared = true;
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
@@ -511,6 +634,14 @@ static void dcmi_buf_cleanup(struct vb2_buffer *vb)
if (!buf->prepared)
return;
+ if (dcmi->mdma_chan) {
+ ret = dmaengine_desc_free(buf->mdma_desc);
+ if (ret)
+ dev_err(dcmi->dev, "%s: Failed to free the mdma descriptor (0x%x)\n",
+ __func__, ret);
+ sg_free_table(&buf->sgt_mdma);
+ }
+
ret = dmaengine_desc_free(buf->dma_desc);
if (ret)
dev_err(dcmi->dev, "%s: Failed to free the mdma descriptor (0x%x)\n",
@@ -816,6 +947,8 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
/* Stop all pending DMA operations */
dmaengine_terminate_sync(dcmi->dma_chan);
+ if (dcmi->mdma_chan)
+ dmaengine_terminate_sync(dcmi->mdma_chan);
pm_runtime_put(dcmi->dev);
@@ -1824,9 +1957,9 @@ static int dcmi_probe(struct platform_device *pdev)
struct v4l2_fwnode_endpoint ep = { .bus_type = 0 };
struct stm32_dcmi *dcmi;
struct vb2_queue *q;
- struct dma_chan *chan;
+ struct dma_chan *chan, *mdma_chan;
struct dma_slave_caps caps;
- struct dma_slave_config dma_config;
+ struct dma_slave_config dma_config, mdma_config;
struct clk *mclk;
int ret = 0;
@@ -1888,15 +2021,21 @@ static int dcmi_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(chan),
"Failed to request DMA channel\n");
- dcmi->dma_max_burst = UINT_MAX;
- ret = dma_get_slave_caps(chan, &caps);
- if (!ret && caps.max_sg_burst)
- dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES;
+ mdma_chan = dma_request_chan(&pdev->dev, "mdma_tx");
+ if (IS_ERR(mdma_chan)) {
+ ret = PTR_ERR(mdma_chan);
+ if (ret != -ENODEV)
+ return dev_err_probe(&pdev->dev, ret, "Failed to request MDMA channel\n");
+ mdma_chan = NULL;
+ }
+ /* Configure the DMA channel */
memset(&dma_config, 0, sizeof(dma_config));
dma_config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR;
dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ if (mdma_chan)
+ dma_config.peripheral_size = 1; /* Indicates chaining */
/* Configure DMA channel */
ret = dmaengine_slave_config(chan, &dma_config);
@@ -1906,6 +2045,47 @@ static int dcmi_probe(struct platform_device *pdev)
goto err_dma_slave_config;
}
+ /* If we want MDMA, we also need a sram pool */
+ if (mdma_chan) {
+ dcmi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ if (!dcmi->sram_pool) {
+ dev_info(&pdev->dev, "No SRAM pool, can't use MDMA chaining\n");
+ goto err_dma_slave_config;
+ }
+
+ dev_info(&pdev->dev, "SRAM pool: %zu KiB for DMA/MDMA chaining\n",
+ gen_pool_size(dcmi->sram_pool) / 1024);
+
+ dcmi->sram_buf_size = gen_pool_size(dcmi->sram_pool);
+ dcmi->sram_buf = gen_pool_dma_zalloc(dcmi->sram_pool, dcmi->sram_buf_size,
+ &dcmi->sram_dma_buf);
+ if (!dcmi->sram_buf) {
+ dev_err(dcmi->dev, "Failed to allocate from SRAM\n");
+ goto err_dma_slave_config;
+ }
+
+ /* Configure the MDMA Channel */
+ memset(&mdma_config, 0, sizeof(mdma_config));
+ mdma_config.direction = DMA_DEV_TO_MEM;
+ mdma_config.src_addr = dcmi->sram_dma_buf;
+ mdma_config.peripheral_size = dma_config.peripheral_size;
+ mdma_config.peripheral_config = dma_config.peripheral_config;
+ ret = dmaengine_slave_config(mdma_chan, &mdma_config);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: MDMA channel config failed (%d)\n",
+ __func__, ret);
+ goto err_mdma_slave_config;
+ }
+ }
+
+ dcmi->dma_max_burst = UINT_MAX;
+ /* In case of using DMA-MDMA chaining we consider the maximum infini */
+ if (!mdma_chan) {
+ ret = dma_get_slave_caps(chan, &caps);
+ if (!ret && caps.max_sg_burst)
+ dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
+
spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
init_completion(&dcmi->complete);
@@ -1915,6 +2095,7 @@ static int dcmi_probe(struct platform_device *pdev)
dcmi->mclk = mclk;
dcmi->state = STOPPED;
dcmi->dma_chan = chan;
+ dcmi->mdma_chan = mdma_chan;
q = &dcmi->queue;
@@ -2023,8 +2204,13 @@ static int dcmi_probe(struct platform_device *pdev)
v4l2_device_unregister(&dcmi->v4l2_dev);
err_media_device_cleanup:
media_device_cleanup(&dcmi->mdev);
+err_mdma_slave_config:
+ if (dcmi->mdma_chan)
+ gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size);
err_dma_slave_config:
dma_release_channel(dcmi->dma_chan);
+ if (dcmi->mdma_chan)
+ dma_release_channel(mdma_chan);
return ret;
}
@@ -2042,6 +2228,10 @@ static void dcmi_remove(struct platform_device *pdev)
media_device_cleanup(&dcmi->mdev);
dma_release_channel(dcmi->dma_chan);
+ if (dcmi->mdma_chan) {
+ gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size);
+ dma_release_channel(dcmi->mdma_chan);
+ }
}
static int dcmi_runtime_suspend(struct device *dev)
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 11/12] ARM: dts: stm32: add sram node within stm32mp151.dtsi
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (9 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 10/12] media: stm32: dcmi: addition of DMA-MDMA chaining support Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-01-06 11:34 ` [PATCH v2 12/12] ARM: dts: stm32: enable DCMI DMA-MDMA chaining on stm32mp157c-ev1.dts Alain Volmat
` (2 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Introduce the sram node in order to be used by drivers
requiring SRAM memory space.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
arch/arm/boot/dts/st/stm32mp151.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/st/stm32mp151.dtsi b/arch/arm/boot/dts/st/stm32mp151.dtsi
index b1b568dfd126..85cb0f16ca73 100644
--- a/arch/arm/boot/dts/st/stm32mp151.dtsi
+++ b/arch/arm/boot/dts/st/stm32mp151.dtsi
@@ -123,6 +123,14 @@ soc {
interrupt-parent = <&intc>;
ranges;
+ sram4: sram@10050000 {
+ compatible = "mmio-sram";
+ reg = <0x10050000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x10050000 0x10000>;
+ };
+
ipcc: mailbox@4c001000 {
compatible = "st,stm32mp1-ipcc";
#mbox-cells = <1>;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 12/12] ARM: dts: stm32: enable DCMI DMA-MDMA chaining on stm32mp157c-ev1.dts
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (10 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 11/12] ARM: dts: stm32: add sram node within stm32mp151.dtsi Alain Volmat
@ 2026-01-06 11:34 ` Alain Volmat
2026-03-05 17:31 ` [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Sakari Ailus
2026-03-06 9:42 ` Alexandre TORGUE
13 siblings, 0 replies; 16+ messages in thread
From: Alain Volmat @ 2026-01-06 11:34 UTC (permalink / raw)
To: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig, Alain Volmat
Enable the DMA-MDMA chaining for the dcmi (camera capture)
in order to be able to achieve higher resolution.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
arch/arm/boot/dts/st/stm32mp157c-ev1.dts | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm/boot/dts/st/stm32mp157c-ev1.dts b/arch/arm/boot/dts/st/stm32mp157c-ev1.dts
index 8f99c30f1af1..68b536b0c6ee 100644
--- a/arch/arm/boot/dts/st/stm32mp157c-ev1.dts
+++ b/arch/arm/boot/dts/st/stm32mp157c-ev1.dts
@@ -86,6 +86,14 @@ &dcmi {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&dcmi_pins_a>;
pinctrl-1 = <&dcmi_sleep_pins_a>;
+ /*
+ * Enable DMA-MDMA chaining by adding a SRAM pool and
+ * a MDMA channel
+ */
+ sram = <&dcmi_pool>;
+
+ dmas = <&dmamux1 75 0x400 0x01>, <&mdma1 0 0x3 0x1200000a 0 0>;
+ dma-names = "tx", "mdma_tx";
port {
dcmi_0: endpoint {
@@ -301,6 +309,13 @@ &spi1 {
status = "disabled";
};
+&sram4 {
+ dcmi_pool: dcmi-sram@0 {
+ reg = <0x0 0x8000>;
+ pool;
+ };
+};
+
&timers2 {
/* spare dmas for other usage (un-delete to enable pwm capture) */
/delete-property/dmas;
--
2.34.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v2 09/12] dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties
2026-01-06 11:34 ` [PATCH v2 09/12] dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties Alain Volmat
@ 2026-01-14 21:52 ` Rob Herring (Arm)
0 siblings, 0 replies; 16+ messages in thread
From: Rob Herring (Arm) @ 2026-01-14 21:52 UTC (permalink / raw)
To: Alain Volmat
Cc: dri-devel, linux-media, Maxime Coquelin, devicetree,
Christian König, Sumit Semwal, linaro-mm-sig,
Krzysztof Kozlowski, Hugues Fruchet, linux-arm-kernel,
linux-stm32, linux-kernel, Alexandre Torgue, Conor Dooley,
Mauro Carvalho Chehab
On Tue, 06 Jan 2026 12:34:37 +0100, Alain Volmat wrote:
> Add properties update and new sram property necessary in order
> to enable the DMA-MDMA chaining.
>
> Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
>
> ---
> Change in v2:
> - correct typo (phandles -> phandle)
> - remove explanation about generic binding sram.yaml
> ---
> Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (11 preceding siblings ...)
2026-01-06 11:34 ` [PATCH v2 12/12] ARM: dts: stm32: enable DCMI DMA-MDMA chaining on stm32mp157c-ev1.dts Alain Volmat
@ 2026-03-05 17:31 ` Sakari Ailus
2026-03-06 9:42 ` Alexandre TORGUE
13 siblings, 0 replies; 16+ messages in thread
From: Sakari Ailus @ 2026-03-05 17:31 UTC (permalink / raw)
To: Alain Volmat
Cc: Hugues Fruchet, Mauro Carvalho Chehab, Maxime Coquelin,
Alexandre Torgue, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König, linux-media, linux-stm32,
linux-arm-kernel, linux-kernel, devicetree, dri-devel,
linaro-mm-sig
Hi Alain,
On Tue, Jan 06, 2026 at 12:34:28PM +0100, Alain Volmat wrote:
> This series improve stability of the capture by fixing the
> handling of the overrun which was leading to captured
> frame corruption.
> Locking within the driver is also simplified and the way
> DMA is handled is reworked allowing to avoid having a
> specific handling for the JPEG data.
>
> Performances of capture can now be increased via the usage
> of a DMA->MDMA chaining which allows for capture of higher
> resolution / framerate.
>
> Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
I've picked the 10 first patches to my tree, I presume the rest are merged
via another tree?
Please cc me on the next time. Thanks.
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
` (12 preceding siblings ...)
2026-03-05 17:31 ` [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Sakari Ailus
@ 2026-03-06 9:42 ` Alexandre TORGUE
13 siblings, 0 replies; 16+ messages in thread
From: Alexandre TORGUE @ 2026-03-06 9:42 UTC (permalink / raw)
To: Alain Volmat, Hugues Fruchet, Mauro Carvalho Chehab,
Maxime Coquelin, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sumit Semwal, Christian König
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
devicetree, dri-devel, linaro-mm-sig
Hi
On 1/6/26 12:34, Alain Volmat wrote:
> This series improve stability of the capture by fixing the
> handling of the overrun which was leading to captured
> frame corruption.
> Locking within the driver is also simplified and the way
> DMA is handled is reworked allowing to avoid having a
> specific handling for the JPEG data.
>
> Performances of capture can now be increased via the usage
> of a DMA->MDMA chaining which allows for capture of higher
> resolution / framerate.
>
> Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
> ---
> Changes in v2:
> - Fix pm_sleep_ptr -> pm_ptr to avoid unused function warning
> - Fix typo / remove useless comment in binding
> - Link to v1: https://lore.kernel.org/r/20251218-stm32-dcmi-dma-chaining-v1-0-39948ca6cbf6@foss.st.com
>
> ---
> Alain Volmat (12):
> media: stm32: dcmi: Switch from __maybe_unused to pm_ptr()
> media: stm32: dcmi: perform dmaengine_slave_config at probe
> media: stm32: dcmi: only create dma descriptor once at buf_prepare
> media: stm32: dcmi: stop the dma transfer on overrun
> media: stm32: dcmi: rework spin_lock calls
> media: stm32: dcmi: perform all dma handling within irq_thread
> media: stm32: dcmi: use dmaengine_terminate_async in irq context
> media: stm32: dcmi: continuous mode capture in JPEG
> dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties
> media: stm32: dcmi: addition of DMA-MDMA chaining support
> ARM: dts: stm32: add sram node within stm32mp151.dtsi
> ARM: dts: stm32: enable DCMI DMA-MDMA chaining on stm32mp157c-ev1.dts
>
> .../devicetree/bindings/media/st,stm32-dcmi.yaml | 11 +-
> arch/arm/boot/dts/st/stm32mp151.dtsi | 8 +
> arch/arm/boot/dts/st/stm32mp157c-ev1.dts | 15 +
> drivers/media/platform/st/stm32/stm32-dcmi.c | 475 +++++++++++++--------
> 4 files changed, 341 insertions(+), 168 deletions(-)
> ---
> base-commit: f7231cff1f3ff8259bef02dc4999bc132abf29cf
> change-id: 20251213-stm32-dcmi-dma-chaining-9ea1da83007d
>
> Best regards,
DT patches applied stm32-next branch.
regards
Alex
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-03-06 9:42 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-06 11:34 [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Alain Volmat
2026-01-06 11:34 ` [PATCH v2 01/12] media: stm32: dcmi: Switch from __maybe_unused to pm_ptr() Alain Volmat
2026-01-06 11:34 ` [PATCH v2 02/12] media: stm32: dcmi: perform dmaengine_slave_config at probe Alain Volmat
2026-01-06 11:34 ` [PATCH v2 03/12] media: stm32: dcmi: only create dma descriptor once at buf_prepare Alain Volmat
2026-01-06 11:34 ` [PATCH v2 04/12] media: stm32: dcmi: stop the dma transfer on overrun Alain Volmat
2026-01-06 11:34 ` [PATCH v2 05/12] media: stm32: dcmi: rework spin_lock calls Alain Volmat
2026-01-06 11:34 ` [PATCH v2 06/12] media: stm32: dcmi: perform all dma handling within irq_thread Alain Volmat
2026-01-06 11:34 ` [PATCH v2 07/12] media: stm32: dcmi: use dmaengine_terminate_async in irq context Alain Volmat
2026-01-06 11:34 ` [PATCH v2 08/12] media: stm32: dcmi: continuous mode capture in JPEG Alain Volmat
2026-01-06 11:34 ` [PATCH v2 09/12] dt-bindings: media: st: dcmi: add DMA-MDMA chaining properties Alain Volmat
2026-01-14 21:52 ` Rob Herring (Arm)
2026-01-06 11:34 ` [PATCH v2 10/12] media: stm32: dcmi: addition of DMA-MDMA chaining support Alain Volmat
2026-01-06 11:34 ` [PATCH v2 11/12] ARM: dts: stm32: add sram node within stm32mp151.dtsi Alain Volmat
2026-01-06 11:34 ` [PATCH v2 12/12] ARM: dts: stm32: enable DCMI DMA-MDMA chaining on stm32mp157c-ev1.dts Alain Volmat
2026-03-05 17:31 ` [PATCH v2 00/12] media: stm32: dcmi: stability & performance enhancements Sakari Ailus
2026-03-06 9:42 ` Alexandre TORGUE
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox