* [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes
@ 2026-06-02 13:13 Alain Volmat
2026-06-02 13:13 ` [PATCH v2 01/13] media: stm32: dcmipp: share struct dcmipp_device among subdevs Alain Volmat
` (12 more replies)
0 siblings, 13 replies; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:13 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
This series add support for the main & aux pixel pipes
available on the MP2x. In addition to the byte pipe
already available, the DCMIPP is able to process two
other streams in parallel, including ISP operations
such as demosaicing, exposure etc, post-processing operations
such as cropping, downscaling and various pixel formats
output.
This series introduces the whole DCMIPP pipelines including
static configuration of the ISP part (such as demosaicing).
Dynamic control of the ISP as well as statistic extraction
part will be added on top of this serie via usage of 2 other
video devices (input & output) for statistics extraction and
dynamic ISP parameter injection.
To: Hugues Fruchet <hugues.fruchet@foss.st.com>
To: Mauro Carvalho Chehab <mchehab@kernel.org>
To: Maxime Coquelin <mcoquelin.stm32@gmail.com>
To: Alexandre Torgue <alexandre.torgue@foss.st.com>
Cc: linux-media@vger.kernel.org
Cc: linux-stm32@st-md-mailman.stormreply.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
Changes in v2:
- Rebased on top of kernel v7.1-rc6
- check-patch minor fixes
- replaced kzalloc(.., sizeof(..),) with kzalloc_obj
- Link to v1: https://lore.kernel.org/r/20260202-stm32-dcmipp-pixel-pipes-support-v1-0-8d8d51f5e014@foss.st.com
---
Alain Volmat (13):
media: stm32: dcmipp: share struct dcmipp_device among subdevs
media: stm32: dcmipp: make dcmipp_state & cmsr2 read common
media: stm32: dcmipp: bytecap: protect CMIER register access
media: stm32: dcmipp: move common structures in dcmipp-common.h
media: stm32: dcmipp: correct swap in YUYV data with parallel input
media: stm32: dcmipp: configure csi input of all pipes on stm32mp25
media: stm32: dcmipp: introduce a dcmipp global media_pipeline
media: stm32: dcmipp: add pixel pipes helper functions
media: stm32: dcmipp: addition of a dcmipp-isp subdev
media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev
media: stm32: dcmipp: add pixel-pipe support in bytecap
media: stm32: dcmipp: rename bytecap into capture
media: stm32: dcmipp: instantiate & link stm32mp25 subdevs
.../media/platform/st/stm32/stm32-dcmipp/Makefile | 3 +-
.../st/stm32/stm32-dcmipp/dcmipp-byteproc.c | 27 +-
.../{dcmipp-bytecap.c => dcmipp-capture.c} | 590 +++++++++----
.../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 98 ++-
.../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 124 ++-
.../platform/st/stm32/stm32-dcmipp/dcmipp-input.c | 113 ++-
.../platform/st/stm32/stm32-dcmipp/dcmipp-isp.c | 482 +++++++++++
.../st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c | 180 ++++
.../st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h | 41 +
.../st/stm32/stm32-dcmipp/dcmipp-pixelproc.c | 935 +++++++++++++++++++++
10 files changed, 2318 insertions(+), 275 deletions(-)
---
base-commit: 6f3ed7fec72fc8979b2a8c7219c0a9fcfc8d07b5
change-id: 20251219-stm32-dcmipp-pixel-pipes-support-b42924c0b4e5
Best regards,
--
Alain Volmat <alain.volmat@foss.st.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 01/13] media: stm32: dcmipp: share struct dcmipp_device among subdevs
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
@ 2026-06-02 13:13 ` Alain Volmat
2026-06-02 13:13 ` [PATCH v2 02/13] media: stm32: dcmipp: make dcmipp_state & cmsr2 read common Alain Volmat
` (11 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:13 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
In preparation of need for sharing of data between subdevices,
make the struct dcmipp_device structure part of dcmipp_common.h
and share it with subdevs at init time. This allows for simplifying
parameters of each subdev init function as well.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 13 ++++---
.../st/stm32/stm32-dcmipp/dcmipp-byteproc.c | 11 +++---
.../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 43 +++++++++++++++++-----
.../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 33 ++---------------
.../platform/st/stm32/stm32-dcmipp/dcmipp-input.c | 12 +++---
5 files changed, 55 insertions(+), 57 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index a42f43d19f9e..395c0ea72019 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -855,12 +855,11 @@ static const struct media_entity_operations dcmipp_bytecap_entity_ops = {
.link_validate = dcmipp_bytecap_link_validate,
};
-struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs)
+struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
{
struct dcmipp_bytecap_device *vcap;
+ struct device *dev = dcmipp->dev;
struct video_device *vdev;
struct vb2_queue *q;
const unsigned long pad_flag = MEDIA_PAD_FL_SINK;
@@ -878,6 +877,8 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
goto err_free_vcap;
}
+ vcap->ved.dcmipp = dcmipp;
+
/* Initialize the media entity */
vcap->vdev.entity.name = entity_name;
vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
@@ -928,7 +929,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
vcap->ved.handler = dcmipp_bytecap_irq_callback;
vcap->ved.thread_fn = dcmipp_bytecap_irq_thread;
vcap->dev = dev;
- vcap->regs = regs;
+ vcap->regs = dcmipp->regs;
/* Initialize the video_device struct */
vdev = &vcap->vdev;
@@ -939,7 +940,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
vdev->ioctl_ops = &dcmipp_bytecap_ioctl_ops;
vdev->lock = &vcap->lock;
vdev->queue = q;
- vdev->v4l2_dev = v4l2_dev;
+ vdev->v4l2_dev = &dcmipp->v4l2_dev;
strscpy(vdev->name, entity_name, sizeof(vdev->name));
video_set_drvdata(vdev, &vcap->ved);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
index 0c7aeb0888f6..5fcd5123136f 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
@@ -571,8 +571,8 @@ void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved)
}
struct dcmipp_ent_device *
-dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
- struct v4l2_device *v4l2_dev, void __iomem *regs)
+dcmipp_byteproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
{
struct dcmipp_byteproc_device *byteproc;
const unsigned long pads_flag[] = {
@@ -585,11 +585,11 @@ dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
if (!byteproc)
return ERR_PTR(-ENOMEM);
- byteproc->regs = regs;
+ byteproc->regs = dcmipp->regs;
/* Initialize ved and sd */
ret = dcmipp_ent_sd_register(&byteproc->ved, &byteproc->sd,
- v4l2_dev, entity_name,
+ &dcmipp->v4l2_dev, entity_name,
MEDIA_ENT_F_PROC_VIDEO_SCALER,
ARRAY_SIZE(pads_flag), pads_flag,
&dcmipp_byteproc_int_ops,
@@ -600,7 +600,8 @@ dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
return ERR_PTR(ret);
}
- byteproc->dev = dev;
+ byteproc->ved.dcmipp = dcmipp;
+ byteproc->dev = dcmipp->dev;
return &byteproc->ved;
}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index fe5f97233f5e..e5c0eda8b18a 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -58,10 +58,36 @@ do { \
(fmt)->xfer_func = DCMIPP_XFER_FUNC_DEFAULT; \
} while (0)
+struct dcmipp_device {
+ /* The platform device */
+ struct platform_device pdev;
+ struct device *dev;
+
+ /* Hardware resources */
+ void __iomem *regs;
+ struct clk *mclk;
+ struct clk *kclk;
+
+ /* The pipeline configuration */
+ const struct dcmipp_pipeline_config *pipe_cfg;
+
+ /* The Associated media_device parent */
+ struct media_device mdev;
+
+ /* Internal v4l2 parent device*/
+ struct v4l2_device v4l2_dev;
+
+ /* Entities */
+ struct dcmipp_ent_device **entity;
+
+ struct v4l2_async_notifier notifier;
+};
+
/**
* struct dcmipp_ent_device - core struct that represents a node in the topology
*
* @ent: the pointer to struct media_entity for the node
+ * @dcmipp: the pointer to the parent dcmipp_device
* @pads: the list of pads of the node
* @bus: struct v4l2_mbus_config_parallel describing input bus
* @bus_type: type of input bus (parallel or BT656)
@@ -84,6 +110,7 @@ do { \
*/
struct dcmipp_ent_device {
struct media_entity *ent;
+ struct dcmipp_device *dcmipp;
struct media_pad *pads;
/* Parallel input device */
@@ -199,19 +226,15 @@ static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg,
}
/* DCMIPP subdev init / release entry points */
-struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs);
+struct dcmipp_ent_device *dcmipp_inp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved);
struct dcmipp_ent_device *
-dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
- struct v4l2_device *v4l2_dev, void __iomem *regs);
+dcmipp_byteproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved);
-struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs);
+struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved);
#endif
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index 49398d077764..dd784cfcaac8 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -33,31 +33,6 @@
.flags = link_flags, \
}
-struct dcmipp_device {
- /* The platform device */
- struct platform_device pdev;
- struct device *dev;
-
- /* Hardware resources */
- void __iomem *regs;
- struct clk *mclk;
- struct clk *kclk;
-
- /* The pipeline configuration */
- const struct dcmipp_pipeline_config *pipe_cfg;
-
- /* The Associated media_device parent */
- struct media_device mdev;
-
- /* Internal v4l2 parent device*/
- struct v4l2_device v4l2_dev;
-
- /* Entities */
- struct dcmipp_ent_device **entity;
-
- struct v4l2_async_notifier notifier;
-};
-
static inline struct dcmipp_device *
notifier_to_dcmipp(struct v4l2_async_notifier *n)
{
@@ -68,8 +43,8 @@ notifier_to_dcmipp(struct v4l2_async_notifier *n)
struct dcmipp_ent_config {
const char *name;
struct dcmipp_ent_device *(*init)
- (struct device *dev, const char *entity_name,
- struct v4l2_device *v4l2_dev, void __iomem *regs);
+ (const char *entity_name,
+ struct dcmipp_device *dcmipp);
void (*release)(struct dcmipp_ent_device *ved);
};
@@ -221,9 +196,7 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp)
dev_dbg(dcmipp->dev, "add subdev %s\n", name);
dcmipp->entity[i] =
- dcmipp->pipe_cfg->ents[i].init(dcmipp->dev, name,
- &dcmipp->v4l2_dev,
- dcmipp->regs);
+ dcmipp->pipe_cfg->ents[i].init(name, dcmipp);
if (IS_ERR(dcmipp->entity[i])) {
dev_err(dcmipp->dev, "failed to init subdev %s\n",
name);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
index 7d3f5857cbfe..564f37e2e1dd 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
@@ -515,15 +515,14 @@ void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved)
dcmipp_ent_sd_unregister(ved, &inp->sd);
}
-struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs)
+struct dcmipp_ent_device *dcmipp_inp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
{
struct dcmipp_inp_device *inp;
const unsigned long pads_flag[] = {
MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
};
+ struct device *dev = dcmipp->dev;
int ret;
/* Allocate the inp struct */
@@ -531,10 +530,10 @@ struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
if (!inp)
return ERR_PTR(-ENOMEM);
- inp->regs = regs;
+ inp->regs = dcmipp->regs;
/* Initialize ved and sd */
- ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, v4l2_dev,
+ ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, &dcmipp->v4l2_dev,
entity_name, MEDIA_ENT_F_VID_IF_BRIDGE,
ARRAY_SIZE(pads_flag), pads_flag,
&dcmipp_inp_int_ops, &dcmipp_inp_ops,
@@ -543,6 +542,7 @@ struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
kfree(inp);
return ERR_PTR(ret);
}
+ inp->ved.dcmipp = dcmipp;
inp->dev = dev;
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 02/13] media: stm32: dcmipp: make dcmipp_state & cmsr2 read common
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
2026-06-02 13:13 ` [PATCH v2 01/13] media: stm32: dcmipp: share struct dcmipp_device among subdevs Alain Volmat
@ 2026-06-02 13:13 ` Alain Volmat
2026-06-02 13:38 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 03/13] media: stm32: dcmipp: bytecap: protect CMIER register access Alain Volmat
` (10 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:13 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
In preparation of the introduction of the pixel pipes capture devices,
move struct dcmipp_state into common header and perform
interrupt status register CMSR2 into the core interrupt handler and
share the value with each subdevs.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 11 ++++-------
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 7 +++++++
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 7 +++++++
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index 395c0ea72019..990bbaaf4bda 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -110,12 +110,6 @@ struct dcmipp_buf {
struct list_head list;
};
-enum dcmipp_state {
- DCMIPP_STOPPED = 0,
- DCMIPP_WAIT_FOR_BUFFER,
- DCMIPP_RUNNING,
-};
-
struct dcmipp_bytecap_device {
struct dcmipp_ent_device ved;
struct video_device vdev;
@@ -797,9 +791,12 @@ static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg)
{
struct dcmipp_bytecap_device *vcap =
container_of(arg, struct dcmipp_bytecap_device, ved);
+ struct dcmipp_ent_device *ved = arg;
/* Store interrupt status register */
- vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & DCMIPP_CMIER_P0ALL;
+ vcap->cmsr2 = ved->cmsr2 & DCMIPP_CMIER_P0ALL;
+ if (!vcap->cmsr2)
+ return IRQ_HANDLED;
vcap->count.it++;
/* Clear interrupt */
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index e5c0eda8b18a..3c3996472e03 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -119,6 +119,13 @@ struct dcmipp_ent_device {
irq_handler_t handler;
irqreturn_t handler_ret;
irq_handler_t thread_fn;
+ u32 cmsr2;
+};
+
+enum dcmipp_state {
+ DCMIPP_STOPPED = 0,
+ DCMIPP_WAIT_FOR_BUFFER,
+ DCMIPP_RUNNING,
};
/**
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index dd784cfcaac8..b06a4931ae95 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -33,6 +33,8 @@
.flags = link_flags, \
}
+#define DCMIPP_CMSR2 0x3f8
+
static inline struct dcmipp_device *
notifier_to_dcmipp(struct v4l2_async_notifier *n)
{
@@ -251,10 +253,15 @@ static irqreturn_t dcmipp_irq_callback(int irq, void *arg)
struct dcmipp_ent_device *ved;
irqreturn_t ret = IRQ_HANDLED;
unsigned int i;
+ u32 cmsr2;
+
+ /* Centralized read of CMSR2 */
+ cmsr2 = reg_read(dcmipp, DCMIPP_CMSR2);
/* Call irq handler of each entities of pipeline */
for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) {
ved = dcmipp->entity[i];
+ ved->cmsr2 = cmsr2;
if (ved->handler)
ved->handler_ret = ved->handler(irq, ved);
else if (ved->thread_fn)
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 03/13] media: stm32: dcmipp: bytecap: protect CMIER register access
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
2026-06-02 13:13 ` [PATCH v2 01/13] media: stm32: dcmipp: share struct dcmipp_device among subdevs Alain Volmat
2026-06-02 13:13 ` [PATCH v2 02/13] media: stm32: dcmipp: make dcmipp_state & cmsr2 read common Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:31 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 04/13] media: stm32: dcmipp: move common structures in dcmipp-common.h Alain Volmat
` (9 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
CMIER register is common between all pipes and thus needs to be
protected from concurrent access. The struct v4l2_device structure,
unique to the whole driver embeds a spin_lock which can also be used
by the driver itself as explained in its description. Rely on this
spin_lock to protect from concurrent access to the CMIER register.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index 990bbaaf4bda..dac7338dd981 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -436,7 +436,9 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
dcmipp_start_capture(vcap, vcap->next);
/* Enable interruptions */
+ spin_lock(&vcap->vdev.v4l2_dev->lock);
reg_set(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
+ spin_unlock(&vcap->vdev.v4l2_dev->lock);
vcap->state = DCMIPP_RUNNING;
@@ -492,7 +494,9 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
media_pipeline_stop(vcap->vdev.entity.pads);
/* Disable interruptions */
+ spin_lock(&vcap->vdev.v4l2_dev->lock);
reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
+ spin_unlock(&vcap->vdev.v4l2_dev->lock);
/* Stop capture */
reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 04/13] media: stm32: dcmipp: move common structures in dcmipp-common.h
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (2 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 03/13] media: stm32: dcmipp: bytecap: protect CMIER register access Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:14 ` [PATCH v2 05/13] media: stm32: dcmipp: correct swap in YUYV data with parallel input Alain Volmat
` (8 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
Move the structure dcmipp_pipeline_config into dcmipp-common.h
so that all subdeves can have access to the information of
capabilities.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 37 ++++++++++++++++++++++
.../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 37 ----------------------
2 files changed, 37 insertions(+), 37 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index 3c3996472e03..9e7b2434200a 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -83,6 +83,43 @@ struct dcmipp_device {
struct v4l2_async_notifier notifier;
};
+#define DCMIPP_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
+ .src_ent = src, \
+ .src_pad = srcpad, \
+ .sink_ent = sink, \
+ .sink_pad = sinkpad, \
+ .flags = link_flags, \
+}
+
+/* Structure which describes individual configuration for each entity */
+struct dcmipp_ent_config {
+ const char *name;
+ struct dcmipp_ent_device *(*init)
+ (const char *entity_name,
+ struct dcmipp_device *dcmipp);
+ void (*release)(struct dcmipp_ent_device *ved);
+};
+
+/* Structure which describes links between entities */
+struct dcmipp_ent_link {
+ unsigned int src_ent;
+ u16 src_pad;
+ unsigned int sink_ent;
+ u16 sink_pad;
+ u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct dcmipp_pipeline_config {
+ const struct dcmipp_ent_config *ents;
+ size_t num_ents;
+ const struct dcmipp_ent_link *links;
+ size_t num_links;
+ u32 hw_revision;
+ bool has_csi2;
+ bool needs_mclk;
+};
+
/**
* struct dcmipp_ent_device - core struct that represents a node in the topology
*
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index b06a4931ae95..3a0a4df410d8 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -25,14 +25,6 @@
#define DCMIPP_MDEV_MODEL_NAME "DCMIPP MDEV"
-#define DCMIPP_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
- .src_ent = src, \
- .src_pad = srcpad, \
- .sink_ent = sink, \
- .sink_pad = sinkpad, \
- .flags = link_flags, \
-}
-
#define DCMIPP_CMSR2 0x3f8
static inline struct dcmipp_device *
@@ -41,35 +33,6 @@ notifier_to_dcmipp(struct v4l2_async_notifier *n)
return container_of(n, struct dcmipp_device, notifier);
}
-/* Structure which describes individual configuration for each entity */
-struct dcmipp_ent_config {
- const char *name;
- struct dcmipp_ent_device *(*init)
- (const char *entity_name,
- struct dcmipp_device *dcmipp);
- void (*release)(struct dcmipp_ent_device *ved);
-};
-
-/* Structure which describes links between entities */
-struct dcmipp_ent_link {
- unsigned int src_ent;
- u16 src_pad;
- unsigned int sink_ent;
- u16 sink_pad;
- u32 flags;
-};
-
-/* Structure which describes the whole topology */
-struct dcmipp_pipeline_config {
- const struct dcmipp_ent_config *ents;
- size_t num_ents;
- const struct dcmipp_ent_link *links;
- size_t num_links;
- u32 hw_revision;
- bool has_csi2;
- bool needs_mclk;
-};
-
/* --------------------------------------------------------------------------
* Topology Configuration
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 05/13] media: stm32: dcmipp: correct swap in YUYV data with parallel input
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (3 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 04/13] media: stm32: dcmipp: move common structures in dcmipp-common.h Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:30 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 06/13] media: stm32: dcmipp: configure csi input of all pipes on stm32mp25 Alain Volmat
` (7 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
When used with parallel input, the DCMIPP is expecting data to come
in YUYV order (for all DUMP/MAIN and AUX pipes). Not doing so will
lead to bad color generated by the pipes when processing is done.
The DUMP pipe is also doing by default a swap since, while it accepts
YUYV data, it will by default generate UYVY data.
Current implementation is not correct for parallel input since it is
performing a cycle swap on the input side and since the dump pipe is
also internally doing a swap, the data captured from the dump pipe are
correct, while the data captured from the main / aux pipes are not.
To correct this, only perform cycle swap when it is necessary, hence
changing from YUYV to UYVY for example, and for all parallel YUV MBUS,
add the P0PPCR based SWAPYUV to put back the data into the ordering of
the input.
Keep previous behavior when the SWAPYUV is not available (such as
stm32mp13).
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../st/stm32/stm32-dcmipp/dcmipp-byteproc.c | 16 ++++++++++++-
.../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 1 +
.../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 3 ++-
.../platform/st/stm32/stm32-dcmipp/dcmipp-input.c | 28 +++++++++++++++-------
4 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
index 5fcd5123136f..f61618e12322 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
@@ -25,6 +25,7 @@
#define DCMIPP_P0SCSZR_HSIZE_SHIFT 0
#define DCMIPP_P0SCSZR_VSIZE_SHIFT 16
#define DCMIPP_P0PPCR 0x5c0
+#define DCMIPP_P0PPCR_SWAPYUV BIT(0)
#define DCMIPP_P0PPCR_BSM_1_2 0x1
#define DCMIPP_P0PPCR_BSM_1_4 0x2
#define DCMIPP_P0PPCR_BSM_2_4 0x3
@@ -428,9 +429,11 @@ static int dcmipp_byteproc_configure_scale_crop
if (!vpix)
return -EINVAL;
- /* clear decimation/crop */
+ /* clear decimation/crop/swap yuv */
reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_BSM_MASK);
reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_LSM);
+ if (byteproc->ved.dcmipp->pipe_cfg->has_swapyuv)
+ reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_SWAPYUV);
reg_write(byteproc, DCMIPP_P0SCSTR, 0);
reg_write(byteproc, DCMIPP_P0SCSZR, 0);
@@ -451,6 +454,17 @@ static int dcmipp_byteproc_configure_scale_crop
if (vprediv == 2)
val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS;
+ /*
+ * Perform a SWAP YUV if input is parallel since in this mode
+ * the DCMIPP will swap YUV by default
+ */
+ if (byteproc->ved.dcmipp->pipe_cfg->has_swapyuv &&
+ (sink_fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ sink_fmt->code == MEDIA_BUS_FMT_YVYU8_2X8 ||
+ sink_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+ sink_fmt->code == MEDIA_BUS_FMT_VYUY8_2X8))
+ val |= DCMIPP_P0PPCR_SWAPYUV;
+
/* decimate using bytes and lines skipping */
if (val) {
reg_set(byteproc, DCMIPP_P0PPCR, val);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index 9e7b2434200a..2d26425b0b0f 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -118,6 +118,7 @@ struct dcmipp_pipeline_config {
u32 hw_revision;
bool has_csi2;
bool needs_mclk;
+ bool has_swapyuv;
};
/**
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index 3a0a4df410d8..9aba16096d28 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -108,7 +108,8 @@ static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = {
.num_links = ARRAY_SIZE(stm32mp25_ent_links),
.hw_revision = DCMIPP_STM32MP25_VERR,
.has_csi2 = true,
- .needs_mclk = true
+ .needs_mclk = true,
+ .has_swapyuv = true
};
#define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
index 564f37e2e1dd..75421035cfad 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
@@ -80,15 +80,15 @@ static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = {
PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_3X8, RGB888_3X8, RGB888, 0, MIPI_CSI2_DT_RGB888),
PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_1X24, RGB888_1X24, RGB888, 0, MIPI_CSI2_DT_RGB888),
/* YUV422 */
- PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_1X16, UYVY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_1X16, YVYU8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
/* GREY */
PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8),
@@ -356,8 +356,20 @@ static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp,
val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT;
/* swap cycles */
- if (vpix->prcr_swapcycles)
- val |= DCMIPP_PRCR_SWAPCYCLES;
+ /*
+ * Table dcmipp_inp_pix_map_list take into consideration that SWAPYUV
+ * bit is available when dealing with 16bit YUV formats. If it is not
+ * available (such as on stm32mp13), swapcycle setting should be
+ * reversed
+ */
+ if (!inp->ved.dcmipp->pipe_cfg->has_swapyuv &&
+ (src_fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ src_fmt->code == MEDIA_BUS_FMT_YVYU8_2X8 ||
+ src_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+ src_fmt->code == MEDIA_BUS_FMT_VYUY8_2X8))
+ val |= (!vpix->prcr_swapcycles ? DCMIPP_PRCR_SWAPCYCLES : 0);
+ else
+ val |= (vpix->prcr_swapcycles ? DCMIPP_PRCR_SWAPCYCLES : 0);
reg_write(inp, DCMIPP_PRCR, val);
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 06/13] media: stm32: dcmipp: configure csi input of all pipes on stm32mp25
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (4 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 05/13] media: stm32: dcmipp: correct swap in YUYV data with parallel input Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:29 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 07/13] media: stm32: dcmipp: introduce a dcmipp global media_pipeline Alain Volmat
` (6 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
The STM32MP25 has CSI input and 2 additional pixel pipes in addition
to the byte pipe. Each pipe can select which data to receive based
on CSI VC/DT selection.
The multi-stream support of DCMIPP will be added in a future commit,
however, to start putting proper control method, the input subset
has now 3 SRC pads, one per pipe available.
Currently, and until multi-stream support is added, same data is
sent to all pipes.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../platform/st/stm32/stm32-dcmipp/dcmipp-input.c | 75 +++++++++++++++-------
1 file changed, 51 insertions(+), 24 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
index 75421035cfad..3fc859f1cb14 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
@@ -43,12 +43,18 @@
#define DCMIPP_CMCR_INSEL BIT(0)
#define DCMIPP_P0FSCR 0x404
-#define DCMIPP_P0FSCR_DTMODE_MASK GENMASK(17, 16)
-#define DCMIPP_P0FSCR_DTMODE_SHIFT 16
-#define DCMIPP_P0FSCR_DTMODE_DTIDA 0x00
+#define DCMIPP_P1FSCR 0x804
+#define DCMIPP_P2FSCR 0xC04
+#define DCMIPP_PXFSCR_DTMODE_MASK GENMASK(17, 16)
+#define DCMIPP_PXFSCR_DTMODE_SHIFT 16
+#define DCMIPP_PXFSCR_DTMODE_DTIDA 0x00
#define DCMIPP_P0FSCR_DTMODE_ALLDT 0x03
-#define DCMIPP_P0FSCR_DTIDA_MASK GENMASK(5, 0)
-#define DCMIPP_P0FSCR_DTIDA_SHIFT 0
+#define DCMIPP_PXFSCR_DTIDA_MASK GENMASK(5, 0)
+#define DCMIPP_PXFSCR_DTIDA_SHIFT 0
+
+#define DCMIPP_PXFSCR(a) (((a) == 0) ? DCMIPP_P0FSCR :\
+ ((a) == 1) ? DCMIPP_P1FSCR :\
+ DCMIPP_P2FSCR)
#define IS_SINK(pad) (!(pad))
#define IS_SRC(pad) ((pad))
@@ -383,7 +389,8 @@ static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp,
}
static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp,
- struct v4l2_subdev_state *state)
+ struct v4l2_subdev_state *state,
+ u32 pad)
{
const struct dcmipp_inp_pix_map *vpix;
struct v4l2_mbus_framefmt *sink_fmt;
@@ -399,22 +406,28 @@ static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp,
return -EINVAL;
}
- /* Apply configuration on each input pipe */
- reg_clear(inp, DCMIPP_P0FSCR,
- DCMIPP_P0FSCR_DTMODE_MASK | DCMIPP_P0FSCR_DTIDA_MASK);
+ /* Perform the configuration on the related pad/pipe */
+ reg_clear(inp, DCMIPP_PXFSCR(pad - 1),
+ DCMIPP_PXFSCR_DTMODE_MASK | DCMIPP_PXFSCR_DTIDA_MASK);
/* In case of JPEG we don't know the DT so we allow all data */
/*
* TODO - check instead dt == 0 for the time being to allow other
* unknown data-type
*/
- if (!vpix->dt)
- reg_set(inp, DCMIPP_P0FSCR,
- DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_P0FSCR_DTMODE_SHIFT);
- else
+ if (!vpix->dt) {
+ if (pad != 1) {
+ dev_err(inp->dev, "JPEG only available on pipe 0\n");
+ return -EINVAL;
+ }
+ /* Only available on Pipe #0 */
reg_set(inp, DCMIPP_P0FSCR,
- vpix->dt << DCMIPP_P0FSCR_DTIDA_SHIFT |
- DCMIPP_P0FSCR_DTMODE_DTIDA);
+ DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_PXFSCR_DTMODE_SHIFT);
+ } else {
+ reg_set(inp, DCMIPP_PXFSCR(pad - 1),
+ vpix->dt << DCMIPP_PXFSCR_DTIDA_SHIFT |
+ DCMIPP_PXFSCR_DTMODE_DTIDA);
+ }
/* Select the DCMIPP CSI interface */
reg_write(inp, DCMIPP_CMCR, DCMIPP_CMCR_INSEL);
@@ -432,20 +445,24 @@ static int dcmipp_inp_enable_streams(struct v4l2_subdev *sd,
struct media_pad *s_pad;
int ret = 0;
- /* Get source subdev */
- s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
- if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
- return -EINVAL;
- s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
-
if (inp->ved.bus_type == V4L2_MBUS_PARALLEL ||
inp->ved.bus_type == V4L2_MBUS_BT656)
ret = dcmipp_inp_configure_parallel(inp, state);
else if (inp->ved.bus_type == V4L2_MBUS_CSI2_DPHY)
- ret = dcmipp_inp_configure_csi(inp, state);
+ ret = dcmipp_inp_configure_csi(inp, state, pad);
if (ret)
return ret;
+ /* If there where no other pad enabled, then enable the source subdev */
+ if (sd->enabled_pads)
+ return 0;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
if (ret < 0) {
dev_err(inp->dev,
@@ -466,6 +483,10 @@ static int dcmipp_inp_disable_streams(struct v4l2_subdev *sd,
struct media_pad *s_pad;
int ret;
+ /* Don't do anything if there are still other pads enabled */
+ if ((sd->enabled_pads & ~BIT(pad)))
+ return 0;
+
/* Get source subdev */
s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
@@ -533,8 +554,10 @@ struct dcmipp_ent_device *dcmipp_inp_ent_init(const char *entity_name,
struct dcmipp_inp_device *inp;
const unsigned long pads_flag[] = {
MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ MEDIA_PAD_FL_SOURCE, MEDIA_PAD_FL_SOURCE,
};
struct device *dev = dcmipp->dev;
+ u16 num_pads = ARRAY_SIZE(pads_flag);
int ret;
/* Allocate the inp struct */
@@ -543,18 +566,22 @@ struct dcmipp_ent_device *dcmipp_inp_ent_init(const char *entity_name,
return ERR_PTR(-ENOMEM);
inp->regs = dcmipp->regs;
+ inp->ved.dcmipp = dcmipp;
+
+ /* For DCMIPP without CSI2, there is only a single pipe hence 2 pads */
+ if (!inp->ved.dcmipp->pipe_cfg->has_csi2)
+ num_pads = 2;
/* Initialize ved and sd */
ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, &dcmipp->v4l2_dev,
entity_name, MEDIA_ENT_F_VID_IF_BRIDGE,
- ARRAY_SIZE(pads_flag), pads_flag,
+ num_pads, pads_flag,
&dcmipp_inp_int_ops, &dcmipp_inp_ops,
NULL, NULL);
if (ret) {
kfree(inp);
return ERR_PTR(ret);
}
- inp->ved.dcmipp = dcmipp;
inp->dev = dev;
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 07/13] media: stm32: dcmipp: introduce a dcmipp global media_pipeline
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (5 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 06/13] media: stm32: dcmipp: configure csi input of all pipes on stm32mp25 Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:14 ` [PATCH v2 08/13] media: stm32: dcmipp: add pixel pipes helper functions Alain Volmat
` (5 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
With the introduction of stm32mp25 containing several capture
devices, it becomes necessary to share the media_pipeline
structure among all capture devices since subdev pads can be
shared between several capture devices.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 3 +--
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 1 +
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index dac7338dd981..bd6f2d68df80 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -125,7 +125,6 @@ struct dcmipp_bytecap_device {
/* mutex used as vdev and queue lock */
struct mutex lock;
u32 sequence;
- struct media_pipeline pipe;
struct v4l2_subdev *s_subdev;
u32 s_subdev_pad_nb;
@@ -408,7 +407,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
goto err_buffer_done;
}
- ret = media_pipeline_start(entity->pads, &vcap->pipe);
+ ret = media_pipeline_start(entity->pads, &vcap->ved.dcmipp->pipe);
if (ret) {
dev_dbg(vcap->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n",
__func__, ret);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index 2d26425b0b0f..ee9f36268e64 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -73,6 +73,7 @@ struct dcmipp_device {
/* The Associated media_device parent */
struct media_device mdev;
+ struct media_pipeline pipe;
/* Internal v4l2 parent device*/
struct v4l2_device v4l2_dev;
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 08/13] media: stm32: dcmipp: add pixel pipes helper functions
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (6 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 07/13] media: stm32: dcmipp: introduce a dcmipp global media_pipeline Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:14 ` [PATCH v2 09/13] media: stm32: dcmipp: addition of a dcmipp-isp subdev Alain Volmat
` (4 subsequent siblings)
12 siblings, 0 replies; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
Pixel pipes are made of an isp subdev (only main pipe) and a
postproc subdev. This commit add a helper functions common to
those 2 subdevs such as for handling format enumeration,
set_selection handling, so that they do not have to be
duplicated in the two subdeves.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../media/platform/st/stm32/stm32-dcmipp/Makefile | 1 +
.../st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c | 180 +++++++++++++++++++++
.../st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h | 41 +++++
3 files changed, 222 insertions(+)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
index 159105fb40b8..54231569ed6f 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o
+stm32-dcmipp-y += dcmipp-pixelcommon.o
obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c
new file mode 100644
index 000000000000..8b0699e870ef
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-rect.h>
+#include <media/v4l2-subdev.h>
+
+#include "dcmipp-common.h"
+#include "dcmipp-pixelcommon.h"
+
+#define DCMIPP_ENT(id, pad) (1 << (2 * (id) + (pad)))
+#define DCMIPP_ISP_SINK (DCMIPP_ENT(DCMIPP_ISP, 0))
+#define DCMIPP_ISP_SRC (DCMIPP_ENT(DCMIPP_ISP, 1))
+#define DCMIPP_ISP_INOUT (DCMIPP_ISP_SINK | DCMIPP_ISP_SRC)
+#define DCMIPP_MAIN_POSTPROC_SINK (DCMIPP_ENT(DCMIPP_MAIN, 0))
+#define DCMIPP_MAIN_POSTPROC_SRC (DCMIPP_ENT(DCMIPP_MAIN, 1))
+#define DCMIPP_MAIN_POSTPROC_INOUT \
+ (DCMIPP_MAIN_POSTPROC_SINK | DCMIPP_MAIN_POSTPROC_SRC)
+#define DCMIPP_AUX_POSTPROC_SINK (DCMIPP_ENT(DCMIPP_AUX, 0))
+#define DCMIPP_AUX_POSTPROC_SRC (DCMIPP_ENT(DCMIPP_AUX, 1))
+#define DCMIPP_AUX_POSTPROC_INOUT \
+ (DCMIPP_AUX_POSTPROC_SINK | DCMIPP_AUX_POSTPROC_SRC)
+#define DCMIPP_ALL_POSTPROC_SINK \
+ (DCMIPP_MAIN_POSTPROC_SINK | DCMIPP_AUX_POSTPROC_SINK)
+#define DCMIPP_ALL_POSTPROC_INOUT \
+ (DCMIPP_MAIN_POSTPROC_INOUT | DCMIPP_AUX_POSTPROC_INOUT)
+
+#define PIXMAP_MBUS(mbus, applicable_pipes) \
+ { \
+ .code = MEDIA_BUS_FMT_##mbus, \
+ .pipes = applicable_pipes, \
+ }
+static const struct dcmipp_pixelpipe_pix_map
+dcmipp_pixel_formats_list[] = {
+ /* RGB formats */
+ /* RGB565 / RGB888 */
+ PIXMAP_MBUS(RGB565_2X8_LE, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(RGB565_1X16, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(RGB888_3X8, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(RGB888_1X24, DCMIPP_ALL_POSTPROC_INOUT | DCMIPP_ISP_INOUT),
+ /* YUV formats */
+ PIXMAP_MBUS(YUYV8_2X8, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(UYVY8_1X16, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(YUV8_1X24, DCMIPP_ALL_POSTPROC_INOUT | DCMIPP_ISP_SRC),
+ /* GREY */
+ PIXMAP_MBUS(Y8_1X8, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(Y10_1X10, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(Y12_1X12, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(Y14_1X14, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ /* Raw Bayer */
+ /* Raw 8 */
+ PIXMAP_MBUS(SBGGR8_1X8, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG8_1X8, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG8_1X8, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB8_1X8, DCMIPP_ISP_SINK),
+ /* Raw 10 */
+ PIXMAP_MBUS(SBGGR10_1X10, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG10_1X10, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG10_1X10, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB10_1X10, DCMIPP_ISP_SINK),
+ /* Raw 12 */
+ PIXMAP_MBUS(SBGGR12_1X12, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG12_1X12, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG12_1X12, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB12_1X12, DCMIPP_ISP_SINK),
+ /* Raw 14 */
+ PIXMAP_MBUS(SBGGR14_1X14, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG14_1X14, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG14_1X14, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB14_1X14, DCMIPP_ISP_SINK),
+};
+
+const struct dcmipp_pixelpipe_pix_map *
+dcmipp_pixelpipe_pix_map_by_code(__u32 code, unsigned int id, unsigned int pad)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dcmipp_pixel_formats_list); i++) {
+ if (dcmipp_pixel_formats_list[i].code == code &&
+ dcmipp_pixel_formats_list[i].pipes & DCMIPP_ENT(id, pad))
+ return &dcmipp_pixel_formats_list[i];
+ }
+
+ return NULL;
+}
+
+int dcmipp_pixelpipe_enum_mbus_code(unsigned int id,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ unsigned int index = code->index;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dcmipp_pixel_formats_list); i++) {
+ if (!(dcmipp_pixel_formats_list[i].pipes &
+ DCMIPP_ENT(id, code->pad)))
+ continue;
+
+ if (index == 0)
+ break;
+
+ index--;
+ }
+
+ if (i == ARRAY_SIZE(dcmipp_pixel_formats_list))
+ return -EINVAL;
+
+ code->code = dcmipp_pixel_formats_list[i].code;
+
+ return 0;
+}
+
+int dcmipp_pixelpipe_enum_frame_size(unsigned int id,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct dcmipp_pixelpipe_pix_map *vpix;
+
+ if (fse->index)
+ return -EINVAL;
+
+ /* Only accept code in the pix map table */
+ vpix = dcmipp_pixelpipe_pix_map_by_code(fse->code, id, fse->pad);
+ if (!vpix)
+ return -EINVAL;
+
+ fse->min_width = DCMIPP_FRAME_MIN_WIDTH;
+ fse->max_width = DCMIPP_FRAME_MAX_WIDTH;
+ fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;
+ fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;
+
+ return 0;
+}
+
+int dcmipp_pixelpipe_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+
+ if (IS_SRC(s->pad))
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ s->r = *v4l2_subdev_state_get_crop(state, s->pad);
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sink_fmt = v4l2_subdev_state_get_format(state, s->pad);
+ s->r.top = 0;
+ s->r.left = 0;
+ s->r.width = sink_fmt->width;
+ s->r.height = sink_fmt->height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r = *v4l2_subdev_state_get_compose(state, s->pad);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+__u32 dcmipp_pixelpipe_src_format(__u32 input_format)
+{
+ if (input_format >= MEDIA_BUS_FMT_Y8_1X8 &&
+ input_format < MEDIA_BUS_FMT_SBGGR8_1X8)
+ return MEDIA_BUS_FMT_YUV8_1X24;
+
+ return MEDIA_BUS_FMT_RGB888_1X24;
+}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h
new file mode 100644
index 000000000000..db29e1fe939e
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#ifndef _DCMIPP_PIXELCOMMON_H
+#define _DCMIPP_PIXELCOMMON_H
+
+#define IS_SINK(pad) (!(pad))
+#define IS_SRC(pad) ((pad))
+
+#define DCMIPP_ISP 0
+#define DCMIPP_MAIN 1
+#define DCMIPP_AUX 2
+
+struct dcmipp_pixelpipe_pix_map {
+ __u32 code;
+ __u32 pipes;
+};
+
+const struct dcmipp_pixelpipe_pix_map *
+dcmipp_pixelpipe_pix_map_by_code(__u32 code, unsigned int id, unsigned int pad);
+
+int dcmipp_pixelpipe_enum_mbus_code(unsigned int id,
+ struct v4l2_subdev_mbus_code_enum *code);
+
+int dcmipp_pixelpipe_enum_frame_size(unsigned int id,
+ struct v4l2_subdev_frame_size_enum *fse);
+
+int dcmipp_pixelpipe_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s);
+
+__u32 dcmipp_pixelpipe_src_format(__u32 input_format);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 09/13] media: stm32: dcmipp: addition of a dcmipp-isp subdev
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (7 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 08/13] media: stm32: dcmipp: add pixel pipes helper functions Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:29 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 10/13] media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev Alain Volmat
` (3 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
The ISP subdev is the first element after input of the main pipeline.
Part (static configuration) of this block is done via this subdev while
other configuration done on a per-frame basis will be done via a output
metadata device attached to this subdev.
This subdev handled the following features of the ISP block:
- statistic removal (top / bottom of the frame)
- decimation
- demosaicing
- control of frame export to the aux pipeline
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../media/platform/st/stm32/stm32-dcmipp/Makefile | 2 +-
.../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 3 +
.../platform/st/stm32/stm32-dcmipp/dcmipp-isp.c | 482 +++++++++++++++++++++
3 files changed, 486 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
index 54231569ed6f..a708534a51af 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o
-stm32-dcmipp-y += dcmipp-pixelcommon.o
+stm32-dcmipp-y += dcmipp-pixelcommon.o dcmipp-isp.o
obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index ee9f36268e64..e04fde86550a 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -282,5 +282,8 @@ void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved);
struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
struct dcmipp_device *dcmipp);
void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved);
+struct dcmipp_ent_device *dcmipp_isp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
+void dcmipp_isp_ent_release(struct dcmipp_ent_device *ved);
#endif
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c
new file mode 100644
index 000000000000..ed7281495e16
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-rect.h>
+#include <media/v4l2-subdev.h>
+
+#include "dcmipp-common.h"
+#include "dcmipp-pixelcommon.h"
+
+#define DCMIPP_P1FSCR 0x804
+#define DCMIPP_P1FSCR_PIPEDIFF BIT(18)
+
+#define DCMIPP_P1SRCR 0x820
+#define DCMIPP_P1SRCR_LASTLINE_SHIFT 0
+#define DCMIPP_P1SRCR_FIRSTLINEDEL_SHIFT 12
+#define DCMIPP_P1SRCR_CROPEN BIT(15)
+
+#define DCMIPP_P1DECR 0x830
+#define DCMIPP_P1DECR_ENABLE BIT(0)
+#define DCMIPP_P1DECR_HDEC_SHIFT 1
+#define DCMIPP_P1DECR_VDEC_SHIFT 3
+
+#define DCMIPP_P1DMCR 0x870
+#define DCMIPP_P1DMCR_ENABLE BIT(0)
+#define DCMIPP_P1DMCR_TYPE_SHIFT 1
+#define DCMIPP_P1DMCR_TYPE_MASK GENMASK(2, 1)
+#define DCMIPP_P1DMCR_TYPE_RGGB 0x0
+#define DCMIPP_P1DMCR_TYPE_GRBG 0x1
+#define DCMIPP_P1DMCR_TYPE_GBRG 0x2
+#define DCMIPP_P1DMCR_TYPE_BGGR 0x3
+
+#define ISP_MEDIA_BUS_SINK_FMT_DEFAULT MEDIA_BUS_FMT_RGB565_1X16
+#define ISP_MEDIA_BUS_SRC_FMT_DEFAULT MEDIA_BUS_FMT_RGB888_1X24
+
+struct dcmipp_isp_device {
+ struct dcmipp_ent_device ved;
+ struct v4l2_subdev sd;
+ struct device *dev;
+
+ void __iomem *regs;
+};
+
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ .code = ISP_MEDIA_BUS_SINK_FMT_DEFAULT,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = DCMIPP_COLORSPACE_DEFAULT,
+ .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,
+ .quantization = DCMIPP_QUANTIZATION_DEFAULT,
+ .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
+};
+
+static inline unsigned int dcmipp_isp_set_compose(__u32 size, __u32 req)
+{
+ unsigned int i = 0;
+
+ if (req > size)
+ return size;
+
+ /* Maximum decimation factor is 8 */
+ while (size > req && i++ < 3)
+ size /= 2;
+
+ return size;
+}
+
+static void dcmipp_isp_adjust_fmt(struct v4l2_mbus_framefmt *fmt, u32 pad)
+{
+ /* Only accept code in the pix map table */
+ if (!dcmipp_pixelpipe_pix_map_by_code(fmt->code, DCMIPP_ISP, pad))
+ fmt->code = IS_SRC(pad) ? ISP_MEDIA_BUS_SRC_FMT_DEFAULT :
+ ISP_MEDIA_BUS_SINK_FMT_DEFAULT;
+
+ fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,
+ DCMIPP_FRAME_MAX_WIDTH) & ~1;
+ fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,
+ DCMIPP_FRAME_MAX_HEIGHT);
+
+ if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
+ fmt->field = V4L2_FIELD_NONE;
+
+ dcmipp_colorimetry_clamp(fmt);
+}
+
+static int dcmipp_isp_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ for (unsigned int i = 0; i < sd->entity.num_pads; i++) {
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = v4l2_subdev_state_get_format(state, i);
+ *mf = fmt_default;
+ mf->code = IS_SRC(i) ? ISP_MEDIA_BUS_SRC_FMT_DEFAULT :
+ ISP_MEDIA_BUS_SINK_FMT_DEFAULT;
+
+ if (IS_SINK(i)) {
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ };
+
+ *v4l2_subdev_state_get_crop(state, i) = r;
+ *v4l2_subdev_state_get_compose(state, i) = r;
+ }
+ }
+
+ return 0;
+}
+
+static int dcmipp_isp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ return dcmipp_pixelpipe_enum_mbus_code(DCMIPP_ISP, code);
+}
+
+static int dcmipp_isp_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ return dcmipp_pixelpipe_enum_frame_size(DCMIPP_ISP, fse);
+}
+
+static int dcmipp_isp_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ if (v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ dcmipp_isp_adjust_fmt(&fmt->format, fmt->pad);
+
+ if (IS_SINK(fmt->pad)) {
+ struct v4l2_mbus_framefmt *src_fmt =
+ v4l2_subdev_state_get_format(state, 1);
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = fmt->format.width,
+ .height = fmt->format.height,
+ };
+
+ /* Adjust SINK pad crop/compose */
+ *v4l2_subdev_state_get_crop(state, 0) = r;
+ *v4l2_subdev_state_get_compose(state, 0) = r;
+
+ /* Forward format to SRC pads */
+ *src_fmt = fmt->format;
+ src_fmt->code = dcmipp_pixelpipe_src_format(fmt->format.code);
+ *v4l2_subdev_state_get_format(state, 2) = *src_fmt;
+ } else {
+ struct v4l2_mbus_framefmt *sink_fmt =
+ v4l2_subdev_state_get_format(state, 0);
+ struct v4l2_rect *compose =
+ v4l2_subdev_state_get_compose(state, 0);
+
+ fmt->format = *sink_fmt;
+ fmt->format.code = dcmipp_pixelpipe_src_format(sink_fmt->code);
+ if (compose->width && compose->height) {
+ fmt->format.width = compose->width;
+ fmt->format.height = compose->height;
+ }
+ /* Set to the 2nd SRC pad */
+ *v4l2_subdev_state_get_format(state, fmt->pad == 1 ? 2 : 1) =
+ fmt->format;
+ }
+
+ /* Update the selected pad format */
+ *v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format;
+
+ return 0;
+}
+
+static void dcmipp_isp_adjust_crop(struct v4l2_rect *r,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_rect src_rect = {
+ .top = 0,
+ .left = 0,
+ .width = fmt->width,
+ .height = fmt->height,
+ };
+ struct v4l2_rect crop_min = {
+ .top = 8,
+ .left = 0,
+ .width = fmt->width,
+ .height = 1,
+ };
+
+ /* Disallow rectangles smaller than the minimal one. */
+ v4l2_rect_set_min_size(r, &crop_min);
+ v4l2_rect_map_inside(r, &src_rect);
+}
+
+static int dcmipp_isp_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *crop, *compose;
+
+ if (IS_SRC(s->pad))
+ return -EINVAL;
+
+ if (v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ crop = v4l2_subdev_state_get_crop(state, s->pad);
+ compose = v4l2_subdev_state_get_compose(state, s->pad);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ sink_fmt = v4l2_subdev_state_get_format(state, s->pad);
+ dcmipp_isp_adjust_crop(&s->r, sink_fmt);
+
+ *crop = s->r;
+ *compose = s->r;
+
+ dev_dbg(isp->dev, "s_selection: crop (%d,%d)/%ux%u\n",
+ crop->left, crop->top, crop->width, crop->height);
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.top = 0;
+ s->r.left = 0;
+ s->r.width = dcmipp_isp_set_compose(crop->width, s->r.width);
+ s->r.height = dcmipp_isp_set_compose(crop->height, s->r.height);
+ *compose = s->r;
+
+ dev_dbg(isp->dev, "s_selection: compose (%d,%d)/%ux%u\n",
+ compose->left, compose->top,
+ compose->width, compose->height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Update the source pad size */
+ src_fmt = v4l2_subdev_state_get_format(state, 1);
+ src_fmt->width = s->r.width;
+ src_fmt->height = s->r.height;
+
+ return 0;
+}
+
+#define STM32_DCMIPP_IS_BAYER_VARIANT(code, variant) \
+ ((code) == MEDIA_BUS_FMT_S##variant##8_1X8 || \
+ (code) == MEDIA_BUS_FMT_S##variant##10_1X10 || \
+ (code) == MEDIA_BUS_FMT_S##variant##12_1X12 || \
+ (code) == MEDIA_BUS_FMT_S##variant##14_1X14 || \
+ (code) == MEDIA_BUS_FMT_S##variant##16_1X16)
+static void dcmipp_isp_config_demosaicing(struct dcmipp_isp_device *isp,
+ struct v4l2_subdev_state *state)
+{
+ __u32 code = v4l2_subdev_state_get_format(state, 0)->code;
+ unsigned int val = 0;
+
+ /* Disable demosaicing */
+ reg_clear(isp, DCMIPP_P1DMCR,
+ DCMIPP_P1DMCR_ENABLE | DCMIPP_P1DMCR_TYPE_MASK);
+
+ /* Only perform demosaicing if format is bayer */
+ if (code < MEDIA_BUS_FMT_SBGGR8_1X8 || code >= MEDIA_BUS_FMT_JPEG_1X8)
+ return;
+
+ dev_dbg(isp->dev, "Input is RawBayer, enable Demosaicing\n");
+
+ if (STM32_DCMIPP_IS_BAYER_VARIANT(code, BGGR))
+ val = DCMIPP_P1DMCR_TYPE_BGGR << DCMIPP_P1DMCR_TYPE_SHIFT;
+ else if (STM32_DCMIPP_IS_BAYER_VARIANT(code, GBRG))
+ val = DCMIPP_P1DMCR_TYPE_GBRG << DCMIPP_P1DMCR_TYPE_SHIFT;
+ else if (STM32_DCMIPP_IS_BAYER_VARIANT(code, GRBG))
+ val = DCMIPP_P1DMCR_TYPE_GRBG << DCMIPP_P1DMCR_TYPE_SHIFT;
+ else if (STM32_DCMIPP_IS_BAYER_VARIANT(code, RGGB))
+ val = DCMIPP_P1DMCR_TYPE_RGGB << DCMIPP_P1DMCR_TYPE_SHIFT;
+
+ val |= DCMIPP_P1DMCR_ENABLE;
+
+ reg_set(isp, DCMIPP_P1DMCR, val);
+}
+
+static bool dcmipp_isp_is_aux_output_enabled(struct dcmipp_isp_device *isp)
+{
+ struct media_link *link;
+
+ for_each_media_entity_data_link(isp->ved.ent, link) {
+ if (link->source != &isp->ved.pads[2])
+ continue;
+
+ if (!(link->flags & MEDIA_LNK_FL_ENABLED))
+ continue;
+
+ if (!strcmp(link->sink->entity->name, "dcmipp_aux_postproc"))
+ return true;
+ }
+
+ return false;
+}
+
+static void dcmipp_isp_config_decimation(struct dcmipp_isp_device *isp,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+ struct v4l2_rect *compose = v4l2_subdev_state_get_compose(state, 0);
+ u32 decr;
+
+ decr = (fls(crop->width / compose->width) - 1) << DCMIPP_P1DECR_HDEC_SHIFT |
+ (fls(crop->height / compose->height) - 1) << DCMIPP_P1DECR_VDEC_SHIFT;
+ if (decr)
+ decr |= DCMIPP_P1DECR_ENABLE;
+
+ reg_write(isp, DCMIPP_P1DECR, decr);
+}
+
+static int dcmipp_isp_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+ struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Perform configuration only if no other pad is enabled */
+ if (sd->enabled_pads)
+ return 0;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ /* Check if link between ISP & Pipe2 postproc is enabled */
+ if (dcmipp_isp_is_aux_output_enabled(isp))
+ reg_clear(isp, DCMIPP_P1FSCR, DCMIPP_P1FSCR_PIPEDIFF);
+ else
+ reg_set(isp, DCMIPP_P1FSCR, DCMIPP_P1FSCR_PIPEDIFF);
+
+ /* Configure Statistic Removal */
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ reg_write(isp, DCMIPP_P1SRCR,
+ ((crop->top << DCMIPP_P1SRCR_FIRSTLINEDEL_SHIFT) |
+ (crop->height << DCMIPP_P1SRCR_LASTLINE_SHIFT) |
+ DCMIPP_P1SRCR_CROPEN));
+
+ /* Configure Decimation */
+ dcmipp_isp_config_decimation(isp, state);
+
+ /* Configure Demosaicing */
+ dcmipp_isp_config_demosaicing(isp, state);
+
+ ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(isp->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dcmipp_isp_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Don't do anything if there are still other pads enabled */
+ if ((sd->enabled_pads & ~BIT(pad)))
+ return 0;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ /* Disable all blocks */
+ reg_write(isp, DCMIPP_P1SRCR, 0);
+ reg_write(isp, DCMIPP_P1DECR, 0);
+ reg_write(isp, DCMIPP_P1DMCR, 0);
+
+ ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(isp->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops dcmipp_isp_pad_ops = {
+ .enum_mbus_code = dcmipp_isp_enum_mbus_code,
+ .enum_frame_size = dcmipp_isp_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = dcmipp_isp_set_fmt,
+ .get_selection = dcmipp_pixelpipe_get_selection,
+ .set_selection = dcmipp_isp_set_selection,
+ .enable_streams = dcmipp_isp_enable_streams,
+ .disable_streams = dcmipp_isp_disable_streams,
+};
+
+static const struct v4l2_subdev_video_ops dcmipp_isp_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_ops dcmipp_isp_ops = {
+ .pad = &dcmipp_isp_pad_ops,
+ .video = &dcmipp_isp_video_ops,
+};
+
+static void dcmipp_isp_release(struct v4l2_subdev *sd)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+
+ kfree(isp);
+}
+
+static const struct v4l2_subdev_internal_ops dcmipp_isp_int_ops = {
+ .init_state = dcmipp_isp_init_state,
+ .release = dcmipp_isp_release,
+};
+
+void dcmipp_isp_ent_release(struct dcmipp_ent_device *ved)
+{
+ struct dcmipp_isp_device *isp =
+ container_of(ved, struct dcmipp_isp_device, ved);
+
+ dcmipp_ent_sd_unregister(ved, &isp->sd);
+}
+
+struct dcmipp_ent_device *dcmipp_isp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
+{
+ struct dcmipp_isp_device *isp;
+ const unsigned long pads_flag[] = {
+ MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ MEDIA_PAD_FL_SOURCE,
+ };
+ int ret;
+
+ /* Allocate the isp struct */
+ isp = kzalloc_obj(*isp);
+ if (!isp)
+ return ERR_PTR(-ENOMEM);
+
+ isp->regs = dcmipp->regs;
+
+ /* Initialize ved and sd */
+ ret = dcmipp_ent_sd_register(&isp->ved, &isp->sd,
+ &dcmipp->v4l2_dev, entity_name,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER,
+ ARRAY_SIZE(pads_flag), pads_flag,
+ &dcmipp_isp_int_ops, &dcmipp_isp_ops,
+ NULL, NULL);
+ if (ret) {
+ kfree(isp);
+ return ERR_PTR(ret);
+ }
+
+ isp->ved.dcmipp = dcmipp;
+ isp->dev = dcmipp->dev;
+
+ return &isp->ved;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 10/13] media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (8 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 09/13] media: stm32: dcmipp: addition of a dcmipp-isp subdev Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:32 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 11/13] media: stm32: dcmipp: add pixel-pipe support in bytecap Alain Volmat
` (2 subsequent siblings)
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
Addition of the driver for dcmipp-pixelproc subdev. This subdev is the
last one before the capture device at the tail of both main and
aux pipelines.
It is in charge of:
- framerate adjustment
- downscale
- gamma correction
- color conversion
- pixel packing
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../media/platform/st/stm32/stm32-dcmipp/Makefile | 2 +-
.../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 4 +
.../st/stm32/stm32-dcmipp/dcmipp-pixelproc.c | 935 +++++++++++++++++++++
3 files changed, 940 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
index a708534a51af..7178934bb116 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o
-stm32-dcmipp-y += dcmipp-pixelcommon.o dcmipp-isp.o
+stm32-dcmipp-y += dcmipp-pixelcommon.o dcmipp-isp.o dcmipp-pixelproc.o
obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index e04fde86550a..8f41473605aa 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -285,5 +285,9 @@ void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved);
struct dcmipp_ent_device *dcmipp_isp_ent_init(const char *entity_name,
struct dcmipp_device *dcmipp);
void dcmipp_isp_ent_release(struct dcmipp_ent_device *ved);
+struct dcmipp_ent_device *
+dcmipp_pixelproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
+void dcmipp_pixelproc_ent_release(struct dcmipp_ent_device *ved);
#endif
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c
new file mode 100644
index 000000000000..40394ebfd827
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c
@@ -0,0 +1,935 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-rect.h>
+#include <media/v4l2-subdev.h>
+
+#include "dcmipp-common.h"
+#include "dcmipp-pixelcommon.h"
+
+#define DCMIPP_P1CRSTR 0x904
+#define DCMIPP_P2CRSTR 0xD04
+#define DCMIPP_PxCRSTR(id) (((id) == 1) ? DCMIPP_P1CRSTR :\
+ DCMIPP_P2CRSTR)
+#define DCMIPP_PxCRSTR_HSTART_SHIFT 0
+#define DCMIPP_PxCRSTR_VSTART_SHIFT 16
+#define DCMIPP_P1CRSZR 0x908
+#define DCMIPP_P2CRSZR 0xD08
+#define DCMIPP_PxCRSZR(id) (((id) == 1) ? DCMIPP_P1CRSZR :\
+ DCMIPP_P2CRSZR)
+#define DCMIPP_PxCRSZR_ENABLE BIT(31)
+#define DCMIPP_PxCRSZR_HSIZE_SHIFT 0
+#define DCMIPP_PxCRSZR_VSIZE_SHIFT 16
+
+#define DCMIPP_P1DCCR 0x90C
+#define DCMIPP_P2DCCR 0xD0C
+#define DCMIPP_PxDCCR(id) (((id) == 1) ? DCMIPP_P1DCCR :\
+ DCMIPP_P2DCCR)
+#define DCMIPP_PxDCCR_ENABLE BIT(0)
+#define DCMIPP_PxDCCR_HDEC_SHIFT 1
+#define DCMIPP_PxDCCR_VDEC_SHIFT 3
+
+#define DCMIPP_P1DSCR 0x910
+#define DCMIPP_P2DSCR 0xD10
+#define DCMIPP_PxDSCR(id) (((id) == 1) ? DCMIPP_P1DSCR :\
+ DCMIPP_P2DSCR)
+#define DCMIPP_PxDSCR_HDIV_SHIFT 0
+#define DCMIPP_PxDSCR_VDIV_SHIFT 16
+#define DCMIPP_PxDSCR_ENABLE BIT(31)
+
+#define DCMIPP_P1DSRTIOR 0x914
+#define DCMIPP_P2DSRTIOR 0xD14
+#define DCMIPP_PxDSRTIOR(id) (((id) == 1) ? DCMIPP_P1DSRTIOR :\
+ DCMIPP_P2DSRTIOR)
+#define DCMIPP_PxDSRTIOR_HRATIO_SHIFT 0
+#define DCMIPP_PxDSRTIOR_HRATIO_MASK GENMASK(15, 0)
+#define DCMIPP_PxDSRTIOR_VRATIO_SHIFT 16
+#define DCMIPP_PxDSRTIOR_VRATIO_MASK GENMASK(31, 16)
+
+#define DCMIPP_P1DSSZR 0x918
+#define DCMIPP_P2DSSZR 0xD18
+#define DCMIPP_PxDSSZR(id) (((id) == 1) ? DCMIPP_P1DSSZR :\
+ DCMIPP_P2DSSZR)
+#define DCMIPP_PxDSSZR_HSIZE_SHIFT 0
+#define DCMIPP_PxDSSZR_HSIZE_MASK GENMASK(11, 0)
+#define DCMIPP_PxDSSZR_VSIZE_SHIFT 16
+#define DCMIPP_PxDSSZR_VSIZE_MASK GENMASK(27, 16)
+
+#define DCMIPP_P1GMCR 0x970
+#define DCMIPP_P2GMCR 0xD70
+#define DCMIPP_PxGMCR(id) (((id) == 1) ? DCMIPP_P1GMCR :\
+ DCMIPP_P2GMCR)
+#define DCMIPP_PxGMCR_ENABLE BIT(0)
+
+#define DCMIPP_P1YUVCR 0x980
+#define DCMIPP_P1YUVCR_ENABLE BIT(0)
+#define DCMIPP_P1YUVCR_TYPE_RGB BIT(1)
+#define DCMIPP_P1YUVCR_CLAMP BIT(2)
+#define DCMIPP_P1YUVRR1 0x984
+#define DCMIPP_P1YUVRR2 0x988
+#define DCMIPP_P1YUVGR1 0x98C
+#define DCMIPP_P1YUVGR2 0x990
+#define DCMIPP_P1YUVBR1 0x994
+#define DCMIPP_P1YUVBR2 0x998
+
+#define PIXELPROC_MEDIA_BUS_FMT_DEFAULT MEDIA_BUS_FMT_RGB888_1X24
+
+/* Macro for negative coefficient, 11 bits coded */
+#define N11(val) (((val) ^ 0x7ff) + 1)
+/* Macro for added value, 10 bits coded */
+#define N10(val) (((val) ^ 0x3ff) + 1)
+
+/* Macro to convert row matrix to DCMIPP PxCCCyy register value */
+#define CCTBL(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba) \
+ .conv_matrix = { \
+ ((rg) << 16 | (rr)), ((ra) << 16 | (rb)), \
+ ((gg) << 16 | (gr)), ((ga) << 16 | (gb)), \
+ ((bg) << 16 | (br)), ((ba) << 16 | (bb)) }
+
+struct dcmipp_colorconv_config {
+ unsigned int conv_matrix[6];
+ bool clamping;
+ bool clamping_as_rgb;
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv601full = {
+ /* R G B Add */
+ CCTBL(131, N11(110), N11(21), 128, /* Cr */
+ 77, 150, 29, 0, /* Y */
+ N11(44), N11(87), 131, 128), /* Cb */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv601lim = {
+ /* R G B Add */
+ CCTBL(112, N11(94), N11(18), 128, /* Cr */
+ 66, 129, 25, 16, /* Y */
+ N11(38), N11(74), 112, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv709full = {
+ /* R G B Add */
+ CCTBL(131, N11(119), N11(12), 128, /* Cr */
+ 55, 183, 18, 0, /* Y */
+ N11(30), N11(101), 131, 128), /* Cb */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv709lim = {
+ /* R G B Add */
+ CCTBL(112, N11(102), N11(10), 128, /* Cr */
+ 47, 157, 16, 16, /* Y */
+ N11(26), N11(87), 112, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgblim_to_yuv601lim = {
+ /* R G B Add */
+ CCTBL(131, N11(110), N11(21), 128, /* Cr */
+ 77, 150, 29, 0, /* Y */
+ N11(44), N11(87), 131, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgblim_to_yuv709lim = {
+ /* R G B Add */
+ CCTBL(131, N11(119), N11(12), 128, /* Cr */
+ 55, 183, 18, 0, /* Y */
+ N11(30), N11(101), 131, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv601full_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(351, 256, 0, N10(175), /* R */
+ N11(179), 256, N11(86), 132, /* G */
+ 0, 256, 443, N10(222)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv601lim_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(409, 298, 0, N10(223), /* R */
+ N11(208), 298, N11(100), 135, /* G */
+ 0, 298, 517, N10(277)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv601lim_to_rgblim = {
+ /* Cr Y Cb Add */
+ CCTBL(351, 256, 0, N10(175), /* R */
+ N11(179), 256, N11(86), 132, /* G */
+ 0, 256, 443, N10(222)), /* B */
+ .clamping = true,
+ .clamping_as_rgb = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv709full_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(394, 256, 0, N10(197), /* R */
+ N11(118), 256, N11(47), 82, /* G */
+ 0, 256, 456, N10(232)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv709lim_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(459, 298, 0, N10(248), /* R */
+ N11(137), 298, N11(55), 77, /* G */
+ 0, 298, 541, N10(289)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv709lim_to_rgblim = {
+ /* Cr Y Cb Add */
+ CCTBL(394, 256, 0, N10(197), /* R */
+ N11(118), 256, N11(47), 82, /* G */
+ 0, 256, 465, N10(232)), /* B */
+ .clamping = true,
+ .clamping_as_rgb = true,
+};
+
+/* cconv_matrices[src_fmt][src_range][sink_fmt][sink_range] */
+static const struct dcmipp_colorconv_config *dcmipp_cconv_cfgs[3][2][3][2] = {
+ /* RGB */
+ {
+ /* RGB full range */
+ {
+ /* RGB full range => RGB */
+ {
+ NULL, NULL,
+ },
+ /* RGB full range => YUV601 */
+ {
+ &dcmipp_rgbfull_to_yuv601full,
+ &dcmipp_rgbfull_to_yuv601lim,
+ },
+ /* RGB full range => YUV709 */
+ {
+ &dcmipp_rgbfull_to_yuv709full,
+ &dcmipp_rgbfull_to_yuv709lim,
+ },
+ },
+ /* RGB limited range */
+ {
+ /* RGB limited range => RGB */
+ {
+ NULL, NULL,
+ },
+ /* RGB limited range => YUV601 */
+ {
+ NULL, &dcmipp_rgblim_to_yuv601lim,
+ },
+ /* RGB limited range => YUV709 */
+ {
+ NULL, &dcmipp_rgblim_to_yuv709lim,
+ },
+ },
+ },
+ /* YUV601 */
+ {
+ /* YUV601 full range */
+ {
+ /* YUV601 full range => RGB */
+ {
+ &dcmipp_yuv601full_to_rgbfull, NULL,
+ },
+ /* YUV601 full range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV601 full range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ /* YUV601 limited range */
+ {
+ /* YUV601 limited range => RGB */
+ {
+ &dcmipp_yuv601lim_to_rgbfull,
+ &dcmipp_yuv601lim_to_rgblim,
+ },
+ /* YUV601 limited range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV601 limited range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ },
+ /* YUV709 */
+ {
+ /* YUV709 full range */
+ {
+ /* YUV709 full range => RGB */
+ {
+ &dcmipp_yuv709full_to_rgbfull, NULL,
+ },
+ /* YUV709 full range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV709 full range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ /* YUV709 limited range */
+ {
+ /* YUV709 limited range => RGB */
+ {
+ &dcmipp_yuv709lim_to_rgbfull,
+ &dcmipp_yuv709lim_to_rgblim,
+ },
+ /* YUV709 limited range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV709 limited range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ },
+};
+
+enum dcmipp_cconv_fmt {
+ FMT_RGB = 0,
+ FMT_YUV601,
+ FMT_YUV709
+};
+
+static inline enum dcmipp_cconv_fmt to_cconv_fmt(struct v4l2_mbus_framefmt *fmt)
+{
+ /* YUV format codes are within the 0x2xxx */
+ if (fmt->code >= MEDIA_BUS_FMT_Y8_1X8 &&
+ fmt->code < MEDIA_BUS_FMT_SBGGR8_1X8) {
+ if (fmt->ycbcr_enc == V4L2_YCBCR_ENC_709)
+ return FMT_YUV709;
+ else
+ return FMT_YUV601;
+ }
+
+ /* All other formats are referred as RGB, indeed, demosaicing bloc
+ * generate RGB format
+ */
+ return FMT_RGB;
+};
+
+#define FMT_STR(f) ({ \
+ typeof(f) __f = (f); \
+ (__f) == FMT_RGB ? "RGB" : \
+ (__f) == FMT_YUV601 ? "YUV601" : \
+ (__f) == FMT_YUV709 ? "YUV709" : "?"; })
+
+enum dcmipp_cconv_range {
+ RANGE_FULL = 0,
+ RANGE_LIMITED,
+};
+
+static inline enum dcmipp_cconv_range
+to_cconv_range(struct v4l2_mbus_framefmt *fmt)
+{
+ if (fmt->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ return RANGE_FULL;
+
+ return RANGE_LIMITED;
+};
+
+#define RANGE_STR(range) ((range) == RANGE_FULL ? "full" : "limited")
+
+struct dcmipp_pixelproc_device {
+ struct dcmipp_ent_device ved;
+ struct v4l2_subdev sd;
+ struct device *dev;
+ bool streaming;
+
+ void __iomem *regs;
+ struct v4l2_ctrl_handler ctrls;
+
+ u32 pipe_id;
+};
+
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ .code = PIXELPROC_MEDIA_BUS_FMT_DEFAULT,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = DCMIPP_COLORSPACE_DEFAULT,
+ .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,
+ .quantization = DCMIPP_QUANTIZATION_DEFAULT,
+ .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
+};
+
+static const struct v4l2_rect crop_min = {
+ .width = DCMIPP_FRAME_MIN_WIDTH,
+ .height = DCMIPP_FRAME_MIN_HEIGHT,
+ .top = 0,
+ .left = 0,
+};
+
+/*
+ * Downscale is a combination of both decimation block (1/2/4/8)
+ * and downsize block (up to 8x) for a total of maximum downscale of 64
+ */
+#define DCMIPP_MAX_DECIMATION_RATIO 8
+#define DCMIPP_MAX_DOWNSIZE_RATIO 8
+#define DCMIPP_MAX_DOWNSCALE_RATIO 64
+
+/*
+ * Functions handling controls
+ */
+#define V4L2_CID_PIXELPROC_GAMMA_CORRECTION (V4L2_CID_USER_BASE | 0x1001)
+
+static int dcmipp_pixelproc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct dcmipp_pixelproc_device *pixelproc =
+ container_of(ctrl->handler,
+ struct dcmipp_pixelproc_device, ctrls);
+
+ if (pm_runtime_get_if_in_use(pixelproc->dev) == 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_PIXELPROC_GAMMA_CORRECTION:
+ reg_write(pixelproc, DCMIPP_PxGMCR(pixelproc->pipe_id),
+ (ctrl->val ? DCMIPP_PxGMCR_ENABLE : 0));
+ break;
+ }
+
+ pm_runtime_put(pixelproc->dev);
+
+ return 0;
+};
+
+static const struct v4l2_ctrl_ops dcmipp_pixelproc_ctrl_ops = {
+ .s_ctrl = dcmipp_pixelproc_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config dcmipp_pixelproc_ctrls[] = {
+ {
+ .ops = &dcmipp_pixelproc_ctrl_ops,
+ .id = V4L2_CID_PIXELPROC_GAMMA_CORRECTION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Gamma correction",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+ }
+};
+
+static void dcmipp_pixelproc_adjust_crop(struct v4l2_rect *r,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_rect src_rect = {
+ .top = 0,
+ .left = 0,
+ .width = fmt->width,
+ .height = fmt->height,
+ };
+
+ /* Disallow rectangles smaller than the minimal one. */
+ v4l2_rect_set_min_size(r, &crop_min);
+ v4l2_rect_map_inside(r, &src_rect);
+}
+
+static void
+dcmipp_pixelproc_adjust_fmt(struct dcmipp_pixelproc_device *pixelproc,
+ struct v4l2_mbus_framefmt *fmt, u32 pad)
+{
+ const struct dcmipp_pixelpipe_pix_map *vpix;
+
+ /* Only accept code in the pix map table */
+ vpix = dcmipp_pixelpipe_pix_map_by_code(fmt->code,
+ pixelproc->pipe_id == 1 ? DCMIPP_MAIN : DCMIPP_AUX,
+ pad);
+ if (!vpix)
+ fmt->code = PIXELPROC_MEDIA_BUS_FMT_DEFAULT;
+
+ fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,
+ DCMIPP_FRAME_MAX_WIDTH);
+ fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,
+ DCMIPP_FRAME_MAX_HEIGHT);
+
+ if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
+ fmt->field = V4L2_FIELD_NONE;
+
+ dcmipp_colorimetry_clamp(fmt);
+}
+
+static int dcmipp_pixelproc_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ *v4l2_subdev_state_get_format(state, i) = fmt_default;
+
+ if (IS_SINK(i)) {
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ };
+ *v4l2_subdev_state_get_crop(state, i) = r;
+ *v4l2_subdev_state_get_compose(state, i) = r;
+ }
+ }
+
+ return 0;
+}
+
+static int
+dcmipp_pixelproc_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ return dcmipp_pixelpipe_enum_mbus_code(pixelproc->pipe_id == 1 ? DCMIPP_MAIN : DCMIPP_AUX,
+ code);
+}
+
+static int
+dcmipp_pixelproc_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ return dcmipp_pixelpipe_enum_frame_size(pixelproc->pipe_id == 1 ? DCMIPP_MAIN : DCMIPP_AUX,
+ fse);
+}
+
+static int dcmipp_pixelproc_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ if (v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ dcmipp_pixelproc_adjust_fmt(pixelproc, &fmt->format, fmt->pad);
+
+ if (IS_SINK(fmt->pad)) {
+ struct v4l2_mbus_framefmt *src_fmt =
+ v4l2_subdev_state_get_format(state, 1);
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = fmt->format.width,
+ .height = fmt->format.height,
+ };
+
+ /* Adjust SINK pad crop/compose */
+ *v4l2_subdev_state_get_crop(state, 0) = r;
+ *v4l2_subdev_state_get_compose(state, 0) = r;
+
+ /* Forward format to SRC pad */
+ *src_fmt = fmt->format;
+ src_fmt->code = dcmipp_pixelpipe_src_format(fmt->format.code);
+ } else {
+ struct v4l2_rect *compose =
+ v4l2_subdev_state_get_compose(state, 0);
+
+ /* AUX (pipe_nb 2) cannot perform color conv */
+ if (pixelproc->pipe_id == 2) {
+ struct v4l2_mbus_framefmt *sink_fmt =
+ v4l2_subdev_state_get_format(state, 0);
+
+ fmt->format = *sink_fmt;
+ fmt->format.code =
+ dcmipp_pixelpipe_src_format(fmt->format.code);
+ }
+
+ fmt->format.width = compose->width;
+ fmt->format.height = compose->height;
+ }
+
+ /* Update the selected pad format */
+ *v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format;
+
+ return 0;
+}
+
+static int dcmipp_pixelproc_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *crop, *compose;
+
+ if (IS_SRC(s->pad))
+ return -EINVAL;
+
+ if (v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ crop = v4l2_subdev_state_get_crop(state, s->pad);
+ compose = v4l2_subdev_state_get_compose(state, s->pad);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ sink_fmt = v4l2_subdev_state_get_format(state, s->pad);
+ dcmipp_pixelproc_adjust_crop(&s->r, sink_fmt);
+
+ *crop = s->r;
+ *compose = s->r;
+
+ dev_dbg(pixelproc->dev, "s_selection: crop (%d,%d)/%ux%u\n",
+ crop->left, crop->top, crop->width, crop->height);
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.top = 0;
+ s->r.left = 0;
+ s->r.width = clamp_t(u32, s->r.width,
+ crop->width / DCMIPP_MAX_DOWNSCALE_RATIO,
+ crop->width);
+ s->r.height = clamp_t(u32, s->r.height,
+ crop->height / DCMIPP_MAX_DOWNSCALE_RATIO,
+ crop->height);
+ *compose = s->r;
+
+ dev_dbg(pixelproc->dev, "s_selection: compose (%d,%d)/%ux%u\n",
+ compose->left, compose->top,
+ compose->width, compose->height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Update the source pad size */
+ src_fmt = v4l2_subdev_state_get_format(state, 1);
+ src_fmt->width = s->r.width;
+ src_fmt->height = s->r.height;
+
+ return 0;
+}
+
+static int
+dcmipp_pixelproc_colorconv_config(struct dcmipp_pixelproc_device *pixelproc,
+ struct v4l2_mbus_framefmt *sink,
+ struct v4l2_mbus_framefmt *src)
+{
+ const struct dcmipp_colorconv_config *cconv_cfg;
+ enum dcmipp_cconv_fmt sink_fmt = to_cconv_fmt(sink);
+ enum dcmipp_cconv_range sink_range = to_cconv_range(sink);
+ enum dcmipp_cconv_fmt src_fmt = to_cconv_fmt(src);
+ enum dcmipp_cconv_range src_range = to_cconv_range(src);
+ unsigned int val = 0;
+ int i;
+
+ /* Disable color conversion by default */
+ reg_write(pixelproc, DCMIPP_P1YUVCR, 0);
+
+ if (sink_fmt == src_fmt && sink_range == src_range)
+ return 0;
+
+ /* color conversion */
+ cconv_cfg = dcmipp_cconv_cfgs[sink_fmt][sink_range][src_fmt][src_range];
+ if (!cconv_cfg) {
+ dev_err(pixelproc->dev,
+ "Unsupported color conversion %s-%s => %s-%s\n",
+ FMT_STR(sink_fmt), RANGE_STR(sink_range),
+ FMT_STR(src_fmt), RANGE_STR(src_range));
+ return -EINVAL;
+ }
+
+ dev_dbg(pixelproc->dev, "color conversion %s-%s => %s-%s\n",
+ FMT_STR(sink_fmt), RANGE_STR(sink_range),
+ FMT_STR(src_fmt), RANGE_STR(src_range));
+
+ for (i = 0; i < 6; i++)
+ reg_write(pixelproc, DCMIPP_P1YUVRR1 + (4 * i),
+ cconv_cfg->conv_matrix[i]);
+
+ if (cconv_cfg->clamping)
+ val |= DCMIPP_P1YUVCR_CLAMP;
+ if (cconv_cfg->clamping_as_rgb)
+ val |= DCMIPP_P1YUVCR_TYPE_RGB;
+ val |= DCMIPP_P1YUVCR_ENABLE;
+
+ reg_write(pixelproc, DCMIPP_P1YUVCR, val);
+
+ return 0;
+}
+
+#define DCMIPP_PIXELPROC_HVRATIO_CONS 8192
+#define DCMIPP_PIXELPROC_HVRATIO_MAX 65535
+#define DCMIPP_PIXELPROC_HVDIV_CONS 1024
+#define DCMIPP_PIXELPROC_HVDIV_MAX 1023
+static void
+dcmipp_pixelproc_set_crop_downscale(struct dcmipp_pixelproc_device *pixelproc,
+ struct v4l2_rect *compose,
+ struct v4l2_rect *crop)
+{
+ unsigned int hratio, vratio, hdiv, vdiv;
+ unsigned int hdec = 0, vdec = 0;
+ unsigned int h_post_dec = crop->width;
+ unsigned int v_post_dec = crop->height;
+
+ /* Configure cropping */
+ reg_write(pixelproc, DCMIPP_PxCRSTR(pixelproc->pipe_id),
+ (crop->top << DCMIPP_PxCRSTR_VSTART_SHIFT) |
+ (crop->left << DCMIPP_PxCRSTR_HSTART_SHIFT));
+ reg_write(pixelproc, DCMIPP_PxCRSZR(pixelproc->pipe_id),
+ (crop->width << DCMIPP_PxCRSZR_HSIZE_SHIFT) |
+ (crop->height << DCMIPP_PxCRSZR_VSIZE_SHIFT) |
+ DCMIPP_PxCRSZR_ENABLE);
+
+ /* Compute decimation factors (HDEC/VDEC) */
+ while (compose->width * DCMIPP_MAX_DOWNSIZE_RATIO < h_post_dec) {
+ hdec++;
+ h_post_dec /= 2;
+ }
+ while (compose->height * DCMIPP_MAX_DOWNSIZE_RATIO < v_post_dec) {
+ vdec++;
+ v_post_dec /= 2;
+ }
+
+ /* Compute downsize factor */
+ hratio = h_post_dec * DCMIPP_PIXELPROC_HVRATIO_CONS /
+ compose->width;
+ if (hratio > DCMIPP_PIXELPROC_HVRATIO_MAX)
+ hratio = DCMIPP_PIXELPROC_HVRATIO_MAX;
+ vratio = v_post_dec * DCMIPP_PIXELPROC_HVRATIO_CONS /
+ compose->height;
+ if (vratio > DCMIPP_PIXELPROC_HVRATIO_MAX)
+ vratio = DCMIPP_PIXELPROC_HVRATIO_MAX;
+ hdiv = (DCMIPP_PIXELPROC_HVDIV_CONS * compose->width) /
+ h_post_dec;
+ if (hdiv > DCMIPP_PIXELPROC_HVDIV_MAX)
+ hdiv = DCMIPP_PIXELPROC_HVDIV_MAX;
+ vdiv = (DCMIPP_PIXELPROC_HVDIV_CONS * compose->height) /
+ v_post_dec;
+ if (vdiv > DCMIPP_PIXELPROC_HVDIV_MAX)
+ vdiv = DCMIPP_PIXELPROC_HVDIV_MAX;
+
+ dev_dbg(pixelproc->dev, "%s: decimation config: hdec: 0x%x, vdec: 0x%x\n",
+ pixelproc->sd.name,
+ hdec, vdec);
+ dev_dbg(pixelproc->dev, "%s: downsize config: hratio: 0x%x, vratio: 0x%x, hdiv: 0x%x, vdiv: 0x%x\n",
+ pixelproc->sd.name,
+ hratio, vratio,
+ hdiv, vdiv);
+
+ reg_clear(pixelproc, DCMIPP_PxDCCR(pixelproc->pipe_id),
+ DCMIPP_PxDCCR_ENABLE);
+ if (hdec || vdec)
+ reg_write(pixelproc, DCMIPP_PxDCCR(pixelproc->pipe_id),
+ (hdec << DCMIPP_PxDCCR_HDEC_SHIFT) |
+ (vdec << DCMIPP_PxDCCR_VDEC_SHIFT) |
+ DCMIPP_PxDCCR_ENABLE);
+
+ reg_clear(pixelproc, DCMIPP_PxDSCR(pixelproc->pipe_id),
+ DCMIPP_PxDSCR_ENABLE);
+ reg_write(pixelproc, DCMIPP_PxDSRTIOR(pixelproc->pipe_id),
+ (hratio << DCMIPP_PxDSRTIOR_HRATIO_SHIFT) |
+ (vratio << DCMIPP_PxDSRTIOR_VRATIO_SHIFT));
+ reg_write(pixelproc, DCMIPP_PxDSSZR(pixelproc->pipe_id),
+ (compose->width << DCMIPP_PxDSSZR_HSIZE_SHIFT) |
+ (compose->height << DCMIPP_PxDSSZR_VSIZE_SHIFT));
+ reg_write(pixelproc, DCMIPP_PxDSCR(pixelproc->pipe_id),
+ (hdiv << DCMIPP_PxDSCR_HDIV_SHIFT) |
+ (vdiv << DCMIPP_PxDSCR_VDIV_SHIFT) |
+ DCMIPP_PxDSCR_ENABLE);
+}
+
+static int dcmipp_pixelproc_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ /* Configure crop/downscale */
+ dcmipp_pixelproc_set_crop_downscale(pixelproc,
+ v4l2_subdev_state_get_compose(state, 0),
+ v4l2_subdev_state_get_crop(state, 0));
+
+ /* Configure YUV Conversion (if applicable) */
+ if (pixelproc->pipe_id == 1) {
+ ret = dcmipp_pixelproc_colorconv_config(pixelproc,
+ v4l2_subdev_state_get_format(state, 0),
+ v4l2_subdev_state_get_format(state, 1));
+ if (ret)
+ return ret;
+ }
+
+ /* Apply customized values from user when stream starts. */
+ ret = v4l2_ctrl_handler_setup(pixelproc->sd.ctrl_handler);
+ if (ret < 0) {
+ dev_err(pixelproc->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(pixelproc->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dcmipp_pixelproc_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0)
+ dev_err(pixelproc->dev,
+ "failed to stop source subdev streaming (%d)\n",
+ ret);
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops dcmipp_pixelproc_pad_ops = {
+ .enum_mbus_code = dcmipp_pixelproc_enum_mbus_code,
+ .enum_frame_size = dcmipp_pixelproc_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = dcmipp_pixelproc_set_fmt,
+ .get_selection = dcmipp_pixelpipe_get_selection,
+ .set_selection = dcmipp_pixelproc_set_selection,
+ .enable_streams = dcmipp_pixelproc_enable_streams,
+ .disable_streams = dcmipp_pixelproc_disable_streams,
+};
+
+static const struct v4l2_subdev_core_ops dcmipp_pixelproc_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops dcmipp_pixelproc_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_ops dcmipp_pixelproc_ops = {
+ .core = &dcmipp_pixelproc_core_ops,
+ .pad = &dcmipp_pixelproc_pad_ops,
+ .video = &dcmipp_pixelproc_video_ops,
+};
+
+static void dcmipp_pixelproc_release(struct v4l2_subdev *sd)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ kfree(pixelproc);
+}
+
+static const struct v4l2_subdev_internal_ops dcmipp_pixelproc_int_ops = {
+ .init_state = dcmipp_pixelproc_init_state,
+ .release = dcmipp_pixelproc_release,
+};
+
+void dcmipp_pixelproc_ent_release(struct dcmipp_ent_device *ved)
+{
+ struct dcmipp_pixelproc_device *pixelproc =
+ container_of(ved, struct dcmipp_pixelproc_device, ved);
+
+ dcmipp_ent_sd_unregister(ved, &pixelproc->sd);
+}
+
+static int dcmipp_name_to_pipe_id(const char *name)
+{
+ if (strstr(name, "main"))
+ return 1;
+ else if (strstr(name, "aux"))
+ return 2;
+ else
+ return -EINVAL;
+}
+
+struct dcmipp_ent_device *
+dcmipp_pixelproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
+{
+ struct dcmipp_pixelproc_device *pixelproc;
+ const unsigned long pads_flag[] = {
+ MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ };
+ int ret, i;
+
+ /* Allocate the pixelproc struct */
+ pixelproc = kzalloc_obj(*pixelproc);
+ if (!pixelproc)
+ return ERR_PTR(-ENOMEM);
+
+ pixelproc->regs = dcmipp->regs;
+ pixelproc->dev = dcmipp->dev;
+
+ /* Pipe identifier */
+ pixelproc->pipe_id = dcmipp_name_to_pipe_id(entity_name);
+ if (pixelproc->pipe_id != 1 && pixelproc->pipe_id != 2) {
+ dev_err(pixelproc->dev, "failed to retrieve pipe_id\n");
+ kfree(pixelproc);
+ return ERR_PTR(-EIO);
+ }
+
+ /* Initialize controls */
+ v4l2_ctrl_handler_init(&pixelproc->ctrls,
+ ARRAY_SIZE(dcmipp_pixelproc_ctrls));
+
+ for (i = 0; i < ARRAY_SIZE(dcmipp_pixelproc_ctrls); i++)
+ v4l2_ctrl_new_custom(&pixelproc->ctrls,
+ &dcmipp_pixelproc_ctrls[i], NULL);
+
+ pixelproc->sd.ctrl_handler = &pixelproc->ctrls;
+ if (pixelproc->ctrls.error) {
+ ret = pixelproc->ctrls.error;
+ dev_err(pixelproc->dev, "control initialization error %d\n", ret);
+ kfree(pixelproc);
+ return ERR_PTR(ret);
+ }
+
+ /* Initialize ved and sd */
+ ret = dcmipp_ent_sd_register(&pixelproc->ved, &pixelproc->sd,
+ &dcmipp->v4l2_dev, entity_name,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER,
+ ARRAY_SIZE(pads_flag), pads_flag,
+ &dcmipp_pixelproc_int_ops,
+ &dcmipp_pixelproc_ops,
+ NULL, NULL);
+ if (ret) {
+ kfree(pixelproc);
+ return ERR_PTR(ret);
+ }
+
+ pixelproc->ved.dcmipp = dcmipp;
+
+ return &pixelproc->ved;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 11/13] media: stm32: dcmipp: add pixel-pipe support in bytecap
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (9 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 10/13] media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:44 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 12/13] media: stm32: dcmipp: rename bytecap into capture Alain Volmat
2026-06-02 13:14 ` [PATCH v2 13/13] media: stm32: dcmipp: instantiate & link stm32mp25 subdevs Alain Volmat
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
The dump pipe and pixel pipes capture part (tail of each pipe)
is different in that pixel pipes have a pixel packer capable
of generating various output format while the on dump pipe no
such manipulation is possible.
Still, all the buffer handling, format related manipulations
are all same hence both dump and pixel pipe capture part are
put together to avoid having large duplication of code.
This patch adds the pixel pipe capture within bytecap hence
name isn't modified and a further commit should rename the
file and probably function name to not only highlight byte
capture (aka dump pipe).
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 413 ++++++++++++++++++---
1 file changed, 352 insertions(+), 61 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index bd6f2d68df80..d4432a9fa0e2 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -25,27 +25,81 @@
#define DCMIPP_CMIER_P0ALL (DCMIPP_CMIER_P0VSYNCIE |\
DCMIPP_CMIER_P0FRAMEIE |\
DCMIPP_CMIER_P0OVRIE)
+#define DCMIPP_CMIER_P1FRAMEIE BIT(17)
+#define DCMIPP_CMIER_P1VSYNCIE BIT(18)
+#define DCMIPP_CMIER_P1OVRIE BIT(23)
+#define DCMIPP_CMIER_P1ALL (DCMIPP_CMIER_P1VSYNCIE |\
+ DCMIPP_CMIER_P1FRAMEIE |\
+ DCMIPP_CMIER_P1OVRIE)
+#define DCMIPP_CMIER_P2FRAMEIE BIT(25)
+#define DCMIPP_CMIER_P2VSYNCIE BIT(26)
+#define DCMIPP_CMIER_P2OVRIE BIT(31)
+#define DCMIPP_CMIER_P2ALL (DCMIPP_CMIER_P2VSYNCIE |\
+ DCMIPP_CMIER_P2FRAMEIE |\
+ DCMIPP_CMIER_P2OVRIE)
+#define DCMIPP_CMIER_PxALL(id) (((id) == 0) ? DCMIPP_CMIER_P0ALL : \
+ (((id) == 1) ? DCMIPP_CMIER_P1ALL : \
+ DCMIPP_CMIER_P2ALL))
#define DCMIPP_CMSR1 0x3f4
#define DCMIPP_CMSR2 0x3f8
#define DCMIPP_CMSR2_P0FRAMEF BIT(9)
#define DCMIPP_CMSR2_P0VSYNCF BIT(10)
#define DCMIPP_CMSR2_P0OVRF BIT(15)
+#define DCMIPP_CMSR2_P1FRAMEF BIT(17)
+#define DCMIPP_CMSR2_P1VSYNCF BIT(18)
+#define DCMIPP_CMSR2_P1OVRF BIT(23)
+#define DCMIPP_CMSR2_P2FRAMEF BIT(25)
+#define DCMIPP_CMSR2_P2VSYNCF BIT(26)
+#define DCMIPP_CMSR2_P2OVRF BIT(31)
+#define DCMIPP_CMSR2_PxFRAMEF(id) (((id) == 0) ? DCMIPP_CMSR2_P0FRAMEF :\
+ (((id) == 1) ? DCMIPP_CMSR2_P1FRAMEF :\
+ DCMIPP_CMSR2_P2FRAMEF))
+#define DCMIPP_CMSR2_PxVSYNCF(id) (((id) == 0) ? DCMIPP_CMSR2_P0VSYNCF :\
+ (((id) == 1) ? DCMIPP_CMSR2_P1VSYNCF :\
+ DCMIPP_CMSR2_P2VSYNCF))
+#define DCMIPP_CMSR2_PxOVRF(id) (((id) == 0) ? DCMIPP_CMSR2_P0OVRF :\
+ (((id) == 1) ? DCMIPP_CMSR2_P1OVRF :\
+ DCMIPP_CMSR2_P2OVRF))
#define DCMIPP_CMFCR 0x3fc
-#define DCMIPP_P0FSCR 0x404
-#define DCMIPP_P0FSCR_PIPEN BIT(31)
-#define DCMIPP_P0FCTCR 0x500
-#define DCMIPP_P0FCTCR_CPTREQ BIT(3)
+#define DCMIPP_PxFSCR(id) (0x404 + ((id) * 0x400))
+#define DCMIPP_PxFSCR_PIPEN BIT(31)
+#define DCMIPP_PxFCTCR(id) (0x500 + ((id) * 0x400))
+#define DCMIPP_PxFCTCR_CPTREQ BIT(3)
#define DCMIPP_P0DCCNTR 0x5b0
#define DCMIPP_P0DCLMTR 0x5b4
#define DCMIPP_P0DCLMTR_ENABLE BIT(31)
#define DCMIPP_P0DCLMTR_LIMIT_MASK GENMASK(23, 0)
-#define DCMIPP_P0PPM0AR1 0x5c4
-#define DCMIPP_P0SR 0x5f8
-#define DCMIPP_P0SR_CPTACT BIT(23)
+
+#define DCMIPP_PxPPM0AR1(id) (0x5c4 + ((id) * 0x400))
+#define DCMIPP_PxPPM0PR(id) (0x9cc + (((id) - 1) * 0x400))
+#define DCMIPP_P1PPM1AR1 0x9d4
+#define DCMIPP_P1PPM1PR 0x9dc
+#define DCMIPP_P1PPM2AR1 0x9e4
+
+#define DCMIPP_PxSR(id) (0x5f8 + ((id) * 0x400))
+#define DCMIPP_PxSR_CPTACT BIT(23)
+
+#define DCMIPP_PxPPCR(id) (0x9c0 + (((id) - 1) * 0x400))
+#define DCMIPP_PxPPCR_FORMAT_RGB888 0x0
+#define DCMIPP_PxPPCR_FORMAT_RGB565 0x1
+#define DCMIPP_PxPPCR_FORMAT_ARGB8888 0x2
+#define DCMIPP_PxPPCR_FORMAT_RGBA8888 0x3
+#define DCMIPP_PxPPCR_FORMAT_Y8 0x4
+#define DCMIPP_PxPPCR_FORMAT_YUV444 0x5
+#define DCMIPP_PxPPCR_FORMAT_YUYV 0x6
+#define DCMIPP_P1PPCR_FORMAT_NV61 0x7
+#define DCMIPP_P1PPCR_FORMAT_NV21 0x8
+#define DCMIPP_P1PPCR_FORMAT_YV12 0x9
+#define DCMIPP_PxPPCR_FORMAT_UYVY 0xa
+
+#define DCMIPP_PxPPCR_SWAPRB BIT(4)
struct dcmipp_bytecap_pix_map {
unsigned int code;
u32 pixelformat;
+ u32 plane_nb;
+ unsigned int ppcr_fmt;
+ unsigned int swap_uv;
};
#define PIXMAP_MBUS_PFMT(mbus, fmt) \
@@ -54,7 +108,7 @@ struct dcmipp_bytecap_pix_map {
.pixelformat = V4L2_PIX_FMT_##fmt \
}
-static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = {
+static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_dump_pix_map_list[] = {
PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565),
PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565),
PIXMAP_MBUS_PFMT(RGB888_1X24, RGB24),
@@ -89,24 +143,47 @@ static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = {
PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG),
};
-static const struct dcmipp_bytecap_pix_map *
-dcmipp_bytecap_pix_map_by_pixelformat(u32 pixelformat)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
- if (dcmipp_bytecap_pix_map_list[i].pixelformat == pixelformat)
- return &dcmipp_bytecap_pix_map_list[i];
+#define PIXMAP_MBUS_PIXEL_PFMT(mbus, fmt, nb_plane, pp_code, swap) \
+ { \
+ .code = MEDIA_BUS_FMT_##mbus, \
+ .pixelformat = V4L2_PIX_FMT_##fmt, \
+ .plane_nb = nb_plane, \
+ .ppcr_fmt = pp_code, \
+ .swap_uv = swap, \
}
- return NULL;
-}
+static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pixel_pix_map_list[] = {
+ /* Coplanar formats are supported on main & aux pipe */
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, RGB565, 1, DCMIPP_PxPPCR_FORMAT_RGB565, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YUYV, 1, DCMIPP_PxPPCR_FORMAT_YUYV, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YVYU, 1, DCMIPP_PxPPCR_FORMAT_YUYV, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, UYVY, 1, DCMIPP_PxPPCR_FORMAT_UYVY, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, VYUY, 1, DCMIPP_PxPPCR_FORMAT_UYVY, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, GREY, 1, DCMIPP_PxPPCR_FORMAT_Y8, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, RGB24, 1, DCMIPP_PxPPCR_FORMAT_RGB888, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, BGR24, 1, DCMIPP_PxPPCR_FORMAT_RGB888, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, ARGB32, 1, DCMIPP_PxPPCR_FORMAT_RGBA8888, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, ABGR32, 1, DCMIPP_PxPPCR_FORMAT_ARGB8888, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, RGBA32, 1, DCMIPP_PxPPCR_FORMAT_ARGB8888, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, BGRA32, 1, DCMIPP_PxPPCR_FORMAT_RGBA8888, 0),
+
+ /* Semiplanar & planar formats (plane_nb > 1) are only supported on main pipe */
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV12, 2, DCMIPP_P1PPCR_FORMAT_NV21, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV21, 2, DCMIPP_P1PPCR_FORMAT_NV21, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV16, 2, DCMIPP_P1PPCR_FORMAT_NV61, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV61, 2, DCMIPP_P1PPCR_FORMAT_NV61, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YUV420, 3, DCMIPP_P1PPCR_FORMAT_YV12, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YVU420, 3, DCMIPP_P1PPCR_FORMAT_YV12, 1),
+};
struct dcmipp_buf {
struct vb2_v4l2_buffer vb;
bool prepared;
dma_addr_t addr;
size_t size;
+ dma_addr_t addrs[3];
+ u32 strides[3];
+ u64 sizes[3];
struct list_head list;
};
@@ -140,6 +217,11 @@ struct dcmipp_bytecap_device {
void __iomem *regs;
+ u32 pipe_id;
+
+ const struct dcmipp_bytecap_pix_map *pix_map;
+ unsigned int pix_map_array_size;
+
u32 cmsr2;
struct {
@@ -155,6 +237,30 @@ struct dcmipp_bytecap_device {
} count;
};
+static const struct dcmipp_bytecap_pix_map *
+dcmipp_bytecap_pix_map_by_pixelformat(struct dcmipp_bytecap_device *vcap,
+ u32 pixelformat)
+{
+ for (unsigned int i = 0; i < vcap->pix_map_array_size; i++) {
+ if (vcap->pix_map[i].pixelformat == pixelformat)
+ return &vcap->pix_map[i];
+ }
+
+ return NULL;
+}
+
+static bool dcmipp_bytecap_is_format_valid(struct dcmipp_bytecap_device *vcap,
+ unsigned int pixelformat)
+{
+ const struct dcmipp_bytecap_pix_map *vpix =
+ dcmipp_bytecap_pix_map_by_pixelformat(vcap, pixelformat);
+
+ if (!vpix || (vpix->plane_nb > 1 && vcap->pipe_id != 1))
+ return false;
+
+ return true;
+}
+
static const struct v4l2_pix_format fmt_default = {
.width = DCMIPP_FMT_WIDTH_DEFAULT,
.height = DCMIPP_FMT_HEIGHT_DEFAULT,
@@ -168,6 +274,73 @@ static const struct v4l2_pix_format fmt_default = {
.xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
};
+static inline int hdw_pixel_alignment(u32 format)
+{
+ /* 16 bytes alignment required by hardware */
+ switch (format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_GREY:
+ return 4;/* 2^4 = 16 pixels = 16 bytes */
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ return 3;/* 2^3 = 8 pixels = 16 bytes */
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ return 4;/* 2^4 = 16 pixels = 48 bytes */
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_RGBA32:
+ case V4L2_PIX_FMT_BGRA32:
+ return 2;/* 2^2 = 4 pixels = 16 bytes */
+ default:
+ return 0;
+ }
+}
+
+static inline int frame_planes(dma_addr_t base_addr, dma_addr_t addrs[],
+ u32 strides[], u64 sizes[],
+ u32 width, u32 height, u32 format)
+{
+ const struct v4l2_format_info *info;
+
+ /* Only used by dump pipe hence addrs[0] is enough */
+ if (format == V4L2_PIX_FMT_JPEG) {
+ addrs[0] = base_addr;
+ return 0;
+ }
+
+ info = v4l2_format_info(format);
+ if (!info)
+ return -EINVAL;
+
+ /* Fill-in each plane information */
+ addrs[0] = base_addr;
+ strides[0] = width * info->bpp[0];
+ sizes[0] = strides[0] * height;
+
+ if (info->comp_planes > 1) {
+ addrs[1] = addrs[0] + sizes[0];
+ strides[1] = width * info->bpp[1] / info->hdiv;
+ sizes[1] = strides[1] * height / info->vdiv;
+ }
+
+ if (info->comp_planes > 2) {
+ addrs[2] = addrs[1] + sizes[1];
+ strides[2] = width * info->bpp[2] / info->hdiv;
+ sizes[2] = strides[2] * height / info->vdiv;
+ }
+
+ return 0;
+}
+
static int dcmipp_bytecap_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -192,19 +365,20 @@ static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
{
struct dcmipp_bytecap_device *vcap = video_drvdata(file);
struct v4l2_pix_format *format = &f->fmt.pix;
- const struct dcmipp_bytecap_pix_map *vpix;
+ /* Take into consideration the width constraint of the pixel packer */
+ unsigned int walign = (vcap->pipe_id != 0 ?
+ hdw_pixel_alignment(format->pixelformat) : 0);
u32 in_w, in_h;
/* Don't accept a pixelformat that is not on the table */
- vpix = dcmipp_bytecap_pix_map_by_pixelformat(format->pixelformat);
- if (!vpix)
+ if (!dcmipp_bytecap_is_format_valid(vcap, format->pixelformat))
format->pixelformat = fmt_default.pixelformat;
/* Adjust width & height */
in_w = format->width;
in_h = format->height;
v4l_bound_align_image(&format->width, DCMIPP_FRAME_MIN_WIDTH,
- DCMIPP_FRAME_MAX_WIDTH, 0, &format->height,
+ DCMIPP_FRAME_MAX_WIDTH, walign, &format->height,
DCMIPP_FRAME_MIN_HEIGHT, DCMIPP_FRAME_MAX_HEIGHT,
0, 0);
if (format->width != in_w || format->height != in_h)
@@ -262,7 +436,7 @@ static int dcmipp_bytecap_s_fmt_vid_cap(struct file *file, void *priv,
static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- const struct dcmipp_bytecap_pix_map *vpix;
+ struct dcmipp_bytecap_device *vcap = video_drvdata(file);
unsigned int index = f->index;
unsigned int i, prev_pixelformat = 0;
@@ -271,17 +445,20 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
* care of removing duplicated entries (due to support of both
* parallel & csi 16 bits formats
*/
- for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
- vpix = &dcmipp_bytecap_pix_map_list[i];
+ for (i = 0; i < vcap->pix_map_array_size; i++) {
+ /* Only main pipe supports (Semi)-planar formats */
+ if (vcap->pipe_id != 1 && vcap->pix_map[i].plane_nb > 1)
+ continue;
+
/* Skip formats not matching requested mbus code */
- if (f->mbus_code && vpix->code != f->mbus_code)
+ if (f->mbus_code && vcap->pix_map[i].code != f->mbus_code)
continue;
/* Skip duplicated pixelformat */
- if (vpix->pixelformat == prev_pixelformat)
+ if (vcap->pix_map[i].pixelformat == prev_pixelformat)
continue;
- prev_pixelformat = vpix->pixelformat;
+ prev_pixelformat = vcap->pix_map[i].pixelformat;
if (index == 0)
break;
@@ -289,10 +466,10 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
index--;
}
- if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list))
+ if (i == vcap->pix_map_array_size)
return -EINVAL;
- f->pixelformat = vpix->pixelformat;
+ f->pixelformat = vcap->pix_map[i].pixelformat;
return 0;
}
@@ -300,14 +477,14 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
static int dcmipp_bytecap_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
- const struct dcmipp_bytecap_pix_map *vpix;
+ struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+
if (fsize->index)
return -EINVAL;
/* Only accept code in the pix map table */
- vpix = dcmipp_bytecap_pix_map_by_pixelformat(fsize->pixel_format);
- if (!vpix)
+ if (!dcmipp_bytecap_is_format_valid(vcap, fsize->pixel_format))
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
@@ -355,14 +532,27 @@ static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap,
struct dcmipp_buf *buf)
{
/* Set buffer address */
- reg_write(vcap, DCMIPP_P0PPM0AR1, buf->addr);
+ reg_write(vcap, DCMIPP_PxPPM0AR1(vcap->pipe_id), buf->addrs[0]);
- /* Set buffer size */
- reg_write(vcap, DCMIPP_P0DCLMTR, DCMIPP_P0DCLMTR_ENABLE |
- ((buf->size / 4) & DCMIPP_P0DCLMTR_LIMIT_MASK));
+ if (vcap->pipe_id == 0) {
+ /* Set buffer size */
+ reg_write(vcap, DCMIPP_P0DCLMTR, DCMIPP_P0DCLMTR_ENABLE |
+ ((buf->size / 4) & DCMIPP_P0DCLMTR_LIMIT_MASK));
+ } else {
+ reg_write(vcap, DCMIPP_PxPPM0PR(vcap->pipe_id),
+ buf->strides[0]);
+
+ if (buf->addrs[1]) {
+ reg_write(vcap, DCMIPP_P1PPM1AR1, buf->addrs[1]);
+ reg_write(vcap, DCMIPP_P1PPM1PR, buf->strides[1]);
+ }
+
+ if (buf->addrs[2])
+ reg_write(vcap, DCMIPP_P1PPM2AR1, buf->addrs[2]);
+ }
/* Capture request */
- reg_set(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
+ reg_set(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
}
static void dcmipp_bytecap_all_buffers_done(struct dcmipp_bytecap_device *vcap,
@@ -421,8 +611,25 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
spin_lock_irq(&vcap->irqlock);
+ if (vcap->pipe_id != 0) {
+ const struct dcmipp_bytecap_pix_map *vpix =
+ dcmipp_bytecap_pix_map_by_pixelformat(vcap, vcap->format.pixelformat);
+ unsigned int ppcr = 0;
+
+ /*
+ * Configure the Pixel Packer
+ * vpix is guaranteed to be valid since pixelformat is validated
+ * in dcmipp_pixelcap_s_fmt_vid_cap function before
+ */
+ ppcr = vpix->ppcr_fmt;
+ if (vpix->swap_uv)
+ ppcr |= DCMIPP_PxPPCR_SWAPRB;
+
+ reg_write(vcap, DCMIPP_PxPPCR(vcap->pipe_id), ppcr);
+ }
+
/* Enable pipe at the end of programming */
- reg_set(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN);
+ reg_set(vcap, DCMIPP_PxFSCR(vcap->pipe_id), DCMIPP_PxFSCR_PIPEN);
/*
* vb2 framework guarantee that we have at least 'min_queued_buffers'
@@ -436,7 +643,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
/* Enable interruptions */
spin_lock(&vcap->vdev.v4l2_dev->lock);
- reg_set(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
+ reg_set(vcap, DCMIPP_CMIER, DCMIPP_CMIER_PxALL(vcap->pipe_id));
spin_unlock(&vcap->vdev.v4l2_dev->lock);
vcap->state = DCMIPP_RUNNING;
@@ -467,7 +674,7 @@ static void dcmipp_dump_status(struct dcmipp_bytecap_device *vcap)
struct device *dev = vcap->dev;
dev_dbg(dev, "[DCMIPP_PRSR] =%#10.8x\n", reg_read(vcap, DCMIPP_PRSR));
- dev_dbg(dev, "[DCMIPP_P0SR] =%#10.8x\n", reg_read(vcap, DCMIPP_P0SR));
+ dev_dbg(dev, "[DCMIPP_P0SR] =%#10.8x\n", reg_read(vcap, DCMIPP_PxSR(0)));
dev_dbg(dev, "[DCMIPP_P0DCCNTR]=%#10.8x\n",
reg_read(vcap, DCMIPP_P0DCCNTR));
dev_dbg(dev, "[DCMIPP_CMSR1] =%#10.8x\n", reg_read(vcap, DCMIPP_CMSR1));
@@ -494,25 +701,26 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
/* Disable interruptions */
spin_lock(&vcap->vdev.v4l2_dev->lock);
- reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
+ reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_PxALL(vcap->pipe_id));
spin_unlock(&vcap->vdev.v4l2_dev->lock);
/* Stop capture */
- reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
+ reg_clear(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
/* Wait until CPTACT become 0 */
- ret = readl_relaxed_poll_timeout(vcap->regs + DCMIPP_P0SR, status,
- !(status & DCMIPP_P0SR_CPTACT),
+ ret = readl_relaxed_poll_timeout(vcap->regs + DCMIPP_PxSR(vcap->pipe_id),
+ status,
+ !(status & DCMIPP_PxSR_CPTACT),
20 * USEC_PER_MSEC,
1000 * USEC_PER_MSEC);
if (ret)
dev_warn(vcap->dev, "Timeout when stopping\n");
/* Disable pipe */
- reg_clear(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN);
+ reg_clear(vcap, DCMIPP_PxFSCR(vcap->pipe_id), DCMIPP_PxFSCR_PIPEN);
/* Clear any pending interrupts */
- reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_P0ALL);
+ reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_PxALL(vcap->pipe_id));
spin_lock_irq(&vcap->irqlock);
@@ -525,7 +733,8 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
spin_unlock_irq(&vcap->irqlock);
- dcmipp_dump_status(vcap);
+ if (vcap->pipe_id == 0)
+ dcmipp_dump_status(vcap);
pm_runtime_put(vcap->dev);
@@ -541,7 +750,9 @@ static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
+ struct v4l2_pix_format *format = &vcap->format;
unsigned long size;
+ int ret;
size = vcap->format.sizeimage;
@@ -557,6 +768,24 @@ static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
/* Get memory addresses */
buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
+
+ ret = frame_planes(buf->addr,
+ buf->addrs, buf->strides, buf->sizes,
+ format->width, format->height,
+ format->pixelformat);
+ if (ret) {
+ dev_err(vcap->dev, "%s: Unsupported pixel format (%x)\n",
+ __func__, format->pixelformat);
+ return ret;
+ }
+
+ /* Check for 16 bytes alignment required by hardware */
+ WARN_ON(buf->addrs[0] & 15);
+ WARN_ON(buf->strides[0] & 15);
+ WARN_ON(buf->addrs[1] & 15);
+ WARN_ON(buf->strides[1] & 15);
+ WARN_ON(buf->addrs[2] & 15);
+
buf->prepared = true;
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
@@ -690,7 +919,7 @@ dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
* for next frame). On-going frame capture will continue until
* FRAME END but no further capture will be done.
*/
- reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
+ reg_clear(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
dev_dbg(vcap->dev, "Capture restart is deferred to next buffer queueing\n");
vcap->next = NULL;
@@ -707,7 +936,13 @@ dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
* This register is shadowed and will be taken into
* account on next VSYNC (start of next frame)
*/
- reg_write(vcap, DCMIPP_P0PPM0AR1, vcap->next->addr);
+ reg_write(vcap, DCMIPP_PxPPM0AR1(vcap->pipe_id), vcap->next->addrs[0]);
+ if (vcap->pipe_id == 1) {
+ if (vcap->next->addrs[1])
+ reg_write(vcap, DCMIPP_P1PPM1AR1, vcap->next->addrs[1]);
+ if (vcap->next->addrs[2])
+ reg_write(vcap, DCMIPP_P1PPM2AR1, vcap->next->addrs[2]);
+ }
dev_dbg(vcap->dev, "Write [%d] %p phy=%pad\n",
vcap->next->vb.vb2_buf.index, vcap->next, &vcap->next->addr);
}
@@ -743,29 +978,39 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
{
struct dcmipp_bytecap_device *vcap =
container_of(arg, struct dcmipp_bytecap_device, ved);
+ u32 cmsr2_pxframef;
+ u32 cmsr2_pxvsyncf;
+ u32 cmsr2_pxovrf;
size_t bytesused = 0;
spin_lock_irq(&vcap->irqlock);
+ cmsr2_pxovrf = DCMIPP_CMSR2_PxOVRF(vcap->pipe_id);
+ cmsr2_pxvsyncf = DCMIPP_CMSR2_PxVSYNCF(vcap->pipe_id);
+ cmsr2_pxframef = DCMIPP_CMSR2_PxFRAMEF(vcap->pipe_id);
+
/*
* If we have an overrun, a frame-end will probably not be generated,
* in that case the active buffer will be recycled as next buffer by
* the VSYNC handler
*/
- if (vcap->cmsr2 & DCMIPP_CMSR2_P0OVRF) {
+ if (vcap->cmsr2 & cmsr2_pxovrf) {
vcap->count.errors++;
vcap->count.overrun++;
}
- if (vcap->cmsr2 & DCMIPP_CMSR2_P0FRAMEF) {
+ if (vcap->cmsr2 & cmsr2_pxframef) {
vcap->count.frame++;
/* Read captured buffer size */
- bytesused = reg_read(vcap, DCMIPP_P0DCCNTR);
+ if (vcap->pipe_id == 0)
+ bytesused = reg_read(vcap, DCMIPP_P0DCCNTR);
+ else
+ bytesused = vcap->format.sizeimage;
dcmipp_bytecap_process_frame(vcap, bytesused);
}
- if (vcap->cmsr2 & DCMIPP_CMSR2_P0VSYNCF) {
+ if (vcap->cmsr2 & cmsr2_pxvsyncf) {
vcap->count.vsync++;
if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) {
vcap->count.underrun++;
@@ -797,7 +1042,7 @@ static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg)
struct dcmipp_ent_device *ved = arg;
/* Store interrupt status register */
- vcap->cmsr2 = ved->cmsr2 & DCMIPP_CMIER_P0ALL;
+ vcap->cmsr2 = ved->cmsr2 & DCMIPP_CMIER_PxALL(vcap->pipe_id);
if (!vcap->cmsr2)
return IRQ_HANDLED;
vcap->count.it++;
@@ -826,6 +1071,26 @@ static int dcmipp_bytecap_link_validate(struct media_link *link)
if (ret < 0)
return 0;
+ /* On pixel pipes there can be alignment constraints */
+ if (vcap->pipe_id != 0) {
+ u32 width_aligned;
+ /*
+ * Depending on the format & pixelpacker constraints, vcap width is
+ * different from mbus width. Compute expected vcap width based on
+ * mbus width
+ */
+ width_aligned = round_up(source_fmt.format.width,
+ 1 << hdw_pixel_alignment(vcap->format.pixelformat));
+
+ if (width_aligned != vcap->format.width ||
+ source_fmt.format.height != vcap->format.height) {
+ dev_err(vcap->dev, "Wrong width or height %ux%u (%ux%u expected)\n",
+ vcap->format.width, vcap->format.height,
+ width_aligned, source_fmt.format.height);
+ return -EINVAL;
+ }
+ }
+
if (source_fmt.format.width != vcap->format.width ||
source_fmt.format.height != vcap->format.height) {
dev_err(vcap->dev, "Wrong width or height %ux%u (%ux%u expected)\n",
@@ -834,15 +1099,13 @@ static int dcmipp_bytecap_link_validate(struct media_link *link)
return -EINVAL;
}
- for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
- if (dcmipp_bytecap_pix_map_list[i].pixelformat ==
- vcap->format.pixelformat &&
- dcmipp_bytecap_pix_map_list[i].code ==
- source_fmt.format.code)
+ for (i = 0; i < vcap->pix_map_array_size; i++) {
+ if (vcap->pix_map[i].pixelformat == vcap->format.pixelformat &&
+ vcap->pix_map[i].code == source_fmt.format.code)
break;
}
- if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) {
+ if (i == vcap->pix_map_array_size) {
dev_err(vcap->dev, "mbus code 0x%x do not match capture device format (0x%x)\n",
vcap->format.pixelformat, source_fmt.format.code);
return -EINVAL;
@@ -855,6 +1118,18 @@ static const struct media_entity_operations dcmipp_bytecap_entity_ops = {
.link_validate = dcmipp_bytecap_link_validate,
};
+static int dcmipp_name_to_pipe_id(const char *name)
+{
+ if (strstr(name, "dump"))
+ return 0;
+ else if (strstr(name, "main"))
+ return 1;
+ else if (strstr(name, "aux"))
+ return 2;
+ else
+ return -EINVAL;
+}
+
struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
struct dcmipp_device *dcmipp)
{
@@ -870,6 +1145,22 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
if (!vcap)
return ERR_PTR(-ENOMEM);
+ /* Retrieve the pipe_id */
+ vcap->pipe_id = dcmipp_name_to_pipe_id(entity_name);
+ if (vcap->pipe_id < 0) {
+ dev_err(dev, "failed to retrieve pipe_id\n");
+ goto err_free_vcap;
+ }
+
+ /* Initialize supported format table format */
+ if (vcap->pipe_id == 0) {
+ vcap->pix_map = dcmipp_bytecap_dump_pix_map_list;
+ vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_bytecap_dump_pix_map_list);
+ } else {
+ vcap->pix_map = dcmipp_bytecap_pixel_pix_map_list;
+ vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_bytecap_pixel_pix_map_list);
+ }
+
/* Allocate the pads */
vcap->ved.pads = dcmipp_pads_init(1, &pad_flag);
if (IS_ERR(vcap->ved.pads)) {
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 12/13] media: stm32: dcmipp: rename bytecap into capture
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (10 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 11/13] media: stm32: dcmipp: add pixel-pipe support in bytecap Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:39 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 13/13] media: stm32: dcmipp: instantiate & link stm32mp25 subdevs Alain Volmat
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
Since the bytecap video driver is now handling all
capture (byte & pixel), rename the file and structs
*into capture. This is done by:
- renaming of dcmipp-bytecap.c into dcmipp-capture.c
- replace of dcmipp_bytecap strings into dcmipp_capture
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../media/platform/st/stm32/stm32-dcmipp/Makefile | 2 +-
.../{dcmipp-bytecap.c => dcmipp-capture.c} | 186 ++++++++++-----------
.../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 4 +-
.../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 8 +-
4 files changed, 100 insertions(+), 100 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
index 7178934bb116..e35d45a0aca2 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o
+stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-capture.o
stm32-dcmipp-y += dcmipp-pixelcommon.o dcmipp-isp.o dcmipp-pixelproc.o
obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c
similarity index 86%
rename from drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
rename to drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c
index d4432a9fa0e2..a51f5b92be5b 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c
@@ -94,7 +94,7 @@
#define DCMIPP_PxPPCR_SWAPRB BIT(4)
-struct dcmipp_bytecap_pix_map {
+struct dcmipp_capture_pix_map {
unsigned int code;
u32 pixelformat;
u32 plane_nb;
@@ -108,7 +108,7 @@ struct dcmipp_bytecap_pix_map {
.pixelformat = V4L2_PIX_FMT_##fmt \
}
-static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_dump_pix_map_list[] = {
+static const struct dcmipp_capture_pix_map dcmipp_capture_dump_pix_map_list[] = {
PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565),
PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565),
PIXMAP_MBUS_PFMT(RGB888_1X24, RGB24),
@@ -152,7 +152,7 @@ static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_dump_pix_map_list[] =
.swap_uv = swap, \
}
-static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pixel_pix_map_list[] = {
+static const struct dcmipp_capture_pix_map dcmipp_capture_pixel_pix_map_list[] = {
/* Coplanar formats are supported on main & aux pipe */
PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, RGB565, 1, DCMIPP_PxPPCR_FORMAT_RGB565, 0),
PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YUYV, 1, DCMIPP_PxPPCR_FORMAT_YUYV, 0),
@@ -187,7 +187,7 @@ struct dcmipp_buf {
struct list_head list;
};
-struct dcmipp_bytecap_device {
+struct dcmipp_capture_device {
struct dcmipp_ent_device ved;
struct video_device vdev;
struct device *dev;
@@ -219,7 +219,7 @@ struct dcmipp_bytecap_device {
u32 pipe_id;
- const struct dcmipp_bytecap_pix_map *pix_map;
+ const struct dcmipp_capture_pix_map *pix_map;
unsigned int pix_map_array_size;
u32 cmsr2;
@@ -237,8 +237,8 @@ struct dcmipp_bytecap_device {
} count;
};
-static const struct dcmipp_bytecap_pix_map *
-dcmipp_bytecap_pix_map_by_pixelformat(struct dcmipp_bytecap_device *vcap,
+static const struct dcmipp_capture_pix_map *
+dcmipp_capture_pix_map_by_pixelformat(struct dcmipp_capture_device *vcap,
u32 pixelformat)
{
for (unsigned int i = 0; i < vcap->pix_map_array_size; i++) {
@@ -249,11 +249,11 @@ dcmipp_bytecap_pix_map_by_pixelformat(struct dcmipp_bytecap_device *vcap,
return NULL;
}
-static bool dcmipp_bytecap_is_format_valid(struct dcmipp_bytecap_device *vcap,
+static bool dcmipp_capture_is_format_valid(struct dcmipp_capture_device *vcap,
unsigned int pixelformat)
{
- const struct dcmipp_bytecap_pix_map *vpix =
- dcmipp_bytecap_pix_map_by_pixelformat(vcap, pixelformat);
+ const struct dcmipp_capture_pix_map *vpix =
+ dcmipp_capture_pix_map_by_pixelformat(vcap, pixelformat);
if (!vpix || (vpix->plane_nb > 1 && vcap->pipe_id != 1))
return false;
@@ -341,7 +341,7 @@ static inline int frame_planes(dma_addr_t base_addr, dma_addr_t addrs[],
return 0;
}
-static int dcmipp_bytecap_querycap(struct file *file, void *priv,
+static int dcmipp_capture_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strscpy(cap->driver, DCMIPP_PDEV_NAME, sizeof(cap->driver));
@@ -350,20 +350,20 @@ static int dcmipp_bytecap_querycap(struct file *file, void *priv,
return 0;
}
-static int dcmipp_bytecap_g_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
f->fmt.pix = vcap->format;
return 0;
}
-static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
struct v4l2_pix_format *format = &f->fmt.pix;
/* Take into consideration the width constraint of the pixel packer */
unsigned int walign = (vcap->pipe_id != 0 ?
@@ -371,7 +371,7 @@ static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
u32 in_w, in_h;
/* Don't accept a pixelformat that is not on the table */
- if (!dcmipp_bytecap_is_format_valid(vcap, format->pixelformat))
+ if (!dcmipp_capture_is_format_valid(vcap, format->pixelformat))
format->pixelformat = fmt_default.pixelformat;
/* Adjust width & height */
@@ -401,17 +401,17 @@ static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int dcmipp_bytecap_s_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
int ret;
/* Do not change the format while stream is on */
if (vb2_is_busy(&vcap->queue))
return -EBUSY;
- ret = dcmipp_bytecap_try_fmt_vid_cap(file, priv, f);
+ ret = dcmipp_capture_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
@@ -433,10 +433,10 @@ static int dcmipp_bytecap_s_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
unsigned int index = f->index;
unsigned int i, prev_pixelformat = 0;
@@ -474,17 +474,17 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int dcmipp_bytecap_enum_framesizes(struct file *file, void *fh,
+static int dcmipp_capture_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
if (fsize->index)
return -EINVAL;
/* Only accept code in the pix map table */
- if (!dcmipp_bytecap_is_format_valid(vcap, fsize->pixel_format))
+ if (!dcmipp_capture_is_format_valid(vcap, fsize->pixel_format))
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
@@ -498,7 +498,7 @@ static int dcmipp_bytecap_enum_framesizes(struct file *file, void *fh,
return 0;
}
-static const struct v4l2_file_operations dcmipp_bytecap_fops = {
+static const struct v4l2_file_operations dcmipp_capture_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
@@ -508,14 +508,14 @@ static const struct v4l2_file_operations dcmipp_bytecap_fops = {
.mmap = vb2_fop_mmap,
};
-static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = {
- .vidioc_querycap = dcmipp_bytecap_querycap,
+static const struct v4l2_ioctl_ops dcmipp_capture_ioctl_ops = {
+ .vidioc_querycap = dcmipp_capture_querycap,
- .vidioc_g_fmt_vid_cap = dcmipp_bytecap_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = dcmipp_bytecap_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = dcmipp_bytecap_try_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = dcmipp_bytecap_enum_fmt_vid_cap,
- .vidioc_enum_framesizes = dcmipp_bytecap_enum_framesizes,
+ .vidioc_g_fmt_vid_cap = dcmipp_capture_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = dcmipp_capture_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = dcmipp_capture_try_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = dcmipp_capture_enum_fmt_vid_cap,
+ .vidioc_enum_framesizes = dcmipp_capture_enum_framesizes,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
@@ -528,7 +528,7 @@ static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = {
.vidioc_streamoff = vb2_ioctl_streamoff,
};
-static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_start_capture(struct dcmipp_capture_device *vcap,
struct dcmipp_buf *buf)
{
/* Set buffer address */
@@ -555,7 +555,7 @@ static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap,
reg_set(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
}
-static void dcmipp_bytecap_all_buffers_done(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_capture_all_buffers_done(struct dcmipp_capture_device *vcap,
enum vb2_buffer_state state)
{
struct dcmipp_buf *buf, *node;
@@ -566,10 +566,10 @@ static void dcmipp_bytecap_all_buffers_done(struct dcmipp_bytecap_device *vcap,
}
}
-static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
+static int dcmipp_capture_start_streaming(struct vb2_queue *vq,
unsigned int count)
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vq);
struct media_entity *entity = &vcap->vdev.entity;
struct dcmipp_buf *buf;
struct media_pad *pad;
@@ -580,7 +580,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
/*
* Get source subdev - since link is IMMUTABLE, pointer is cached
- * within the dcmipp_bytecap_device structure
+ * within the dcmipp_capture_device structure
*/
if (!vcap->s_subdev) {
pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
@@ -612,8 +612,8 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
spin_lock_irq(&vcap->irqlock);
if (vcap->pipe_id != 0) {
- const struct dcmipp_bytecap_pix_map *vpix =
- dcmipp_bytecap_pix_map_by_pixelformat(vcap, vcap->format.pixelformat);
+ const struct dcmipp_capture_pix_map *vpix =
+ dcmipp_capture_pix_map_by_pixelformat(vcap, vcap->format.pixelformat);
unsigned int ppcr = 0;
/*
@@ -662,14 +662,14 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
* Return all buffers to vb2 in QUEUED state.
* This will give ownership back to userspace
*/
- dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_QUEUED);
+ dcmipp_capture_all_buffers_done(vcap, VB2_BUF_STATE_QUEUED);
vcap->active = NULL;
spin_unlock_irq(&vcap->irqlock);
return ret;
}
-static void dcmipp_dump_status(struct dcmipp_bytecap_device *vcap)
+static void dcmipp_dump_status(struct dcmipp_capture_device *vcap)
{
struct device *dev = vcap->dev;
@@ -685,9 +685,9 @@ static void dcmipp_dump_status(struct dcmipp_bytecap_device *vcap)
* Stop the stream engine. Any remaining buffers in the stream queue are
* dequeued and passed on to the vb2 framework marked as STATE_ERROR.
*/
-static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
+static void dcmipp_capture_stop_streaming(struct vb2_queue *vq)
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vq);
int ret;
u32 status;
@@ -725,7 +725,7 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
spin_lock_irq(&vcap->irqlock);
/* Return all queued buffers to vb2 in ERROR state */
- dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_ERROR);
+ dcmipp_capture_all_buffers_done(vcap, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&vcap->buffers);
vcap->active = NULL;
@@ -745,9 +745,9 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
vcap->count.underrun, vcap->count.buffers);
}
-static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
+static int dcmipp_capture_buf_prepare(struct vb2_buffer *vb)
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
struct v4l2_pix_format *format = &vcap->format;
@@ -797,9 +797,9 @@ static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
return 0;
}
-static void dcmipp_bytecap_buf_queue(struct vb2_buffer *vb2_buf)
+static void dcmipp_capture_buf_queue(struct vb2_buffer *vb2_buf)
{
- struct dcmipp_bytecap_device *vcap =
+ struct dcmipp_capture_device *vcap =
vb2_get_drv_priv(vb2_buf->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2_buf);
struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
@@ -823,13 +823,13 @@ static void dcmipp_bytecap_buf_queue(struct vb2_buffer *vb2_buf)
spin_unlock_irq(&vcap->irqlock);
}
-static int dcmipp_bytecap_queue_setup(struct vb2_queue *vq,
+static int dcmipp_capture_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers,
unsigned int *nplanes,
unsigned int sizes[],
struct device *alloc_devs[])
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vq);
unsigned int size;
size = vcap->format.sizeimage;
@@ -847,7 +847,7 @@ static int dcmipp_bytecap_queue_setup(struct vb2_queue *vq,
return 0;
}
-static int dcmipp_bytecap_buf_init(struct vb2_buffer *vb)
+static int dcmipp_capture_buf_init(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
@@ -857,19 +857,19 @@ static int dcmipp_bytecap_buf_init(struct vb2_buffer *vb)
return 0;
}
-static const struct vb2_ops dcmipp_bytecap_qops = {
- .start_streaming = dcmipp_bytecap_start_streaming,
- .stop_streaming = dcmipp_bytecap_stop_streaming,
- .buf_init = dcmipp_bytecap_buf_init,
- .buf_prepare = dcmipp_bytecap_buf_prepare,
- .buf_queue = dcmipp_bytecap_buf_queue,
- .queue_setup = dcmipp_bytecap_queue_setup,
+static const struct vb2_ops dcmipp_capture_qops = {
+ .start_streaming = dcmipp_capture_start_streaming,
+ .stop_streaming = dcmipp_capture_stop_streaming,
+ .buf_init = dcmipp_capture_buf_init,
+ .buf_prepare = dcmipp_capture_buf_prepare,
+ .buf_queue = dcmipp_capture_buf_queue,
+ .queue_setup = dcmipp_capture_queue_setup,
};
-static void dcmipp_bytecap_release(struct video_device *vdev)
+static void dcmipp_capture_release(struct video_device *vdev)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(vdev, struct dcmipp_bytecap_device, vdev);
+ struct dcmipp_capture_device *vcap =
+ container_of(vdev, struct dcmipp_capture_device, vdev);
dcmipp_pads_cleanup(vcap->ved.pads);
mutex_destroy(&vcap->lock);
@@ -877,16 +877,16 @@ static void dcmipp_bytecap_release(struct video_device *vdev)
kfree(vcap);
}
-void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved)
+void dcmipp_capture_ent_release(struct dcmipp_ent_device *ved)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(ved, struct dcmipp_bytecap_device, ved);
+ struct dcmipp_capture_device *vcap =
+ container_of(ved, struct dcmipp_capture_device, ved);
media_entity_cleanup(ved->ent);
vb2_video_unregister_device(&vcap->vdev);
}
-static void dcmipp_buffer_done(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_buffer_done(struct dcmipp_capture_device *vcap,
struct dcmipp_buf *buf,
size_t bytesused,
int err)
@@ -910,7 +910,7 @@ static void dcmipp_buffer_done(struct dcmipp_bytecap_device *vcap,
/* irqlock must be held */
static void
-dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
+dcmipp_capture_set_next_frame_or_stop(struct dcmipp_capture_device *vcap)
{
if (!vcap->next && list_is_singular(&vcap->buffers)) {
/*
@@ -948,7 +948,7 @@ dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
}
/* irqlock must be held */
-static void dcmipp_bytecap_process_frame(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_capture_process_frame(struct dcmipp_capture_device *vcap,
size_t bytesused)
{
int err = 0;
@@ -974,10 +974,10 @@ static void dcmipp_bytecap_process_frame(struct dcmipp_bytecap_device *vcap,
vcap->active = NULL;
}
-static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
+static irqreturn_t dcmipp_capture_irq_thread(int irq, void *arg)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(arg, struct dcmipp_bytecap_device, ved);
+ struct dcmipp_capture_device *vcap =
+ container_of(arg, struct dcmipp_capture_device, ved);
u32 cmsr2_pxframef;
u32 cmsr2_pxvsyncf;
u32 cmsr2_pxovrf;
@@ -1007,7 +1007,7 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
bytesused = reg_read(vcap, DCMIPP_P0DCCNTR);
else
bytesused = vcap->format.sizeimage;
- dcmipp_bytecap_process_frame(vcap, bytesused);
+ dcmipp_capture_process_frame(vcap, bytesused);
}
if (vcap->cmsr2 & cmsr2_pxvsyncf) {
@@ -1027,7 +1027,7 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
* active (but not used) buffer and put it back into next.
*/
swap(vcap->active, vcap->next);
- dcmipp_bytecap_set_next_frame_or_stop(vcap);
+ dcmipp_capture_set_next_frame_or_stop(vcap);
}
out:
@@ -1035,10 +1035,10 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
return IRQ_HANDLED;
}
-static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg)
+static irqreturn_t dcmipp_capture_irq_callback(int irq, void *arg)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(arg, struct dcmipp_bytecap_device, ved);
+ struct dcmipp_capture_device *vcap =
+ container_of(arg, struct dcmipp_capture_device, ved);
struct dcmipp_ent_device *ved = arg;
/* Store interrupt status register */
@@ -1053,12 +1053,12 @@ static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg)
return IRQ_WAKE_THREAD;
}
-static int dcmipp_bytecap_link_validate(struct media_link *link)
+static int dcmipp_capture_link_validate(struct media_link *link)
{
struct media_entity *entity = link->sink->entity;
struct video_device *vd = media_entity_to_video_device(entity);
- struct dcmipp_bytecap_device *vcap = container_of(vd,
- struct dcmipp_bytecap_device, vdev);
+ struct dcmipp_capture_device *vcap = container_of(vd,
+ struct dcmipp_capture_device, vdev);
struct v4l2_subdev *source_sd =
media_entity_to_v4l2_subdev(link->source->entity);
struct v4l2_subdev_format source_fmt = {
@@ -1114,8 +1114,8 @@ static int dcmipp_bytecap_link_validate(struct media_link *link)
return 0;
}
-static const struct media_entity_operations dcmipp_bytecap_entity_ops = {
- .link_validate = dcmipp_bytecap_link_validate,
+static const struct media_entity_operations dcmipp_capture_entity_ops = {
+ .link_validate = dcmipp_capture_link_validate,
};
static int dcmipp_name_to_pipe_id(const char *name)
@@ -1130,10 +1130,10 @@ static int dcmipp_name_to_pipe_id(const char *name)
return -EINVAL;
}
-struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
+struct dcmipp_ent_device *dcmipp_capture_ent_init(const char *entity_name,
struct dcmipp_device *dcmipp)
{
- struct dcmipp_bytecap_device *vcap;
+ struct dcmipp_capture_device *vcap;
struct device *dev = dcmipp->dev;
struct video_device *vdev;
struct vb2_queue *q;
@@ -1154,11 +1154,11 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
/* Initialize supported format table format */
if (vcap->pipe_id == 0) {
- vcap->pix_map = dcmipp_bytecap_dump_pix_map_list;
- vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_bytecap_dump_pix_map_list);
+ vcap->pix_map = dcmipp_capture_dump_pix_map_list;
+ vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_capture_dump_pix_map_list);
} else {
- vcap->pix_map = dcmipp_bytecap_pixel_pix_map_list;
- vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_bytecap_pixel_pix_map_list);
+ vcap->pix_map = dcmipp_capture_pixel_pix_map_list;
+ vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_capture_pixel_pix_map_list);
}
/* Allocate the pads */
@@ -1173,7 +1173,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
/* Initialize the media entity */
vcap->vdev.entity.name = entity_name;
vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
- vcap->vdev.entity.ops = &dcmipp_bytecap_entity_ops;
+ vcap->vdev.entity.ops = &dcmipp_capture_entity_ops;
ret = media_entity_pads_init(&vcap->vdev.entity, 1, vcap->ved.pads);
if (ret)
goto err_clean_pads;
@@ -1188,7 +1188,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
q->lock = &vcap->lock;
q->drv_priv = vcap;
q->buf_struct_size = sizeof(struct dcmipp_buf);
- q->ops = &dcmipp_bytecap_qops;
+ q->ops = &dcmipp_capture_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_queued_buffers = 1;
@@ -1217,8 +1217,8 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
/* Fill the dcmipp_ent_device struct */
vcap->ved.ent = &vcap->vdev.entity;
- vcap->ved.handler = dcmipp_bytecap_irq_callback;
- vcap->ved.thread_fn = dcmipp_bytecap_irq_thread;
+ vcap->ved.handler = dcmipp_capture_irq_callback;
+ vcap->ved.thread_fn = dcmipp_capture_irq_thread;
vcap->dev = dev;
vcap->regs = dcmipp->regs;
@@ -1226,9 +1226,9 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
vdev = &vcap->vdev;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_IO_MC;
- vdev->release = dcmipp_bytecap_release;
- vdev->fops = &dcmipp_bytecap_fops;
- vdev->ioctl_ops = &dcmipp_bytecap_ioctl_ops;
+ vdev->release = dcmipp_capture_release;
+ vdev->fops = &dcmipp_capture_fops;
+ vdev->ioctl_ops = &dcmipp_capture_ioctl_ops;
vdev->lock = &vcap->lock;
vdev->queue = q;
vdev->v4l2_dev = &dcmipp->v4l2_dev;
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index 8f41473605aa..c0f7ed3acbfc 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -279,9 +279,9 @@ struct dcmipp_ent_device *
dcmipp_byteproc_ent_init(const char *entity_name,
struct dcmipp_device *dcmipp);
void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved);
-struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
+struct dcmipp_ent_device *dcmipp_capture_ent_init(const char *entity_name,
struct dcmipp_device *dcmipp);
-void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved);
+void dcmipp_capture_ent_release(struct dcmipp_ent_device *ved);
struct dcmipp_ent_device *dcmipp_isp_ent_init(const char *entity_name,
struct dcmipp_device *dcmipp);
void dcmipp_isp_ent_release(struct dcmipp_ent_device *ved);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index 9aba16096d28..a52b3b0e3c37 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -50,8 +50,8 @@ static const struct dcmipp_ent_config stm32mp13_ent_config[] = {
},
{
.name = "dcmipp_dump_capture",
- .init = dcmipp_bytecap_ent_init,
- .release = dcmipp_bytecap_ent_release,
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
},
};
@@ -88,8 +88,8 @@ static const struct dcmipp_ent_config stm32mp25_ent_config[] = {
},
{
.name = "dcmipp_dump_capture",
- .init = dcmipp_bytecap_ent_init,
- .release = dcmipp_bytecap_ent_release,
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
},
};
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 13/13] media: stm32: dcmipp: instantiate & link stm32mp25 subdevs
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
` (11 preceding siblings ...)
2026-06-02 13:14 ` [PATCH v2 12/13] media: stm32: dcmipp: rename bytecap into capture Alain Volmat
@ 2026-06-02 13:14 ` Alain Volmat
2026-06-02 13:39 ` sashiko-bot
12 siblings, 1 reply; 23+ messages in thread
From: Alain Volmat @ 2026-06-02 13:14 UTC (permalink / raw)
To: Hugues Fruchet, Alain Volmat, Mauro Carvalho Chehab,
Maxime Coquelin, Alexandre Torgue
Cc: linux-media, linux-stm32, linux-arm-kernel, linux-kernel,
sakari.ailus
Add topology of the two pixel pipes (main & aux) of the stm32mp25.
Do not make the link from dcmipp_input immutable and enabled by
default since not all pipes are always used together so when a pipeline
is not being used its link should be disconnected to allow proper
pipeline check.
Not doing this would most probably lead to pipeline start failure due
to incompatible pads configurations on the unused pipe.
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
---
.../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 42 ++++++++++++++++++++--
1 file changed, 40 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index a52b3b0e3c37..44440f8ea9f5 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -75,6 +75,11 @@ static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = {
.hw_revision = DCMIPP_STM32MP13_VERR
};
+#define ID_MAIN_ISP 3
+#define ID_MAIN_POSTPROC 4
+#define ID_MAIN_CAPTURE 5
+#define ID_AUX_POSTPROC 6
+#define ID_AUX_CAPTURE 7
static const struct dcmipp_ent_config stm32mp25_ent_config[] = {
{
.name = "dcmipp_input",
@@ -91,13 +96,46 @@ static const struct dcmipp_ent_config stm32mp25_ent_config[] = {
.init = dcmipp_capture_ent_init,
.release = dcmipp_capture_ent_release,
},
+ {
+ .name = "dcmipp_main_isp",
+ .init = dcmipp_isp_ent_init,
+ .release = dcmipp_isp_ent_release,
+ },
+ {
+ .name = "dcmipp_main_postproc",
+ .init = dcmipp_pixelproc_ent_init,
+ .release = dcmipp_pixelproc_ent_release,
+ },
+ {
+ .name = "dcmipp_main_capture",
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
+ },
+ {
+ .name = "dcmipp_aux_postproc",
+ .init = dcmipp_pixelproc_ent_init,
+ .release = dcmipp_pixelproc_ent_release,
+ },
+ {
+ .name = "dcmipp_aux_capture",
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
+ },
};
static const struct dcmipp_ent_link stm32mp25_ent_links[] = {
- DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, 0),
DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0,
MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_INPUT, 2, ID_MAIN_ISP, 0, 0),
+ DCMIPP_ENT_LINK(ID_MAIN_ISP, 1, ID_MAIN_POSTPROC, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_MAIN_ISP, 2, ID_AUX_POSTPROC, 0, 0),
+ DCMIPP_ENT_LINK(ID_MAIN_POSTPROC, 1, ID_MAIN_CAPTURE, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_INPUT, 3, ID_AUX_POSTPROC, 0, 0),
+ DCMIPP_ENT_LINK(ID_AUX_POSTPROC, 1, ID_AUX_CAPTURE, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
};
#define DCMIPP_STM32MP25_VERR 0x30
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 06/13] media: stm32: dcmipp: configure csi input of all pipes on stm32mp25
2026-06-02 13:14 ` [PATCH v2 06/13] media: stm32: dcmipp: configure csi input of all pipes on stm32mp25 Alain Volmat
@ 2026-06-02 13:29 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:29 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Incomplete multi-pad format propagation and hardware configuration logic.
--
commit 11526b79984d515f0895343b0327ef2d55f0975d
Author: Alain Volmat <alain.volmat@foss.st.com>
media: stm32: dcmipp: configure csi input of all pipes on stm32mp25
This patch adds two additional pixel pipes (source pads 2 and 3) to the
dcmipp-input subdevice for the STM32MP25. It modifies the hardware register
configuration to allow routing input streams to all available pipes.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
> index 75421035cfad5..3fc859f1cb14a 100644
> --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
[ ... ]
> @@ -383,7 +389,8 @@ static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp,
> }
>
> static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp,
> - struct v4l2_subdev_state *state)
> + struct v4l2_subdev_state *state,
> + u32 pad)
> {
> const struct dcmipp_inp_pix_map *vpix;
> struct v4l2_mbus_framefmt *sink_fmt;
[Severity: High]
Since this function now accepts a pad argument, should it be updated to use it
when retrieving the source format?
Currently, dcmipp_inp_configure_csi() hardcodes the format lookup to pad 1:
/* Get format information */
sink_fmt = v4l2_subdev_state_get_format(state, 0);
src_fmt = v4l2_subdev_state_get_format(state, 1);
If a user configures a pipeline using pad 2 or 3, the driver will incorrectly
configure the hardware using pad 1's format parameters. For instance, if pad 1
is left at a default JPEG format, attempting to enable streams on pad 2 could
fail with "JPEG only available on pipe 0" because the driver evaluates the
format of pad 1 instead of pad 2.
[ ... ]
> @@ -533,8 +554,10 @@ struct dcmipp_ent_device *dcmipp_inp_ent_init(const char *entity_name,
> struct dcmipp_inp_device *inp;
> const unsigned long pads_flag[] = {
> MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
> + MEDIA_PAD_FL_SOURCE, MEDIA_PAD_FL_SOURCE,
> };
[Severity: High]
With the addition of these new source pads (pads 2 and 3), does
dcmipp_inp_set_fmt() also need to be updated to propagate the sink format
to all source pads?
Currently, dcmipp_inp_set_fmt() only propagates the format to pad 1:
/* When setting the sink format, report that format on the src pad */
if (IS_SINK(fmt->pad)) {
mf = v4l2_subdev_state_get_format(sd_state, 1);
*mf = fmt->format;
dcmipp_inp_adjust_fmt(inp, mf, 1);
}
This could leave pads 2 and 3 uninitialized or in a stale state, violating
V4L2 subdev API expectations and potentially causing
v4l2_subdev_link_validate() to fail when setting up a pipeline through the
new pipes.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=6
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 09/13] media: stm32: dcmipp: addition of a dcmipp-isp subdev
2026-06-02 13:14 ` [PATCH v2 09/13] media: stm32: dcmipp: addition of a dcmipp-isp subdev Alain Volmat
@ 2026-06-02 13:29 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:29 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Critical] A kernel panic can be triggered from userspace via a division by zero in dcmipp_isp_config_decimation() due to an improperly constrained minimum crop height.
- [High] Shifting `crop->top` by 12 bits without masking causes it to structurally overlap with `DCMIPP_P1SRCR_CROPEN` and adjacent fields, leading to hardware register corruption.
- [Medium] Missing cleanup of V4L2 subdev active state and media pads in dcmipp_isp_release().
- [Medium] Pad 2 format dimensions are out-of-sync after changing sink pad crop/compose rectangles.
- [Medium] Lockless data race and incorrect logic when checking `v4l2_subdev_is_streaming(sd)` for TRY formats and selections.
- [Low] Incorrect error log message when disabling streams.
--
commit af4dc8e19ff9f5d7a94a2442ffa7e909d7948584
Author: Alain Volmat <alain.volmat@foss.st.com>
media: stm32: dcmipp: addition of a dcmipp-isp subdev
The ISP subdev is the first element after input of the main pipeline.
Part (static configuration) of this block is done via this subdev while
other configuration done on a per-frame basis will be done via a output
metadata device attached to this subdev.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c
[ ... ]
> +static int dcmipp_isp_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + struct v4l2_subdev_format *fmt)
> +{
> + if (v4l2_subdev_is_streaming(sd))
[Severity: Medium]
Does this code locklessly reject TRY operations when streaming is active?
These format modifications are blocked if v4l2_subdev_is_streaming(sd)
is true, but the code fails to verify whether the format being changed is
V4L2_SUBDEV_FORMAT_TRY. Rejecting TRY operations breaks userspace tooling
that expects to safely test formats while the active stream runs.
Also, calling v4l2_subdev_is_streaming() here reads sd->enabled_pads
locklessly when invoked for TRY formats, creating a race condition against
v4l2_subdev_enable_streams().
> + return -EBUSY;
[ ... ]
> +static void dcmipp_isp_adjust_crop(struct v4l2_rect *r,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + struct v4l2_rect src_rect = {
> + .top = 0,
> + .left = 0,
> + .width = fmt->width,
> + .height = fmt->height,
> + };
> + struct v4l2_rect crop_min = {
> + .top = 8,
[Severity: Critical]
Does this code inadvertently set the minimum crop height to 1 instead of 8?
A typo initializes crop_min with .top = 8 instead of .height = 8.
If userspace sets the crop height to 1 and subsequently requests a compose
height of 0 via V4L2_SEL_TGT_COMPOSE, dcmipp_isp_set_compose() scales size
down to 0, which sets compose->height to 0.
Later, during streaming initialization, dcmipp_isp_config_decimation()
would evaluate crop->height / compose->height, causing a fatal division
by zero kernel panic.
> + .left = 0,
> + .width = fmt->width,
> + .height = 1,
> + };
[ ... ]
> +static int dcmipp_isp_set_selection(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + struct v4l2_subdev_selection *s)
> +{
> + struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
> + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
> + struct v4l2_rect *crop, *compose;
> +
> + if (IS_SRC(s->pad))
> + return -EINVAL;
> +
> + if (v4l2_subdev_is_streaming(sd))
[Severity: Medium]
Does this code also locklessly reject TRY selections when streaming is active?
> + return -EBUSY;
[ ... ]
> + /* Update the source pad size */
> + src_fmt = v4l2_subdev_state_get_format(state, 1);
> + src_fmt->width = s->r.width;
> + src_fmt->height = s->r.height;
[Severity: Medium]
Does this code leave pad 2 with out-of-sync format dimensions?
When a crop or compose operation alters the outgoing image dimensions,
only pad 1 is updated with the new dimensions. Pad 2 is omitted entirely,
leaving it with stale format dimensions that no longer reflect the actual
composed image size.
> +
> + return 0;
> +}
[ ... ]
> +static void dcmipp_isp_config_decimation(struct dcmipp_isp_device *isp,
> + struct v4l2_subdev_state *state)
> +{
> + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
> + struct v4l2_rect *compose = v4l2_subdev_state_get_compose(state, 0);
> + u32 decr;
> +
> + decr = (fls(crop->width / compose->width) - 1) << DCMIPP_P1DECR_HDEC_SHIFT |
> + (fls(crop->height / compose->height) - 1) << DCMIPP_P1DECR_VDEC_SHIFT;
[Severity: Critical]
Does this cause a division by zero kernel panic if compose->height is 0?
Because dcmipp_isp_adjust_crop() mistakenly assigns .top = 8 instead of
.height = 8, userspace can set the compose height to 0, which leads to
a fatal division by zero here.
> + if (decr)
> + decr |= DCMIPP_P1DECR_ENABLE;
> +
> + reg_write(isp, DCMIPP_P1DECR, decr);
> +}
[ ... ]
> +static int dcmipp_isp_enable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + u32 pad, u64 streams_mask)
> +{
[ ... ]
> + /* Configure Statistic Removal */
> + crop = v4l2_subdev_state_get_crop(state, 0);
> + reg_write(isp, DCMIPP_P1SRCR,
> + ((crop->top << DCMIPP_P1SRCR_FIRSTLINEDEL_SHIFT) |
[Severity: High]
Does shifting crop->top by 12 bits without masking structurally overlap with
DCMIPP_P1SRCR_CROPEN and clobber adjacent register fields?
DCMIPP_P1SRCR_FIRSTLINEDEL_SHIFT is defined as 12, while DCMIPP_P1SRCR_CROPEN
is BIT(15). If crop->top specifies dozens or hundreds of lines to drop,
shifting a value like 100 by 12 places it across bits 12 through 23, creating
a structural overlap with CROPEN and potentially causing hardware state
corruption.
> + (crop->height << DCMIPP_P1SRCR_LASTLINE_SHIFT) |
> + DCMIPP_P1SRCR_CROPEN));
[ ... ]
> +static int dcmipp_isp_disable_streams(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + u32 pad, u64 streams_mask)
> +{
[ ... ]
> + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0));
> + if (ret < 0) {
> + dev_err(isp->dev,
> + "failed to start source subdev streaming (%d)\n", ret);
[Severity: Low]
This isn't a bug, but is this error log message printing 'start' instead
of 'stop' or 'disable'?
> + return ret;
> + }
[ ... ]
> +static void dcmipp_isp_release(struct v4l2_subdev *sd)
> +{
> + struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
> +
> + kfree(isp);
[Severity: Medium]
Does this code leak the V4L2 subdev active state and media pads?
In dcmipp_isp_ent_init(), dcmipp_ent_sd_register() allocates media pads
(ved->pads) via dcmipp_pads_init() and the subdev's active state
(sd->active_state) via v4l2_subdev_init_finalize(). However, this release
callback completely omits the required dcmipp_pads_cleanup() and
v4l2_subdev_cleanup(), causing a memory leak upon device removal or unbind.
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=9
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 05/13] media: stm32: dcmipp: correct swap in YUYV data with parallel input
2026-06-02 13:14 ` [PATCH v2 05/13] media: stm32: dcmipp: correct swap in YUYV data with parallel input Alain Volmat
@ 2026-06-02 13:30 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:30 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] 16-bit parallel YUV formats (`_1X16`) are omitted from the `SWAPYUV` compensation, leaving their byte order corrupted.
--
commit 8b2f7ccdfb3c2124d4c63097003a5fddf9ccda9c
Author: Alain Volmat <alain.volmat@foss.st.com>
media: stm32: dcmipp: correct swap in YUYV data with parallel input
This commit introduces logic to selectively swap YUYV data in the DCMIPP
parallel input pipeline to prevent color corruption. It applies the SWAPYUV
bit for 8-bit parallel YUV formats while maintaining legacy behavior on
older hardware revisions.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
> index 5fcd5123136f8..f61618e123224 100644
> --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
[ ... ]
> @@ -451,6 +454,17 @@ static int dcmipp_byteproc_configure_scale_crop
> if (vprediv == 2)
> val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS;
>
> + /*
> + * Perform a SWAP YUV if input is parallel since in this mode
> + * the DCMIPP will swap YUV by default
> + */
> + if (byteproc->ved.dcmipp->pipe_cfg->has_swapyuv &&
> + (sink_fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
> + sink_fmt->code == MEDIA_BUS_FMT_YVYU8_2X8 ||
> + sink_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8 ||
> + sink_fmt->code == MEDIA_BUS_FMT_VYUY8_2X8))
> + val |= DCMIPP_P0PPCR_SWAPYUV;
[Severity: High]
This is a pre-existing issue and was not introduced by the patch under
review, but should 16-bit parallel YUV formats like MEDIA_BUS_FMT_YUYV8_1X16
also be included in this condition in dcmipp_byteproc_configure_scale_crop()?
Since the 1X16 variants are also parallel YUV formats arriving on the
internal bus, will they be subjected to the hardware's default swap without
this SWAPYUV correction and result in corrupted output colors?
> +
> /* decimate using bytes and lines skipping */
> if (val) {
> reg_set(byteproc, DCMIPP_P0PPCR, val);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=5
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 03/13] media: stm32: dcmipp: bytecap: protect CMIER register access
2026-06-02 13:14 ` [PATCH v2 03/13] media: stm32: dcmipp: bytecap: protect CMIER register access Alain Volmat
@ 2026-06-02 13:31 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:31 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Kernel heap Use-After-Free on `vcap->vdev.v4l2_dev->lock` in `dcmipp_bytecap_stop_streaming()` due to lifetime mismatch.
Pre-existing issues:
- [High] Use-After-Free during device unbind because hardware interrupt remains active after devm-allocated memory is freed.
- [High] Missing synchronization with the IRQ thread in `stop_streaming` leads to Use-After-Free and unclocked register access (external abort).
--
commit def9653367321582625d53db751e4abad7f35c94
Author: Alain Volmat <alain.volmat@foss.st.com>
media: stm32: dcmipp: bytecap: protect CMIER register access
This patch adds a spinlock to protect the CMIER register from concurrent
access by utilizing the existing lock in the v4l2_device structure.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
> index 990bbaaf4bda1..dac7338dd9819 100644
> --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
[ ... ]
> @@ -492,7 +494,9 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
> media_pipeline_stop(vcap->vdev.entity.pads);
>
> /* Disable interruptions */
> + spin_lock(&vcap->vdev.v4l2_dev->lock);
[Severity: High]
If a user closes the file descriptor after the device has been unbound,
does this dereference freed memory?
The parent dcmipp structure (which embeds v4l2_dev) is allocated with
devm_kzalloc() and freed by devres when the driver is unbound. However,
the vcap structure is refcounted by the V4L2 video device and remains
alive as long as the file descriptor is open.
When the file descriptor is eventually closed, dcmipp_bytecap_stop_streaming()
is invoked and attempts to acquire this lock inside the already-freed
v4l2_dev memory.
> reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
> + spin_unlock(&vcap->vdev.v4l2_dev->lock);
>
> /* Stop capture */
> reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
[Severity: High]
This is a pre-existing issue, but is it possible for the IRQ thread to
race with dcmipp_bytecap_stop_streaming() since there is no call to
synchronize_irq() before tearing down the capture state?
If the threaded IRQ handler runs after this function sets
vcap->state = DCMIPP_STOPPED, it would observe the stopped state
and proceed to swap(vcap->active, vcap->next).
Because stop_streaming() doesn't clear vcap->next, a buffer that was
already returned to userspace could be swapped into vcap->active. The
IRQ thread would then call dcmipp_bytecap_set_next_frame_or_stop(),
which accesses the freed buffer's memory.
Additionally, since pm_runtime_put() is called during teardown, the
device clocks might be disabled, causing the subsequent register access
in the IRQ thread to trigger a synchronous external abort.
[Severity: High]
This isn't a bug introduced by this patch, but does the driver have an
allocation order issue during probe that leads to a use-after-free
during unbind?
The driver registers its interrupt handler via devm_request_threaded_irq()
before allocating the dcmipp->entity array via devm_kcalloc().
During device unbind, devres frees resources in reverse order. This means
dcmipp->entity is freed before the IRQ is disabled. If a hardware
interrupt fires during this window, dcmipp_irq_callback() will traverse
the freed dcmipp->entity array:
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c:dcmipp_irq_callback() {
...
for (i = 0; i < dcmipp->num_ents; i++) {
ved = dcmipp->entity[i];
...
}
}
Could this reliably crash the system during hot-unplug or driver unbind?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=3
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 10/13] media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev
2026-06-02 13:14 ` [PATCH v2 10/13] media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev Alain Volmat
@ 2026-06-02 13:32 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:32 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Missing v4l2_ctrl_handler_free() calls cause a memory leak of the V4L2 control handler structures when the subdevice is unregistered or when initialization fails.
- [High] Integer division truncation in V4L2_SEL_TGT_COMPOSE clamping allows userspace to trigger a divide-by-zero kernel panic.
- [Medium] Incorrect validation of pm_runtime_get_if_in_use() return value causes an unbalanced runtime PM put and negative usage count.
- [Medium] dcmipp_pixelproc_set_fmt() accepts unsupported color conversion combinations which subsequently cause streaming to fail.
--
commit 44b952f81ce4e958d08d721f7d7bb388641f1def
Author: Alain Volmat <alain.volmat@foss.st.com>
Date: Tue Jun 2 13:14:07 2026 +0000
media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev
Addition of the driver for dcmipp-pixelproc subdev. This subdev is the
last one before the capture device at the tail of both main and
aux pipelines.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c
> new file mode 100644
> index 0000000000000..40394ebfd8279
> --- /dev/null
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c
[ ... ]
> +static int dcmipp_pixelproc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct dcmipp_pixelproc_device *pixelproc =
> + container_of(ctrl->handler,
> + struct dcmipp_pixelproc_device, ctrls);
> +
> + if (pm_runtime_get_if_in_use(pixelproc->dev) == 0)
> + return 0;
[Severity: Medium]
Will this lead to an unbalanced put if pm_runtime_get_if_in_use() returns a
negative error code?
If runtime PM is disabled (e.g. during suspend or unbind),
pm_runtime_get_if_in_use() returns -EINVAL. Because the check only aborts
if the return value is exactly 0, it will proceed to access the hardware
registers and unconditionally call pm_runtime_put(pixelproc->dev). Since the
usage count was not incremented, this will underflow the counter. Should
this check for <= 0 instead?
> +
> + switch (ctrl->id) {
> + case V4L2_CID_PIXELPROC_GAMMA_CORRECTION:
> + reg_write(pixelproc, DCMIPP_PxGMCR(pixelproc->pipe_id),
> + (ctrl->val ? DCMIPP_PxGMCR_ENABLE : 0));
> + break;
> + }
> +
> + pm_runtime_put(pixelproc->dev);
> +
> + return 0;
> +};
[ ... ]
> +static int dcmipp_pixelproc_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
> +
> + if (v4l2_subdev_is_streaming(sd))
> + return -EBUSY;
> +
> + dcmipp_pixelproc_adjust_fmt(pixelproc, &fmt->format, fmt->pad);
> +
> + if (IS_SINK(fmt->pad)) {
[ ... ]
> + } else {
> + struct v4l2_rect *compose =
> + v4l2_subdev_state_get_compose(state, 0);
> +
> + /* AUX (pipe_nb 2) cannot perform color conv */
> + if (pixelproc->pipe_id == 2) {
> + struct v4l2_mbus_framefmt *sink_fmt =
> + v4l2_subdev_state_get_format(state, 0);
> +
> + fmt->format = *sink_fmt;
> + fmt->format.code =
> + dcmipp_pixelpipe_src_format(fmt->format.code);
> + }
> +
> + fmt->format.width = compose->width;
> + fmt->format.height = compose->height;
> + }
> +
> + /* Update the selected pad format */
> + *v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format;
[Severity: Medium]
Does this allow userspace to request unsupported color conversion formats
that will cause streaming to fail later?
According to the V4L2 API, set_fmt should automatically adjust any requested
format to a supported one so that streaming succeeds. Currently, for the main
pipe, unsupported combinations of colorimetry and quantization (e.g. YUV601
full-range to YUV601 limited-range) are accepted here verbatim, but then
dcmipp_pixelproc_colorconv_config() correctly rejects them during streamon,
causing VIDIOC_STREAMON to fail unexpectedly.
> +
> + return 0;
> +}
> +
> +static int dcmipp_pixelproc_set_selection(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + struct v4l2_subdev_selection *s)
> +{
[ ... ]
> + case V4L2_SEL_TGT_COMPOSE:
> + s->r.top = 0;
> + s->r.left = 0;
> + s->r.width = clamp_t(u32, s->r.width,
> + crop->width / DCMIPP_MAX_DOWNSCALE_RATIO,
> + crop->width);
> + s->r.height = clamp_t(u32, s->r.height,
> + crop->height / DCMIPP_MAX_DOWNSCALE_RATIO,
> + crop->height);
[Severity: High]
Can userspace configure a compose width or height of 0 here, leading to a
divide-by-zero crash during stream configuration?
If crop->width is less than DCMIPP_MAX_DOWNSCALE_RATIO (which is 64), the
division crop->width / DCMIPP_MAX_DOWNSCALE_RATIO truncates to 0. This allows
s->r.width to be clamped to a minimum of 0. When streaming starts,
dcmipp_pixelproc_set_crop_downscale() divides by compose->width, resulting in
a kernel panic.
> + *compose = s->r;
> +
> + dev_dbg(pixelproc->dev, "s_selection: compose (%d,%d)/%ux%u\n",
> + compose->left, compose->top,
> + compose->width, compose->height);
> + break;
[ ... ]
> +static void dcmipp_pixelproc_release(struct v4l2_subdev *sd)
> +{
> + struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
> +
> + kfree(pixelproc);
[Severity: High]
Does this leak the V4L2 control handler structures memory?
v4l2_ctrl_handler_init() and v4l2_ctrl_new_custom() dynamically allocate
memory for the controls during dcmipp_pixelproc_ent_init(). However,
v4l2_ctrl_handler_free() is never called here when releasing the subdevice,
which will cause a memory leak when the driver is unbound.
> +}
[ ... ]
> +struct dcmipp_ent_device *
> +dcmipp_pixelproc_ent_init(const char *entity_name,
> + struct dcmipp_device *dcmipp)
> +{
[ ... ]
> + pixelproc->sd.ctrl_handler = &pixelproc->ctrls;
> + if (pixelproc->ctrls.error) {
> + ret = pixelproc->ctrls.error;
> + dev_err(pixelproc->dev, "control initialization error %d\n", ret);
> + kfree(pixelproc);
[Severity: High]
Is a call to v4l2_ctrl_handler_free() missing here as well before returning,
to clean up any partially initialized controls on the error path?
> + return ERR_PTR(ret);
> + }
> +
> + /* Initialize ved and sd */
> + ret = dcmipp_ent_sd_register(&pixelproc->ved, &pixelproc->sd,
> + &dcmipp->v4l2_dev, entity_name,
> + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER,
> + ARRAY_SIZE(pads_flag), pads_flag,
> + &dcmipp_pixelproc_int_ops,
> + &dcmipp_pixelproc_ops,
> + NULL, NULL);
> + if (ret) {
> + kfree(pixelproc);
> + return ERR_PTR(ret);
> + }
> +
> + pixelproc->ved.dcmipp = dcmipp;
> +
> + return &pixelproc->ved;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=10
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 02/13] media: stm32: dcmipp: make dcmipp_state & cmsr2 read common
2026-06-02 13:13 ` [PATCH v2 02/13] media: stm32: dcmipp: make dcmipp_state & cmsr2 read common Alain Volmat
@ 2026-06-02 13:38 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:38 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The interrupt handler `dcmipp_irq_callback` is subject to a teardown and initialization lifecycle mismatch that causes a NULL pointer dereference during probe and a Use-After-Free during removal.
--
commit 57156bb8e4502a74fba0baeca5a498fc6151d274
Author: Alain Volmat <alain.volmat@foss.st.com>
media: stm32: dcmipp: make dcmipp_state & cmsr2 read common
This commit moves the dcmipp_state enum into a common header and centralizes
the reading of the CMSR2 interrupt status register. The core interrupt
handler now reads the register and shares the value with each subdevice.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
> index dd784cfcaac82..b06a4931ae951 100644
> --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
[ ... ]
> @@ -251,10 +253,15 @@ static irqreturn_t dcmipp_irq_callback(int irq, void *arg)
> struct dcmipp_ent_device *ved;
> irqreturn_t ret = IRQ_HANDLED;
> unsigned int i;
> + u32 cmsr2;
> +
> + /* Centralized read of CMSR2 */
> + cmsr2 = reg_read(dcmipp, DCMIPP_CMSR2);
>
> /* Call irq handler of each entities of pipeline */
> for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) {
> ved = dcmipp->entity[i];
> + ved->cmsr2 = cmsr2;
[Severity: High]
This is a pre-existing issue, but does this create a risk of a NULL pointer
dereference during probe or a use-after-free during removal?
In dcmipp_probe(), devm_request_threaded_irq() is called and unmasks the IRQ
before the dcmipp->entity array is allocated via devm_kcalloc():
ret = devm_request_threaded_irq(&pdev->dev, irq, dcmipp_irq_callback,
dcmipp_irq_thread, IRQF_ONESHOT,
dev_name(&pdev->dev), dcmipp);
...
dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents,
...);
If an interrupt fires immediately, dcmipp->entity will be NULL, causing a crash
when evaluating dcmipp->entity[i].
Similarly, in dcmipp_remove(), the subdevice entities are released and their
memory freed while the devres-managed IRQ is still active:
for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++)
dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]);
If a spurious interrupt fires during or after this teardown loop, could this
assignment to ved->cmsr2 corrupt freed memory?
> if (ved->handler)
> ved->handler_ret = ved->handler(irq, ved);
> else if (ved->thread_fn)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=2
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 13/13] media: stm32: dcmipp: instantiate & link stm32mp25 subdevs
2026-06-02 13:14 ` [PATCH v2 13/13] media: stm32: dcmipp: instantiate & link stm32mp25 subdevs Alain Volmat
@ 2026-06-02 13:39 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:39 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Removing the ENABLED flag from the default link between dcmipp_input and dcmipp_dump_postproc breaks existing userspace applications.
--
commit 3794e6b46e60dac386934ce0ec55d3958f228d85
Author: Alain Volmat <alain.volmat@foss.st.com>
media: stm32: dcmipp: instantiate & link stm32mp25 subdevs
Add topology of the two pixel pipes (main & aux) of the stm32mp25.
This also changes the link from dcmipp_input to not be immutable and
enabled by default to allow proper pipeline validation when a pipeline
is not being used.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
> --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
[ ... ]
> @@ -91,13 +96,46 @@ static const struct dcmipp_ent_config stm32mp25_ent_config[] = {
[ ... ]
> static const struct dcmipp_ent_link stm32mp25_ent_links[] = {
> - DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0,
> - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> + DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, 0),
[Severity: High]
Will removing the MEDIA_LNK_FL_ENABLED flag here break existing userspace
applications?
Prior to this commit, the stm32mp25 topology exposed an active, immutable
link between the input and the dump byteproc. This allowed applications like
simple v4l2-ctl scripts to stream directly from the dump video node without
requiring explicit Media Controller link configuration.
By changing the link flags to 0 (mutable and disabled), the link is
disconnected by default. If userspace does not explicitly enable the link
via media-ctl, VIDIOC_STREAMON will fail with -EPIPE because pipeline
validation will find no active path from the sensor source.
Would it be better to just remove MEDIA_LNK_FL_IMMUTABLE but retain
MEDIA_LNK_FL_ENABLED?
This would allow legacy applications to continue working out-of-the-box,
while still permitting new applications to explicitly disable the dump
link to avoid format validation errors when configuring the new main or
aux pipes.
> DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0,
> MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> + DCMIPP_ENT_LINK(ID_INPUT, 2, ID_MAIN_ISP, 0, 0),
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=13
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 12/13] media: stm32: dcmipp: rename bytecap into capture
2026-06-02 13:14 ` [PATCH v2 12/13] media: stm32: dcmipp: rename bytecap into capture Alain Volmat
@ 2026-06-02 13:39 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:39 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] The driver calculates pixel alignment requirements using an unvalidated user format, resulting in missed hardware alignment constraints and unaligned memory access.
- [Low] Stale comment references the old `dcmipp_bytecap_device` structure name.
Pre-existing issues:
- [High] The `dcmipp_capture_start_streaming` function leaks queued buffers if the source subdev cannot be found.
--
commit c041aaac664f7bb802b626e9d71a0c93fba4ced4
Author: Alain Volmat <alain.volmat@foss.st.com>
Subject: media: stm32: dcmipp: rename bytecap into capture
Since the bytecap video driver is now handling all capture (byte & pixel),
rename the file and structs into capture.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c
> --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c
[ ... ]
> @@ -371,7 +371,7 @@ static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
> u32 in_w, in_h;
>
> /* Don't accept a pixelformat that is not on the table */
> - if (!dcmipp_bytecap_is_format_valid(vcap, format->pixelformat))
> + if (!dcmipp_capture_is_format_valid(vcap, format->pixelformat))
> format->pixelformat = fmt_default.pixelformat;
[Severity: High]
Does this code fail to enforce hardware alignment if the user requests an
invalid format?
When the walign variable is computed at the start of this function, the
user's format->pixelformat hasn't been validated yet. If the format is
invalid, hdw_pixel_alignment() returns 0, leaving walign as 0.
When the invalid format is then overridden to fmt_default.pixelformat here,
walign is never recalculated. This allows the subsequent
v4l_bound_align_image() call to use walign = 0, which could result in an
unaligned bytesperline and stride.
Could this cause a WARN_ON() in dcmipp_capture_buf_prepare() and unaligned
memory accesses in the DMA engine?
[ ... ]
> @@ -580,7 +580,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
>
> /*
> * Get source subdev - since link is IMMUTABLE, pointer is cached
> - * within the dcmipp_bytecap_device structure
> + * within the dcmipp_capture_device structure
> */
> if (!vcap->s_subdev) {
> pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
> if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
> return -EINVAL;
[Severity: High]
This is a pre-existing issue, but does this early return leak queued buffers?
According to the videobuf2 API contract, if start_streaming fails, the driver
must return all queued buffers to the QUEUED state so userspace regains
ownership.
If media_pad_remote_pad_first() fails, this code returns -EINVAL directly
instead of going to the err_buffer_done label. This bypasses the cleanup path
and leaves the buffers stranded in vcap->buffers. Should this return be
changed to jump to a cleanup label?
[ ... ]
> @@ -1130,10 +1130,10 @@ static int dcmipp_name_to_pipe_id(const char *name)
> return -EINVAL;
> }
>
> -struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
> +struct dcmipp_ent_device *dcmipp_capture_ent_init(const char *entity_name,
> struct dcmipp_device *dcmipp)
> {
> - struct dcmipp_bytecap_device *vcap;
> + struct dcmipp_capture_device *vcap;
> struct device *dev = dcmipp->dev;
> struct video_device *vdev;
> struct vb2_queue *q;
> const unsigned long pad_flag = MEDIA_PAD_FL_SINK;
> int ret = 0;
>
> /* Allocate the dcmipp_bytecap_device struct */
[Severity: Low]
This isn't a bug, but should this comment also be updated to reflect the
new dcmipp_capture_device structure name?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=12
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 11/13] media: stm32: dcmipp: add pixel-pipe support in bytecap
2026-06-02 13:14 ` [PATCH v2 11/13] media: stm32: dcmipp: add pixel-pipe support in bytecap Alain Volmat
@ 2026-06-02 13:44 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-06-02 13:44 UTC (permalink / raw)
To: Alain Volmat; +Cc: linux-media
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
New issues:
- [High] Unprivileged userspace can trigger kernel panics (`WARN_ON`) by requesting an unaligned width for the dump pipe.
- [High] Inadequate width and height alignment constraints for planar formats like YUV420 result in misaligned chroma strides and plane addresses.
- [High] Missing `else` block in `dcmipp_bytecap_link_validate` causes link validation to unconditionally fail for pixel pipes when the source width is unaligned.
- [High] Error handling for `pipe_id` initialization is defeated by a signedness error, leading to out-of-bounds memory accesses.
Pre-existing issues:
- [Critical] NULL pointer dereference when restarting capture after queue starvation.
- [High] DMA addresses are permanently cached in `buf_prepare`, breaking DMABUF and USERPTR memory models when backing memory changes.
- [High] A race condition between `stop_streaming` and the IRQ thread leads to a Use-After-Free of V4L2 buffers.
--
commit fc3be1eca23ef149d9562a27aaccdb4c295d85ad
Author: Alain Volmat <alain.volmat@foss.st.com>
Subject: media: stm32: dcmipp: add pixel-pipe support in bytecap
This commit adds pixel pipe capture support to the existing bytecap driver to
avoid code duplication, since buffer handling and format manipulation are
similar. It differentiates dump and pixel pipes by introducing a pipe_id and
adds support for configuring the pixel packer on pixel pipes.
> diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
> index bd6f2d68df80c..d4432a9fa0e24 100644
> --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
> +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
> @@ -168,6 +274,73 @@ static const struct v4l2_pix_format fmt_default = {
> .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
> };
>
> +static inline int hdw_pixel_alignment(u32 format)
> +{
> + /* 16 bytes alignment required by hardware */
> + switch (format) {
> + case V4L2_PIX_FMT_NV12:
> + case V4L2_PIX_FMT_NV21:
> + case V4L2_PIX_FMT_YUV420:
> + case V4L2_PIX_FMT_YVU420:
> + case V4L2_PIX_FMT_NV16:
> + case V4L2_PIX_FMT_NV61:
> + case V4L2_PIX_FMT_GREY:
> + return 4;/* 2^4 = 16 pixels = 16 bytes */
[Severity: High]
Does this provide an adequate alignment constraint for planar formats like
YUV420? Since the chroma plane stride is width / 2, a width of 16 pixels
yields an 8-byte chroma stride. Since buf_prepare() asserts 16-byte alignment
across all planes, will this trigger the WARN_ON() checks for unaligned
strides and plane addresses?
[ ... ]
> @@ -192,19 +365,20 @@ static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
> {
> struct dcmipp_bytecap_device *vcap = video_drvdata(file);
> struct v4l2_pix_format *format = &f->fmt.pix;
> - const struct dcmipp_bytecap_pix_map *vpix;
> + /* Take into consideration the width constraint of the pixel packer */
> + unsigned int walign = (vcap->pipe_id != 0 ?
> + hdw_pixel_alignment(format->pixelformat) : 0);
> u32 in_w, in_h;
[Severity: High]
If walign is 0 for the dump pipe, unaligned widths can be negotiated. Will
this cause the unconditional WARN_ON() checks in dcmipp_bytecap_buf_prepare()
to trigger on user-controlled unaligned geometry?
[ ... ]
> @@ -494,25 +701,26 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
>
> /* Disable pipe */
> - reg_clear(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN);
> + reg_clear(vcap, DCMIPP_PxFSCR(vcap->pipe_id), DCMIPP_PxFSCR_PIPEN);
>
> /* Clear any pending interrupts */
> - reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_P0ALL);
> + reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_PxALL(vcap->pipe_id));
>
> spin_lock_irq(&vcap->irqlock);
>
> @@ -525,7 +733,8 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
>
> spin_unlock_irq(&vcap->irqlock);
[Severity: High]
This is a pre-existing issue, but does dcmipp_bytecap_stop_streaming() lack
synchronization with the IRQ thread? The buffers are freed and vcap->active is
cleared, but vcap->next remains intact. If the IRQ thread executes concurrently,
can it swap the dangling next pointer into active and use it, leading to a
Use-After-Free?
[ ... ]
> @@ -541,7 +750,9 @@ static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
> struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
> struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
> + struct v4l2_pix_format *format = &vcap->format;
> unsigned long size;
> + int ret;
>
> size = vcap->format.sizeimage;
>
> @@ -557,6 +768,24 @@ static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
> /* Get memory addresses */
> buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
> buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
> +
> + ret = frame_planes(buf->addr,
> + buf->addrs, buf->strides, buf->sizes,
> + format->width, format->height,
> + format->pixelformat);
> + if (ret) {
> + dev_err(vcap->dev, "%s: Unsupported pixel format (%x)\n",
> + __func__, format->pixelformat);
> + return ret;
> + }
> +
> + /* Check for 16 bytes alignment required by hardware */
> + WARN_ON(buf->addrs[0] & 15);
> + WARN_ON(buf->strides[0] & 15);
> + WARN_ON(buf->addrs[1] & 15);
> + WARN_ON(buf->strides[1] & 15);
> + WARN_ON(buf->addrs[2] & 15);
> +
> buf->prepared = true;
[Severity: High]
This is a pre-existing issue, but does setting buf->prepared = true here
permanently cache the DMA addresses? If userspace uses V4L2_MEMORY_DMABUF and
queues a different file descriptor on the same buffer index, it seems the
driver will skip updating the hardware addresses and DMA into the old memory.
[ ... ]
> @@ -690,7 +919,7 @@ dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
> * for next frame). On-going frame capture will continue until
> * FRAME END but no further capture will be done.
> */
> - reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
> + reg_clear(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
>
> dev_dbg(vcap->dev, "Capture restart is deferred to next buffer queueing\n");
> vcap->next = NULL;
> @@ -707,7 +936,13 @@ dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
> * This register is shadowed and will be taken into
> * account on next VSYNC (start of next frame)
> */
> - reg_write(vcap, DCMIPP_P0PPM0AR1, vcap->next->addr);
> + reg_write(vcap, DCMIPP_PxPPM0AR1(vcap->pipe_id), vcap->next->addrs[0]);
[Severity: Critical]
This is a pre-existing issue, but is there a risk of a NULL pointer dereference
here when restarting capture after queue starvation? If the queue runs empty,
dcmipp_bytecap_process_frame() completes the last buffer and sets vcap->active
to NULL. If userspace resumes capture by queuing two new buffers, it seems
list_next_entry() will dereference vcap->active while it is NULL.
[ ... ]
> @@ -743,29 +978,39 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
> {
> struct dcmipp_bytecap_device *vcap =
> container_of(arg, struct dcmipp_bytecap_device, ved);
> + u32 cmsr2_pxframef;
> + u32 cmsr2_pxvsyncf;
> + u32 cmsr2_pxovrf;
> size_t bytesused = 0;
>
> spin_lock_irq(&vcap->irqlock);
>
> + cmsr2_pxovrf = DCMIPP_CMSR2_PxOVRF(vcap->pipe_id);
> + cmsr2_pxvsyncf = DCMIPP_CMSR2_PxVSYNCF(vcap->pipe_id);
> + cmsr2_pxframef = DCMIPP_CMSR2_PxFRAMEF(vcap->pipe_id);
> +
> /*
> * If we have an overrun, a frame-end will probably not be generated,
> * in that case the active buffer will be recycled as next buffer by
> * the VSYNC handler
> */
> - if (vcap->cmsr2 & DCMIPP_CMSR2_P0OVRF) {
> + if (vcap->cmsr2 & cmsr2_pxovrf) {
> vcap->count.errors++;
> vcap->count.overrun++;
> }
>
> - if (vcap->cmsr2 & DCMIPP_CMSR2_P0FRAMEF) {
> + if (vcap->cmsr2 & cmsr2_pxframef) {
> vcap->count.frame++;
>
> /* Read captured buffer size */
> - bytesused = reg_read(vcap, DCMIPP_P0DCCNTR);
> + if (vcap->pipe_id == 0)
> + bytesused = reg_read(vcap, DCMIPP_P0DCCNTR);
> + else
> + bytesused = vcap->format.sizeimage;
> dcmipp_bytecap_process_frame(vcap, bytesused);
> }
>
> - if (vcap->cmsr2 & DCMIPP_CMSR2_P0VSYNCF) {
> + if (vcap->cmsr2 & cmsr2_pxvsyncf) {
> vcap->count.vsync++;
> if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) {
> vcap->count.underrun++;
[Severity: High]
This is a pre-existing issue, but as mentioned above regarding
dcmipp_bytecap_stop_streaming(), does the IRQ thread miss checking for the
DCMIPP_STOPPED state? If so, it looks like it can swap the dangling next pointer
into active and use it.
[ ... ]
> @@ -826,6 +1071,26 @@ static int dcmipp_bytecap_link_validate(struct media_link *link)
> if (ret < 0)
> return 0;
>
> + /* On pixel pipes there can be alignment constraints */
> + if (vcap->pipe_id != 0) {
> + u32 width_aligned;
> + /*
> + * Depending on the format & pixelpacker constraints, vcap width is
> + * different from mbus width. Compute expected vcap width based on
> + * mbus width
> + */
> + width_aligned = round_up(source_fmt.format.width,
> + 1 << hdw_pixel_alignment(vcap->format.pixelformat));
> +
> + if (width_aligned != vcap->format.width ||
> + source_fmt.format.height != vcap->format.height) {
> + dev_err(vcap->dev, "Wrong width or height %ux%u (%ux%u expected)\n",
> + vcap->format.width, vcap->format.height,
> + width_aligned, source_fmt.format.height);
> + return -EINVAL;
> + }
> + }
> +
> if (source_fmt.format.width != vcap->format.width ||
> source_fmt.format.height != vcap->format.height) {
[Severity: High]
Are we missing an else block here? If the alignment block above successfully
matches the aligned width, this unconditional exact-match check will run
afterward. If the source width was unaligned, they won't match exactly, causing
link validation to unconditionally fail for pixel pipes.
[ ... ]
> @@ -870,6 +1145,22 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(const char *entity_name,
> if (!vcap)
> return ERR_PTR(-ENOMEM);
>
> + /* Retrieve the pipe_id */
> + vcap->pipe_id = dcmipp_name_to_pipe_id(entity_name);
> + if (vcap->pipe_id < 0) {
> + dev_err(dev, "failed to retrieve pipe_id\n");
> + goto err_free_vcap;
> + }
[Severity: High]
Since vcap->pipe_id is a u32, this check (vcap->pipe_id < 0) evaluates to false.
If dcmipp_name_to_pipe_id() returns a negative error code like -EINVAL, will it
be treated as a massive positive integer, leading to out-of-bounds I/O memory
accesses when used in register offset calculations?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-stm32-dcmipp-pixel-pipes-support-v2-0-3c76b5f93157@foss.st.com?part=11
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-06-02 13:44 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-02 13:13 [PATCH v2 00/13] media: stm32: dcmipp: add support for MP2x pixel pipes Alain Volmat
2026-06-02 13:13 ` [PATCH v2 01/13] media: stm32: dcmipp: share struct dcmipp_device among subdevs Alain Volmat
2026-06-02 13:13 ` [PATCH v2 02/13] media: stm32: dcmipp: make dcmipp_state & cmsr2 read common Alain Volmat
2026-06-02 13:38 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 03/13] media: stm32: dcmipp: bytecap: protect CMIER register access Alain Volmat
2026-06-02 13:31 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 04/13] media: stm32: dcmipp: move common structures in dcmipp-common.h Alain Volmat
2026-06-02 13:14 ` [PATCH v2 05/13] media: stm32: dcmipp: correct swap in YUYV data with parallel input Alain Volmat
2026-06-02 13:30 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 06/13] media: stm32: dcmipp: configure csi input of all pipes on stm32mp25 Alain Volmat
2026-06-02 13:29 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 07/13] media: stm32: dcmipp: introduce a dcmipp global media_pipeline Alain Volmat
2026-06-02 13:14 ` [PATCH v2 08/13] media: stm32: dcmipp: add pixel pipes helper functions Alain Volmat
2026-06-02 13:14 ` [PATCH v2 09/13] media: stm32: dcmipp: addition of a dcmipp-isp subdev Alain Volmat
2026-06-02 13:29 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 10/13] media: stm32: dcmipp: pixelproc: addition of dcmipp-pixelproc subdev Alain Volmat
2026-06-02 13:32 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 11/13] media: stm32: dcmipp: add pixel-pipe support in bytecap Alain Volmat
2026-06-02 13:44 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 12/13] media: stm32: dcmipp: rename bytecap into capture Alain Volmat
2026-06-02 13:39 ` sashiko-bot
2026-06-02 13:14 ` [PATCH v2 13/13] media: stm32: dcmipp: instantiate & link stm32mp25 subdevs Alain Volmat
2026-06-02 13:39 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox