* [v8 01/14] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 7:45 ` Jacopo Mondi
2026-05-04 1:05 ` [v8 02/14] media: rppx1: Add framework to support Dreamchip RPPX1 ISP Niklas Söderlund
` (13 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
From: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Register V4L2 metadata fourcc codes for the Dreamchip RPP-X1 ISP
parameters and statistics buffers. These formats are used by the driver
to exchange ISP configuration and 3A statistics with userspace through
the extensible parameters framework.
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++
include/uapi/linux/videodev2.h | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index a2b650f4ec3c..cd3f4a86e27f 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1471,6 +1471,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break;
case V4L2_META_FMT_MALI_C55_PARAMS: descr = "ARM Mali-C55 ISP Parameters"; break;
case V4L2_META_FMT_MALI_C55_STATS: descr = "ARM Mali-C55 ISP 3A Statistics"; break;
+ case V4L2_META_FMT_RPP_X1_PARAMS: descr = "Dreamchip RPP-X1 ISP Parameters"; break;
+ case V4L2_META_FMT_RPP_X1_STATS: descr = "Dreamchip RPP-X1 ISP Statistics"; break;
case V4L2_PIX_FMT_NV12_8L128: descr = "NV12 (8x128 Linear)"; break;
case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break;
case V4L2_PIX_FMT_NV12_10BE_8L128: descr = "10-bit NV12 (8x128 Linear, BE)"; break;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index eda4492e40dc..1f78b5378b3b 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -889,6 +889,10 @@ struct v4l2_pix_format {
#define V4L2_META_FMT_MALI_C55_PARAMS v4l2_fourcc('C', '5', '5', 'P') /* ARM Mali-C55 Parameters */
#define V4L2_META_FMT_MALI_C55_STATS v4l2_fourcc('C', '5', '5', 'S') /* ARM Mali-C55 3A Statistics */
+/* Vendor specific - used for Dreamchip RPP-X1 ISP */
+#define V4L2_META_FMT_RPP_X1_PARAMS v4l2_fourcc('D', 'R', '1', 'P') /* Dreamchip RPP-X1 Parameters */
+#define V4L2_META_FMT_RPP_X1_STATS v4l2_fourcc('D', 'R', '1', 'S') /* Dreamchip RPP-X1 Statistics */
+
#ifdef __KERNEL__
/*
* Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 01/14] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats
2026-05-04 1:05 ` [v8 01/14] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Niklas Söderlund
@ 2026-05-06 7:45 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 7:45 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Niklas
On Mon, May 04, 2026 at 03:05:43AM +0200, Niklas Söderlund wrote:
> From: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
>
> Register V4L2 metadata fourcc codes for the Dreamchip RPP-X1 ISP
> parameters and statistics buffers. These formats are used by the driver
> to exchange ISP configuration and 3A statistics with userspace through
> the extensible parameters framework.
>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
As pointed out in the review of the previous version you need to add
documentation for the formats in
Documentation/userspace-api/media/v4l/metafmt-dcc-rppx1.rst (or
similar)
> ---
> drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++
> include/uapi/linux/videodev2.h | 4 ++++
> 2 files changed, 6 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index a2b650f4ec3c..cd3f4a86e27f 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -1471,6 +1471,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
> case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break;
> case V4L2_META_FMT_MALI_C55_PARAMS: descr = "ARM Mali-C55 ISP Parameters"; break;
> case V4L2_META_FMT_MALI_C55_STATS: descr = "ARM Mali-C55 ISP 3A Statistics"; break;
> + case V4L2_META_FMT_RPP_X1_PARAMS: descr = "Dreamchip RPP-X1 ISP Parameters"; break;
> + case V4L2_META_FMT_RPP_X1_STATS: descr = "Dreamchip RPP-X1 ISP Statistics"; break;
> case V4L2_PIX_FMT_NV12_8L128: descr = "NV12 (8x128 Linear)"; break;
> case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break;
> case V4L2_PIX_FMT_NV12_10BE_8L128: descr = "10-bit NV12 (8x128 Linear, BE)"; break;
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index eda4492e40dc..1f78b5378b3b 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -889,6 +889,10 @@ struct v4l2_pix_format {
> #define V4L2_META_FMT_MALI_C55_PARAMS v4l2_fourcc('C', '5', '5', 'P') /* ARM Mali-C55 Parameters */
> #define V4L2_META_FMT_MALI_C55_STATS v4l2_fourcc('C', '5', '5', 'S') /* ARM Mali-C55 3A Statistics */
>
> +/* Vendor specific - used for Dreamchip RPP-X1 ISP */
> +#define V4L2_META_FMT_RPP_X1_PARAMS v4l2_fourcc('D', 'R', '1', 'P') /* Dreamchip RPP-X1 Parameters */
> +#define V4L2_META_FMT_RPP_X1_STATS v4l2_fourcc('D', 'R', '1', 'S') /* Dreamchip RPP-X1 Statistics */
> +
> #ifdef __KERNEL__
> /*
> * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [v8 02/14] media: rppx1: Add framework to support Dreamchip RPPX1 ISP
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
2026-05-04 1:05 ` [v8 01/14] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 8:53 ` Jacopo Mondi
2026-05-04 1:05 ` [v8 03/14] media: rcar-isp: Add support for ISPCORE Niklas Söderlund
` (12 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Add a framework driver for Dreamchip RPPX1 ISP. The driver aims to
provide a framework for other V4L2 based drivers to drive the RPPX1
functionality. The reason for this split is that the RPPX1 IP itself do
not provide any DMA engines to drive data to/from the device, instead it
depends on other IP blocks to implement these features.
While the peripherals around the RPPX1 ISP used in different designs and
by different vendors are different the RPPX1 core itself is the same.
For this reason the framework solution to be able to split the Dreamchip
RPPX1 driver from vendors usage of it have been picked in hope to reduce
duplication of the common parts.
The design is to try and keep the surface of this framework as small as
possible. The intention of this change is to be able to fill all needs
of this.
* Two functions to create and destroy a RPPX1 instance, rppx1_create()
and rppx1_destory(). These are intended to be called in the users
probe and remove code paths.
* Two functions to start and stop the RPPX1 processing, rppx1_start()
and rppx1_stop(). These are intended to be called in the users
stream on and stream off code paths.
* One function to ask the RPPX1 to process parameters buffer prepared
by user space, rppx1_params(). The intention is to call this
function when the parameter buffer is queued to the V4L2 driver and
the result stored by the driver until the time it needs to be
written to the RPPX1. It's the users responsibility to write it
either using MMIO or other means.
* One function to fill in a statistic buffer based on the current
status of the RPPX1, rppx1_stats_fill_isr(). The intention is that
the user call's this in its interrupt handler when it knows the
RPPX1 is done processing a frame.
* One function to ack and retrieve the interrupts generated by the
RPPX1, rppx1_interrupt(). The intention is to call this function
when the users interrupt handler detects the RPPX1 have raised and
interrupt. There is no need for the user to understand, or act, on
the actual RPPX1 interrupt, but it can if it wants too.
The initial support in the framework is limited and do not implement any
ISP processing algorithms other then configuring the RPPX1 to process
any Bayer (8-, 10, or 12-bit) image and produce either a RGB or YUYV
output. It do however probe all function blocks of the RPPX1 and provide
an interface to interact with both parameter and statistic bufferers.
The user of the framework will not change as algorithms for the
different function blocks of the ISP are being added.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
* Changes since v7
- Merge with fixups done before the pixel format change.
* Changes since v6
- Use kzalloc_obj
- Set device pointer
* Changes since v5
- Make use of v4l2-isp.
- Make VIDEO_DCT_RPPX1 a non visible symbol. It makes no sens to build
it without a consumer.
* Changes since v2
- Add missing include to slab.h
* Changes since v3
- Fix small build issues found by 0day on non-arm ARCH.
---
MAINTAINERS | 7 +
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/dreamchip/Kconfig | 3 +
drivers/media/platform/dreamchip/Makefile | 6 +
.../media/platform/dreamchip/rppx1/Kconfig | 12 +
.../media/platform/dreamchip/rppx1/Makefile | 33 ++
.../platform/dreamchip/rppx1/rpp_module.c | 40 +++
.../platform/dreamchip/rppx1/rpp_module.h | 145 ++++++++
.../platform/dreamchip/rppx1/rpp_params.c | 72 ++++
.../platform/dreamchip/rppx1/rpp_stats.c | 27 ++
.../media/platform/dreamchip/rppx1/rppx1.c | 339 ++++++++++++++++++
.../media/platform/dreamchip/rppx1/rppx1.h | 99 +++++
.../platform/dreamchip/rppx1/rppx1_acq.c | 147 ++++++++
.../platform/dreamchip/rppx1/rppx1_awbg.c | 30 ++
.../media/platform/dreamchip/rppx1/rppx1_bd.c | 52 +++
.../platform/dreamchip/rppx1/rppx1_bdrgb.c | 80 +++++
.../platform/dreamchip/rppx1/rppx1_bls.c | 59 +++
.../platform/dreamchip/rppx1/rppx1_cac.c | 29 ++
.../platform/dreamchip/rppx1/rppx1_ccor.c | 106 ++++++
.../media/platform/dreamchip/rppx1/rppx1_db.c | 44 +++
.../platform/dreamchip/rppx1/rppx1_dpcc.c | 76 ++++
.../platform/dreamchip/rppx1/rppx1_exm.c | 51 +++
.../media/platform/dreamchip/rppx1/rppx1_ga.c | 49 +++
.../platform/dreamchip/rppx1/rppx1_hist.c | 76 ++++
.../platform/dreamchip/rppx1/rppx1_hist256.c | 46 +++
.../media/platform/dreamchip/rppx1/rppx1_is.c | 42 +++
.../platform/dreamchip/rppx1/rppx1_lin.c | 58 +++
.../platform/dreamchip/rppx1/rppx1_lsc.c | 68 ++++
.../platform/dreamchip/rppx1/rppx1_ltm.c | 48 +++
.../platform/dreamchip/rppx1/rppx1_ltmmeas.c | 41 +++
.../platform/dreamchip/rppx1/rppx1_outif.c | 45 +++
.../platform/dreamchip/rppx1/rppx1_outregs.c | 75 ++++
.../platform/dreamchip/rppx1/rppx1_rmap.c | 64 ++++
.../platform/dreamchip/rppx1/rppx1_rmapmeas.c | 47 +++
.../platform/dreamchip/rppx1/rppx1_shrp.c | 64 ++++
.../platform/dreamchip/rppx1/rppx1_wbmeas.c | 61 ++++
.../platform/dreamchip/rppx1/rppx1_xyz2luv.c | 26 ++
include/media/rppx1.h | 34 ++
.../uapi/linux/media/dreamchip/rppx1-config.h | 66 ++++
40 files changed, 2369 insertions(+)
create mode 100644 drivers/media/platform/dreamchip/Kconfig
create mode 100644 drivers/media/platform/dreamchip/Makefile
create mode 100644 drivers/media/platform/dreamchip/rppx1/Kconfig
create mode 100644 drivers/media/platform/dreamchip/rppx1/Makefile
create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_module.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_module.h
create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_params.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_stats.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1.h
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_db.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_is.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
create mode 100644 include/media/rppx1.h
create mode 100644 include/uapi/linux/media/dreamchip/rppx1-config.h
diff --git a/MAINTAINERS b/MAINTAINERS
index fb3c08e42a66..b7bdfa1b4816 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7787,6 +7787,13 @@ F: drivers/block/drbd/
F: include/linux/drbd*
F: lib/lru_cache.c
+DREAMCHIP RPPX1 ISP
+M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/platform/dreamchip/rppx1/
+F: include/uapi/linux/media/dreamchip/rppx1-config.h
+
DRIVER COMPONENT FRAMEWORK
L: dri-devel@lists.freedesktop.org
F: drivers/base/component.c
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 3f0b7bb68cc9..3621b46e1e8d 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -71,6 +71,7 @@ source "drivers/media/platform/atmel/Kconfig"
source "drivers/media/platform/broadcom/Kconfig"
source "drivers/media/platform/cadence/Kconfig"
source "drivers/media/platform/chips-media/Kconfig"
+source "drivers/media/platform/dreamchip/Kconfig"
source "drivers/media/platform/imagination/Kconfig"
source "drivers/media/platform/intel/Kconfig"
source "drivers/media/platform/marvell/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 6d5f79ddfcc3..0cc4bfe0da32 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -14,6 +14,7 @@ obj-y += atmel/
obj-y += broadcom/
obj-y += cadence/
obj-y += chips-media/
+obj-y += dreamchip/
obj-y += imagination/
obj-y += intel/
obj-y += marvell/
diff --git a/drivers/media/platform/dreamchip/Kconfig b/drivers/media/platform/dreamchip/Kconfig
new file mode 100644
index 000000000000..d177d4ee79ae
--- /dev/null
+++ b/drivers/media/platform/dreamchip/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/media/platform/dreamchip/rppx1/Kconfig"
diff --git a/drivers/media/platform/dreamchip/Makefile b/drivers/media/platform/dreamchip/Makefile
new file mode 100644
index 000000000000..ba47ba2d136e
--- /dev/null
+++ b/drivers/media/platform/dreamchip/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Dreamchip device drivers.
+#
+
+obj-y += rppx1/
diff --git a/drivers/media/platform/dreamchip/rppx1/Kconfig b/drivers/media/platform/dreamchip/rppx1/Kconfig
new file mode 100644
index 000000000000..0998a7d10bf2
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+config VIDEO_DCT_RPPX1
+ tristate
+ depends on V4L_PLATFORM_DRIVERS
+ select V4L2_ISP
+ help
+ Support library for Dreamchip HDR RPP X1 High Dynamic Range Real-time
+ Pixel Processor (RPP). The library can be used by other drivers who
+ utilises the RPP as part of an ISP implementation.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rppx1.
diff --git a/drivers/media/platform/dreamchip/rppx1/Makefile b/drivers/media/platform/dreamchip/rppx1/Makefile
new file mode 100644
index 000000000000..b2bd6b5d68bc
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/Makefile
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0
+dct-rpp-x1-objs = \
+ rpp_module.o \
+ rpp_params.o \
+ rpp_stats.o \
+ rppx1.o \
+ rppx1_acq.o \
+ rppx1_awbg.o \
+ rppx1_bd.o \
+ rppx1_bdrgb.o \
+ rppx1_bls.o \
+ rppx1_cac.o \
+ rppx1_ccor.o \
+ rppx1_db.o \
+ rppx1_dpcc.o \
+ rppx1_exm.o \
+ rppx1_ga.o \
+ rppx1_hist.o \
+ rppx1_hist256.o \
+ rppx1_is.o \
+ rppx1_lin.o \
+ rppx1_lsc.o \
+ rppx1_ltm.o \
+ rppx1_ltmmeas.o \
+ rppx1_outif.o \
+ rppx1_outregs.o \
+ rppx1_rmap.o \
+ rppx1_rmapmeas.o \
+ rppx1_shrp.o \
+ rppx1_wbmeas.o \
+ rppx1_xyz2luv.o
+
+obj-$(CONFIG_VIDEO_DCT_RPPX1) += dct-rpp-x1.o
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.c b/drivers/media/platform/dreamchip/rppx1/rpp_module.c
new file mode 100644
index 000000000000..cd923b7ff5c1
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include <linux/slab.h>
+
+#include "rppx1.h"
+#include "rpp_module.h"
+
+int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp,
+ const struct rpp_module_ops *ops, u32 base)
+{
+ mod->rpp = rpp;
+ mod->base = base;
+ mod->ops = ops;
+
+ if (ops->probe)
+ return ops->probe(mod);
+
+ return 0;
+}
+
+void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value)
+{
+ rppx1_write(mod->rpp, mod->base + offset, value);
+}
+
+u32 rpp_module_read(struct rpp_module *mod, u32 offset)
+{
+ return rppx1_read(mod->rpp, mod->base + offset);
+}
+
+void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value)
+{
+ u32 reg = rpp_module_read(mod, offset) & ~mask;
+
+ rpp_module_write(mod, offset, reg | value);
+}
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
new file mode 100644
index 000000000000..742904973e35
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#ifndef __RPPX1_MODULE_H__
+#define __RPPX1_MODULE_H__
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <linux/media/dreamchip/rppx1-config.h>
+
+#include <media/rppx1.h>
+
+struct rpp_module_ops;
+
+enum rpp_raw_pattern {
+ RPP_RGGB = 0,
+ RPP_GRBG,
+ RPP_GBRG,
+ RPP_BGGR,
+};
+
+struct rpp_module {
+ struct rppx1 *rpp;
+ u32 base;
+
+ const struct rpp_module_ops *ops;
+
+ union {
+ struct {
+ enum rpp_raw_pattern raw_pattern;
+ } acq;
+ struct {
+ unsigned int colorbits;
+ } bdrgb;
+ struct {
+ unsigned int colorbits;
+ } bls;
+ struct {
+ unsigned int colorbits;
+ unsigned int type;
+ } ccor;
+ struct {
+ unsigned int colorbits;
+ } dpcc;
+ struct {
+ unsigned int resultbits;
+ } exm;
+ struct {
+ unsigned int colorbits;
+ } ga;
+ struct {
+ unsigned int colorbits;
+ } hist;
+ struct {
+ unsigned int colorbits;
+ } lin;
+ struct {
+ unsigned int colorbits_high;
+ unsigned int colorbits_low;
+ } rmap;
+ struct {
+ unsigned int colorbits_high;
+ unsigned int colorbits_low;
+ } rmapmeas;
+ struct {
+ unsigned int colorbits;
+ } shrp;
+ struct {
+ unsigned int colorbits;
+ } wbmeas;
+ } info;
+};
+
+int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp,
+ const struct rpp_module_ops *ops, u32 base);
+
+void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value);
+u32 rpp_module_read(struct rpp_module *mod, u32 offset);
+void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
+
+union rppx1_params_block {
+ struct v4l2_isp_params_block_header header;
+};
+
+union rppx1_stats_block {
+ struct v4l2_isp_params_block_header header;
+};
+
+struct rpp_module_ops {
+ int (*probe)(struct rpp_module *mod);
+ int (*start)(struct rpp_module *mod, const struct v4l2_mbus_framefmt *fmt);
+
+ int (*fill_params)(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv);
+ int (*fill_stats)(struct rpp_module *mod,
+ union rppx1_stats_block *block);
+};
+
+extern const struct rpp_module_ops rppx1_acq_ops;
+extern const struct rpp_module_ops rppx1_awbg_ops;
+extern const struct rpp_module_ops rppx1_bd_ops;
+extern const struct rpp_module_ops rppx1_bdrgb_ops;
+extern const struct rpp_module_ops rppx1_bls_ops;
+extern const struct rpp_module_ops rppx1_cac_ops;
+extern const struct rpp_module_ops rppx1_ccor_ops;
+extern const struct rpp_module_ops rppx1_ccor_csm_ops;
+extern const struct rpp_module_ops rppx1_db_ops;
+extern const struct rpp_module_ops rppx1_dpcc_ops;
+extern const struct rpp_module_ops rppx1_exm_ops;
+extern const struct rpp_module_ops rppx1_ga_ops;
+extern const struct rpp_module_ops rppx1_hist256_ops;
+extern const struct rpp_module_ops rppx1_hist_ops;
+extern const struct rpp_module_ops rppx1_is_ops;
+extern const struct rpp_module_ops rppx1_lin_ops;
+extern const struct rpp_module_ops rppx1_lsc_ops;
+extern const struct rpp_module_ops rppx1_ltm_ops;
+extern const struct rpp_module_ops rppx1_ltmmeas_ops;
+extern const struct rpp_module_ops rppx1_outif_ops;
+extern const struct rpp_module_ops rppx1_outregs_ops;
+extern const struct rpp_module_ops rppx1_rmapmeas_ops;
+extern const struct rpp_module_ops rppx1_rmap_ops;
+extern const struct rpp_module_ops rppx1_shrp_ops;
+extern const struct rpp_module_ops rppx1_wbmeas_ops;
+extern const struct rpp_module_ops rppx1_xyz2luv_ops;
+
+#define rpp_module_call(mod, op, args...) \
+ ({ \
+ struct rpp_module *__mod = (mod); \
+ int __result; \
+ if (!__mod) \
+ __result = -ENODEV; \
+ else if (!__mod->ops->op) \
+ __result = 0; \
+ else \
+ __result = __mod->ops->op(__mod, ##args); \
+ __result; \
+ })
+
+#endif /* __RPPX1_MODULE_H__ */
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
new file mode 100644
index 000000000000..a5feb18f3bd5
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include <media/v4l2-isp.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "rppx1.h"
+
+#define RPPX1_PARAMS_BLOCK_INFO(block, data) \
+ [RPPX1_PARAMS_BLOCK_TYPE_ ## block] = { \
+ .size = sizeof(struct rppx1_ ## data ## _params), \
+ }
+
+static const struct v4l2_isp_block_type_info
+rppx1_ext_params_blocks_info[] = {
+};
+
+int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
+ rppx1_reg_write write, void *priv)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct v4l2_isp_params_buffer *cfg;
+ size_t block_offset;
+ int ret;
+
+ ret = v4l2_isp_params_validate_buffer_size(rpp->dev, vb, max_size);
+ if (ret)
+ return ret;
+
+ cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+
+ ret = v4l2_isp_params_validate_buffer(rpp->dev, vb,
+ (struct v4l2_isp_params_buffer *)cfg,
+ rppx1_ext_params_blocks_info,
+ ARRAY_SIZE(rppx1_ext_params_blocks_info));
+ if (ret)
+ return ret;
+
+ /* Walk the list of parameter blocks and process them. */
+ block_offset = 0;
+ while (block_offset < cfg->data_size) {
+ const union rppx1_params_block *block =
+ (const union rppx1_params_block *)&cfg->data[block_offset];
+ struct rpp_module *module;
+ int ret;
+
+ block_offset += block->header.size;
+
+ switch (block->header.type) {
+ default:
+ module = NULL;
+ break;
+ }
+
+ if (!module) {
+ pr_warn("Not handled RPPX1 block type: 0x%04x\n", block->header.type);
+ continue;
+ }
+
+ ret = rpp_module_call(module, fill_params, block, write, priv);
+ if (ret) {
+ pr_err("Error processing RPPX1 block type: 0x%04x\n", block->header.type);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rppx1_params);
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
new file mode 100644
index 000000000000..8f43e56ba361
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rppx1.h"
+#include "rpp_module.h"
+
+#include <media/v4l2-isp.h>
+
+#define RPPX1_STATS_BLOCK_INFO(type, block) \
+ [RPPX1_STATS_BLOCK_TYPE_ ## type] = { \
+ .size = sizeof(struct rppx1_ ## block ## _stats), \
+ }
+
+#define rppx1_init_stats_block(rpp, buf, type) \
+ ((union rppx1_stats_block *) \
+ v4l2_isp_stats_init_block((rpp)->dev, (buf), \
+ rppx1_stats_blocks_info, \
+ ARRAY_SIZE(rppx1_stats_blocks_info), \
+ (type), RPPX1_STATS_MAX_SIZE)) \
+
+void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
+{
+}
+EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.c b/drivers/media/platform/dreamchip/rppx1/rppx1.c
new file mode 100644
index 000000000000..2998c5f2a42e
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ *
+ * Support library for Dreamchip HDR RPPX1 High Dynamic Range Real-time Pixel
+ * Processor.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "rppx1.h"
+
+/* RPP_HDR Base Addresses */
+#define RPPX1_HDRREGS_BASE 0x0000
+#define RPPX1_HDR_IRQ_BASE 0x0200
+#define RPPX1_RPP_OUT_BASE 0x0800
+#define RPPX1_RPP_RMAP_BASE 0x0c00
+#define RPPX1_RPP_RMAP_MEAS_BASE 0x1000
+#define RPPX1_RPP_MAIN_PRE1_BASE 0x2000
+#define RPPX1_RPP_MAIN_PRE2_BASE 0x4000
+#define RPPX1_RPP_MAIN_POST_BASE 0xa000
+#define RPPX1_RPP_MVOUT_BASE 0xc000
+#define RPPX1_RPP_FUSA_BASE 0xf000
+
+#define RPPX1_RPP_HDRREGS_VERSION_REG (RPPX1_HDRREGS_BASE + 0x0000)
+#define RPPX1_RPP_HDR_UPD_REG (RPPX1_HDRREGS_BASE + 0x0004)
+#define RPPX1_RESERVED_3_REG (RPPX1_HDRREGS_BASE + 0x0008)
+#define RPPX1_RPP_HDR_INFORM_ENABLE_REG (RPPX1_HDRREGS_BASE + 0x000c)
+#define RPPX1_RPP_HDR_OUT_IF_ON_REG (RPPX1_HDRREGS_BASE + 0x0010)
+#define RPPX1_RPP_HDR_OUT_IF_OFF_REG (RPPX1_HDRREGS_BASE + 0x0014)
+#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG (RPPX1_HDRREGS_BASE + 0x0018)
+
+#define RPPX1_RPP_ISM (RPPX1_HDR_IRQ_BASE + 0x00)
+#define RPPX1_RPP_RIS (RPPX1_HDR_IRQ_BASE + 0x04)
+#define RPPX1_RPP_MIS (RPPX1_HDR_IRQ_BASE + 0x08)
+#define RPPX1_RPP_ISC (RPPX1_HDR_IRQ_BASE + 0x0c)
+
+/* RPP_OUT/MV_OUT Pipelines - Base Addresses */
+#define RPPX1_GAMMA_OUT_BASE 0x0000 /* HV, MV */
+#define RPPX1_IS_BASE 0x00c0 /* HV, MV */
+#define RPPX1_CSM_BASE 0x0100 /* HV, MV */
+#define RPPX1_OUT_IF_BASE 0x0200 /* HV, MV */
+#define RPPX1_RPP_OUTREGS_BASE 0x02c0 /* HV, MV */
+#define RPPX1_LUV_BASE 0x0300 /* MV */
+
+/* PRE1/PRE2/POST Pipelines - Base Addresses */
+#define RPPX1_ACQ_BASE 0x0080 /* PRE1, PRE2 */
+#define RPPX1_BLS_BASE 0x0100 /* PRE1, PRE2 */
+#define RPPX1_GAMMA_IN_BASE 0x0200 /* PRE1, PRE2 */
+#define RPPX1_LSC_BASE 0x0400 /* PRE1, PRE2 */
+#define RPPX1_AWB_GAIN_BASE 0x0500 /* PRE1, PRE2, POST */
+#define RPPX1_DPCC_BASE 0x0600 /* PRE1, PRE2 */
+#define RPPX1_DPF_BASE 0x0700 /* PRE1, PRE2 */
+#define RPPX1_FILT_BASE 0x0800 /* POST */
+#define RPPX1_CAC_BASE 0x0880 /* POST */
+#define RPPX1_CCOR_BASE 0x0900 /* POST */
+#define RPPX1_HIST_BASE 0x0a00 /* PRE1, PRE2, POST */
+#define RPPX1_HIST256_BASE 0x0b00 /* PRE1 */
+#define RPPX1_EXM_BASE 0x0c00 /* PRE1, PRE2 */
+#define RPPX1_LTM_BASE 0x1000 /* POST */
+#define RPPX1_LTM_MEAS_BASE 0x1200 /* POST */
+#define RPPX1_WBMEAS_BASE 0x1700 /* POST */
+#define RPPX1_BDRGB_BASE 0x1800 /* POST */
+#define RPPX1_SHRP_BASE 0x1a00 /* POST */
+
+/* Functional Safety Module Base Addresses */
+#define RPPX1_FMU_BASE 0x0100
+
+#define RPPX1_RPP_HDR_FMU_FSM (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x00)
+#define RPPX1_RPP_HDR_FMU_RFS (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x04)
+#define RPPX1_RPP_HDR_FMU_MFS (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x08)
+#define RPPX1_RPP_HDR_FMU_FSC (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x0c)
+
+void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value)
+{
+ iowrite32(value, rpp->base + offset);
+}
+
+u32 rppx1_read(struct rppx1 *rpp, u32 offset)
+{
+ u32 ret = ioread32(rpp->base + offset);
+ return ret;
+}
+
+bool rppx1_interrupt(struct rppx1 *rpp, u32 *isc)
+{
+ u32 status, raw, fault;
+
+ fault = rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS);
+ if (fault) {
+ pr_err("%s: fault 0x%08x\n", __func__, fault);
+ rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC, fault);
+ }
+
+ /* Read raw interrupt status. */
+ raw = rppx1_read(rpp, RPPX1_RPP_RIS);
+ status = rppx1_read(rpp, RPPX1_RPP_MIS);
+
+ /* Propagate the isc status. */
+ if (isc)
+ *isc = status | raw;
+
+ /* Clear enabled interrupts */
+ rppx1_write(rpp, RPPX1_RPP_ISC, status);
+
+ return !!(status & RPPX1_IRQ_ID_OUT_FRAME);
+}
+EXPORT_SYMBOL_GPL(rppx1_interrupt);
+
+void rppx1_destroy(struct rppx1 *rpp)
+{
+ kfree(rpp);
+}
+EXPORT_SYMBOL_GPL(rppx1_destroy);
+
+/*
+ * Allocate the private data structure and verify the hardware is present.
+ */
+struct rppx1 *rppx1_create(void __iomem *base, struct device *dev)
+{
+ struct rppx1 *rpp;
+ u32 reg;
+
+ /* Allocate library structure */
+ rpp = kzalloc_obj(*rpp);
+ if (!rpp)
+ return NULL;
+
+ rpp->base = base;
+ rpp->dev = dev;
+
+ /* Check communication with RPP and verify it truly is a X1. */
+ reg = rppx1_read(rpp, RPPX1_RPP_HDRREGS_VERSION_REG);
+ if (reg != 3) {
+ pr_err("Unsupported HDR version (%u)\n", reg);
+ rppx1_destroy(rpp);
+ return NULL;
+ }
+
+ /* Probe the PRE1 pipeline. */
+ if (rpp_module_probe(&rpp->pre1.acq, rpp, &rppx1_acq_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_ACQ_BASE) ||
+ rpp_module_probe(&rpp->pre1.bls, rpp, &rppx1_bls_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_BLS_BASE) ||
+ rpp_module_probe(&rpp->pre1.lin, rpp, &rppx1_lin_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_GAMMA_IN_BASE) ||
+ rpp_module_probe(&rpp->pre1.lsc, rpp, &rppx1_lsc_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_LSC_BASE) ||
+ rpp_module_probe(&rpp->pre1.awbg, rpp, &rppx1_awbg_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_AWB_GAIN_BASE) ||
+ rpp_module_probe(&rpp->pre1.dpcc, rpp, &rppx1_dpcc_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPCC_BASE) ||
+ rpp_module_probe(&rpp->pre1.bd, rpp, &rppx1_bd_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPF_BASE) ||
+ rpp_module_probe(&rpp->pre1.hist, rpp, &rppx1_hist_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST_BASE) ||
+ rpp_module_probe(&rpp->pre1.hist256, rpp, &rppx1_hist256_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST256_BASE) ||
+ rpp_module_probe(&rpp->pre1.exm, rpp, &rppx1_exm_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_EXM_BASE))
+ goto err;
+
+ /* Probe the PRE2 pipeline. */
+ if (rpp_module_probe(&rpp->pre2.acq, rpp, &rppx1_acq_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_ACQ_BASE) ||
+ rpp_module_probe(&rpp->pre2.bls, rpp, &rppx1_bls_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_BLS_BASE) ||
+ rpp_module_probe(&rpp->pre2.lin, rpp, &rppx1_lin_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_GAMMA_IN_BASE) ||
+ rpp_module_probe(&rpp->pre2.lsc, rpp, &rppx1_lsc_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_LSC_BASE) ||
+ rpp_module_probe(&rpp->pre2.awbg, rpp, &rppx1_awbg_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_AWB_GAIN_BASE) ||
+ rpp_module_probe(&rpp->pre2.dpcc, rpp, &rppx1_dpcc_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPCC_BASE) ||
+ rpp_module_probe(&rpp->pre2.bd, rpp, &rppx1_bd_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPF_BASE) ||
+ rpp_module_probe(&rpp->pre2.hist, rpp, &rppx1_hist_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_HIST_BASE) ||
+ rpp_module_probe(&rpp->pre2.exm, rpp, &rppx1_exm_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_EXM_BASE))
+ goto err;
+
+ /* Probe the POST pipeline. */
+ if (rpp_module_probe(&rpp->post.awbg, rpp, &rppx1_awbg_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_AWB_GAIN_BASE) ||
+ rpp_module_probe(&rpp->post.ccor, rpp, &rppx1_ccor_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_CCOR_BASE) ||
+ rpp_module_probe(&rpp->post.hist, rpp, &rppx1_hist_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_HIST_BASE) ||
+ rpp_module_probe(&rpp->post.db, rpp, &rppx1_db_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_FILT_BASE) ||
+ rpp_module_probe(&rpp->post.cac, rpp, &rppx1_cac_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_CAC_BASE) ||
+ rpp_module_probe(&rpp->post.ltm, rpp, &rppx1_ltm_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_BASE) ||
+ rpp_module_probe(&rpp->post.ltmmeas, rpp, &rppx1_ltmmeas_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_MEAS_BASE) ||
+ rpp_module_probe(&rpp->post.wbmeas, rpp, &rppx1_wbmeas_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_WBMEAS_BASE) ||
+ rpp_module_probe(&rpp->post.bdrgb, rpp, &rppx1_bdrgb_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_BDRGB_BASE) ||
+ rpp_module_probe(&rpp->post.shrp, rpp, &rppx1_shrp_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_SHRP_BASE))
+ goto err;
+
+ /* Probe the Human Vision pipeline. */
+ if (rpp_module_probe(&rpp->hv.ga, rpp, &rppx1_ga_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_GAMMA_OUT_BASE) ||
+ rpp_module_probe(&rpp->hv.is, rpp, &rppx1_is_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_IS_BASE) ||
+ rpp_module_probe(&rpp->hv.ccor, rpp, &rppx1_ccor_csm_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_CSM_BASE) ||
+ rpp_module_probe(&rpp->hv.outif, rpp, &rppx1_outif_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_OUT_IF_BASE) ||
+ rpp_module_probe(&rpp->hv.outregs, rpp, &rppx1_outregs_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_RPP_OUTREGS_BASE))
+ goto err;
+
+ /* Probe the Machine Vision pipeline. */
+ if (rpp_module_probe(&rpp->mv.ga, rpp, &rppx1_ga_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_GAMMA_OUT_BASE) ||
+ rpp_module_probe(&rpp->mv.is, rpp, &rppx1_is_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_IS_BASE) ||
+ rpp_module_probe(&rpp->mv.ccor, rpp, &rppx1_ccor_csm_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_CSM_BASE) ||
+ rpp_module_probe(&rpp->mv.outif, rpp, &rppx1_outif_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_OUT_IF_BASE) ||
+ rpp_module_probe(&rpp->mv.outregs, rpp, &rppx1_outregs_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_RPP_OUTREGS_BASE) ||
+ rpp_module_probe(&rpp->mv.xyz2luv, rpp, &rppx1_xyz2luv_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_LUV_BASE))
+ goto err;
+
+ /* Probe the standalone Radiance Mapping modules. */
+ if (rpp_module_probe(&rpp->rmap, rpp, &rppx1_rmap_ops,
+ RPPX1_RPP_RMAP_BASE) ||
+ rpp_module_probe(&rpp->rmapmeas, rpp, &rppx1_rmapmeas_ops,
+ RPPX1_RPP_RMAP_MEAS_BASE))
+ goto err;
+
+ return rpp;
+err:
+ rppx1_destroy(rpp);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rppx1_create);
+
+int rppx1_start(struct rppx1 *rpp,
+ const struct v4l2_mbus_framefmt *input,
+ const struct v4l2_mbus_framefmt *hv,
+ const struct v4l2_mbus_framefmt *mv)
+{
+ if (rpp_module_call(&rpp->pre1.acq, start, input) ||
+ rpp_module_call(&rpp->pre1.bls, start, input) ||
+ rpp_module_call(&rpp->pre1.lin, start, input) ||
+ rpp_module_call(&rpp->pre1.lsc, start, input) ||
+ rpp_module_call(&rpp->pre1.awbg, start, input) ||
+ rpp_module_call(&rpp->pre1.dpcc, start, input) ||
+ rpp_module_call(&rpp->pre1.bd, start, input) ||
+ rpp_module_call(&rpp->pre1.hist, start, input) ||
+ rpp_module_call(&rpp->pre1.exm, start, input) ||
+ rpp_module_call(&rpp->pre1.hist256, start, input))
+ return -EINVAL;
+
+ if (rpp_module_call(&rpp->rmap, start, NULL) ||
+ rpp_module_call(&rpp->rmapmeas, start, NULL))
+ return -EINVAL;
+
+ if (rpp_module_call(&rpp->post.awbg, start, input) ||
+ rpp_module_call(&rpp->post.db, start, input) ||
+ rpp_module_call(&rpp->post.cac, start, input) ||
+ rpp_module_call(&rpp->post.ccor, start, input) ||
+ rpp_module_call(&rpp->post.ltm, start, input) ||
+ rpp_module_call(&rpp->post.bdrgb, start, input) ||
+ rpp_module_call(&rpp->post.shrp, start, input) ||
+ rpp_module_call(&rpp->post.ltmmeas, start, input) ||
+ rpp_module_call(&rpp->post.wbmeas, start, input) ||
+ rpp_module_call(&rpp->post.hist, start, input))
+ return -EINVAL;
+
+ if (hv && (rpp_module_call(&rpp->hv.ga, start, hv) ||
+ rpp_module_call(&rpp->hv.ccor, start, hv) ||
+ rpp_module_call(&rpp->hv.outregs, start, hv) ||
+ rpp_module_call(&rpp->hv.is, start, hv) ||
+ rpp_module_call(&rpp->hv.outif, start, hv)))
+ return -EINVAL;
+
+ if (mv && (rpp_module_call(&rpp->mv.ga, start, mv) ||
+ rpp_module_call(&rpp->mv.ccor, start, mv) ||
+ rpp_module_call(&rpp->mv.xyz2luv, start, mv) ||
+ rpp_module_call(&rpp->mv.outregs, start, mv) ||
+ rpp_module_call(&rpp->mv.is, start, mv) ||
+ rpp_module_call(&rpp->mv.outif, start, mv)))
+ return -EINVAL;
+
+ rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, 0x00000001);
+
+ /* Clear fault interrupts. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG, 0x00000001);
+ rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSM, 0x000001c0);
+ rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC, rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS));
+ rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG, 0x00000000);
+
+ /* Set interrupt mask. */
+ rppx1_write(rpp, RPPX1_RPP_ISM, RPPX1_IRQ_ID_OUT_FRAME);
+
+ rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, 0x00000001);
+ rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, 0x00000002);
+
+ /* Clear any pending interrupts. */
+ rppx1_interrupt(rpp, NULL);
+
+ /* Enable input formatters. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rppx1_start);
+
+int rppx1_stop(struct rppx1 *rpp)
+{
+ /* Disable input formatters. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG, 0);
+
+ /* Clear any pending interrupts. */
+ rppx1_interrupt(rpp, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rppx1_stop);
+
+MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
+MODULE_DESCRIPTION("Dreamchip HDR RPPX1 support library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.h b/drivers/media/platform/dreamchip/rppx1/rppx1.h
new file mode 100644
index 000000000000..dcf43826d308
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __MEDIA_RPPX1_H__
+#define __MEDIA_RPPX1_H__
+
+#include <linux/types.h>
+
+#include "rpp_module.h"
+
+#define RPPX1_IRQ_ID_256HIST BIT(27)
+#define RPPX1_IRQ_ID_PRE2_DPCC BIT(25)
+#define RPPX1_IRQ_ID_PRE1_DPCC BIT(24)
+#define RPPX1_IRQ_ID_MV_OUT_FRAME_OUT BIT(23)
+#define RPPX1_IRQ_ID_MV_OUT_OFF BIT(22)
+#define RPPX1_IRQ_ID_POST_AWB_MEAS BIT(21)
+#define RPPX1_IRQ_ID_POST_HIST_MEAS BIT(20)
+#define RPPX1_IRQ_ID_POST_TM BIT(19)
+#define RPPX1_IRQ_ID_PRE1_EXM BIT(18)
+#define RPPX1_IRQ_ID_PRE1_HIST BIT(17)
+#define RPPX1_IRQ_ID_PRE1_FRAME_IN BIT(16)
+#define RPPX1_IRQ_ID_PRE1_HSTART BIT(15)
+#define RPPX1_IRQ_ID_PRE1_VSTART BIT(14)
+#define RPPX1_IRQ_ID_PRE2_EXM BIT(13)
+#define RPPX1_IRQ_ID_PRE2_HIST BIT(12)
+#define RPPX1_IRQ_ID_PRE2_FRAME_IN BIT(11)
+#define RPPX1_IRQ_ID_PRE2_HSTART BIT(10)
+#define RPPX1_IRQ_ID_PRE2_VSTART BIT(9)
+#define RPPX1_IRQ_ID_OUT_FRAME BIT(3)
+#define RPPX1_IRQ_ID_OUT_OFF BIT(2)
+#define RPPX1_IRQ_ID_RMAP_MEAS BIT(1)
+#define RPPX1_IRQ_ID_RMAP_DONE BIT(0)
+
+struct rppx1 {
+ struct device *dev;
+ void __iomem *base;
+
+ struct {
+ struct rpp_module acq;
+ struct rpp_module bls;
+ struct rpp_module lin;
+ struct rpp_module lsc;
+ struct rpp_module awbg;
+ struct rpp_module dpcc;
+ struct rpp_module bd;
+ struct rpp_module hist;
+ struct rpp_module hist256;
+ struct rpp_module exm;
+ } pre1;
+
+ struct {
+ struct rpp_module acq;
+ struct rpp_module bls;
+ struct rpp_module lin;
+ struct rpp_module lsc;
+ struct rpp_module awbg;
+ struct rpp_module dpcc;
+ struct rpp_module bd;
+ struct rpp_module hist;
+ struct rpp_module exm;
+ } pre2;
+
+ struct {
+ struct rpp_module awbg;
+ struct rpp_module ccor;
+ struct rpp_module hist;
+ struct rpp_module db;
+ struct rpp_module cac;
+ struct rpp_module ltm;
+ struct rpp_module ltmmeas;
+ struct rpp_module wbmeas;
+ struct rpp_module bdrgb;
+ struct rpp_module shrp;
+ } post;
+
+ struct {
+ struct rpp_module ga;
+ struct rpp_module is;
+ struct rpp_module ccor;
+ struct rpp_module outif;
+ struct rpp_module outregs;
+ } hv;
+
+ struct {
+ struct rpp_module ga;
+ struct rpp_module is;
+ struct rpp_module ccor;
+ struct rpp_module outif;
+ struct rpp_module outregs;
+ struct rpp_module xyz2luv;
+ } mv;
+
+ struct rpp_module rmap;
+ struct rpp_module rmapmeas;
+};
+
+void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value);
+u32 rppx1_read(struct rppx1 *rpp, u32 offset);
+
+#endif /* __MEDIA_RPPX1_H__ */
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
new file mode 100644
index 000000000000..45f619ccb684
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define ACQ_VERSION_REG 0x0000
+
+#define ACQ_CTRL_REG 0x0004
+#define ACQ_CTRL_ALTERNATIVE_CFG_MODE_ENABLE BIT(8)
+#define ACQ_CTRL_RPP_MODE_MASK GENMASK(3, 1)
+#define ACQ_CTRL_RPP_MODE_RAWBT601 (0 << 1)
+#define ACQ_CTRL_RPP_MODE_BT656 (1 << 1)
+#define ACQ_CTRL_RPP_MODE_BT601 (2 << 1)
+#define ACQ_CTRL_RPP_MODE_BAYER (3 << 1)
+#define ACQ_CTRL_RPP_MODE_DATA (4 << 1)
+#define ACQ_CTRL_RPP_MODE_BAYERRGB (5 << 1)
+#define ACQ_CTRL_RPP_MODE_RAWBT656 (6 << 1)
+#define ACQ_CTRL_INFORM_EN_ENABLE BIT(0)
+
+#define ACQ_PROP_REG 0x0008
+
+#define ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB BIT(30)
+#define ACQ_PROP_YUV_OUT_SEL BIT(25)
+#define ACQ_PROP_MUX_DMA_SEL BIT(24)
+#define ACQ_PROP_SECOND_INPUT_TYPE BIT(18)
+#define ACQ_PROP_LATENCY_FIFO_INPUT_SELECTION BIT(15)
+#define ACQ_PROP_INPUT_SELECTION_MASK GENMASK(14, 12)
+#define ACQ_PROP_INPUT_SELECTION_8BIT (0 << 12)
+#define ACQ_PROP_INPUT_SELECTION_10BIT (1 << 12)
+#define ACQ_PROP_INPUT_SELECTION_12BIT (2 << 12)
+#define ACQ_PROP_BAYER_PAT_MASK GENMASK(4, 3)
+#define ACQ_PROP_BAYER_PAT_RGRG (0 << 3)
+#define ACQ_PROP_BAYER_PAT_GRGR (1 << 3)
+#define ACQ_PROP_BAYER_PAT_GBGB (2 << 3)
+#define ACQ_PROP_BAYER_PAT_BGBG (3 << 3)
+#define ACQ_PROP_VSYNC_POL BIT(2)
+#define ACQ_PROP_HSYNC_POL BIT(1)
+#define ACQ_PROP_SAMPLE_EDGE BIT(0)
+
+#define ACQ_H_OFFS_REG 0x000c
+#define ACQ_V_OFFS_REG 0x0010
+#define ACQ_H_SIZE_REG 0x0014
+#define ACQ_V_SIZE_REG 0x0018
+#define ACQ_OUT_H_OFFS_REG 0x001c
+#define ACQ_OUT_V_OFFS_REG 0x0020
+#define ACQ_OUT_H_SIZE_REG 0x0024
+#define ACQ_OUT_V_SIZE_REG 0x0028
+#define FLAGS_SHD_REG 0x002c
+#define ACQ_OUT_H_OFFS_SHD_REG 0x0030
+#define ACQ_OUT_V_OFFS_SHD_REG 0x0034
+#define ACQ_OUT_H_SIZE_SHD_REG 0x0038
+#define ACQ_OUT_V_SIZE_SHD_REG 0x003c
+
+static int rppx1_acq_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, ACQ_VERSION_REG) != 0x0b)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_acq_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ u32 bayerpat, selection;
+
+ rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_RPP_MODE_MASK,
+ ACQ_CTRL_RPP_MODE_BAYER);
+
+ rpp_module_write(mod, ACQ_H_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_V_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_H_SIZE_REG, fmt->width);
+ rpp_module_write(mod, ACQ_V_SIZE_REG, fmt->height);
+ rpp_module_write(mod, ACQ_OUT_H_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_OUT_V_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_OUT_H_SIZE_REG, fmt->width);
+ rpp_module_write(mod, ACQ_OUT_V_SIZE_REG, fmt->height);
+
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ mod->info.acq.raw_pattern = RPP_BGGR;
+ bayerpat = ACQ_PROP_BAYER_PAT_BGBG;
+ break;
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ mod->info.acq.raw_pattern = RPP_GBRG;
+ bayerpat = ACQ_PROP_BAYER_PAT_GBGB;
+ break;
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ mod->info.acq.raw_pattern = RPP_GRBG;
+ bayerpat = ACQ_PROP_BAYER_PAT_GRGR;
+ break;
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ mod->info.acq.raw_pattern = RPP_RGGB;
+ bayerpat = ACQ_PROP_BAYER_PAT_RGRG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ selection = ACQ_PROP_INPUT_SELECTION_8BIT;
+ break;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ selection = ACQ_PROP_INPUT_SELECTION_10BIT;
+ break;
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ selection = ACQ_PROP_INPUT_SELECTION_12BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rpp_module_write(mod, ACQ_PROP_REG, bayerpat | selection |
+ ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB);
+
+ rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_INFORM_EN_ENABLE,
+ ACQ_CTRL_INFORM_EN_ENABLE);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_acq_ops = {
+ .probe = rppx1_acq_probe,
+ .start = rppx1_acq_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
new file mode 100644
index 000000000000..e20bc369ca8c
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define AWB_GAIN_VERSION_REG 0x0000
+
+#define AWB_ENABLE_REG 0x0004
+#define AWB_ENABLE_AWB_GAIN_EN BIT(0)
+
+#define AWB_GAIN_GR_REG 0x0008
+#define AWB_GAIN_GB_REG 0x000c
+#define AWB_GAIN_R_REG 0x0010
+#define AWB_GAIN_B_REG 0x0014
+
+static int rppx1_awbg_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, AWB_GAIN_VERSION_REG) != 3)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_awbg_ops = {
+ .probe = rppx1_awbg_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
new file mode 100644
index 000000000000..acbfbcd59591
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define DPF_VERSION_REG 0x0000
+
+#define DPF_MODE_REG 0x0004
+#define DPF_MODE_USE_NF_GAIN BIT(9)
+#define DPF_MODE_LSC_GAIN_COMP BIT(8)
+#define DPF_MODE_NLL_SEGMENTATION BIT(6)
+#define DPF_MODE_RB_FILTER_SIZE BIT(5)
+#define DPF_MODE_R_FILTER_OFF BIT(4)
+#define DPF_MODE_GR_FILTER_OFF BIT(3)
+#define DPF_MODE_GB_FILTER_OFF BIT(2)
+#define DPF_MODE_B_FILTER_OFF BIT(1)
+#define DPF_MODE_DPF_ENABLE BIT(0)
+
+#define DPF_STRENGTH_R_REG 0x0008
+#define DPF_STRENGTH_G_REG 0x000c
+#define DPF_STRENGTH_B_REG 0x0010
+#define DPF_S_WEIGHT_G_1_4_REG 0x0014
+#define DPF_S_WEIGHT_G_5_6_REG 0x0018
+#define DPF_S_WEIGHT_RB_1_4_REG 0x001c
+#define DPF_S_WEIGHT_RB_5_6_REG 0x0020
+
+#define DPF_NLL_G_COEFF_REG_NUM 17
+#define DPF_NLL_G_COEFF_REG(n) (0x0024 + (4 * (n)))
+
+#define DPF_NLL_RB_COEFF_REG_NUM 17
+#define DPF_NLL_RB_COEFF_REG(n) (0x0068 + (4 * (n)))
+
+#define DPF_NF_GAIN_R_REG 0x00ac
+#define DPF_NF_GAIN_GR_REG 0x00b0
+#define DPF_NF_GAIN_GB_REG 0x00b4
+#define DPF_NF_GAIN_B_REG 0x00b8
+
+static int rppx1_bd_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, DPF_VERSION_REG) != 5)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_bd_ops = {
+ .probe = rppx1_bd_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
new file mode 100644
index 000000000000..292f0b7bfd3f
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define RGBDENOISE_VERSION_REG 0x0000
+
+#define RGBDENOISE_HW_BYPASS_REG 0x0004
+#define RGBDENOISE_HW_BYPASS_BYPASS_EN BIT(0)
+
+#define RGBDENOISE_SPNR_CTRL_REG 0x0008
+#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_C_MASK GENMASK(11, 8)
+#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_Y_MASK GENMASK(7, 4)
+#define RGBDENOISE_SPNR_CTRL_C2NR_EN BIT(0)
+
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_REG 0x000c
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_REG 0x0010
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_REG 0x0014
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_REG 0x0018
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_REG 0x001c
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_REG 0x0020
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_REG 0x0024
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_REG 0x0028
+#define RGBDENOISE_SPNR_SPATIAL_COEF_0_3_REG 0x002c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_REG 0x0030
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_REG 0x0034
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_REG 0x0038
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_REG 0x003c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_REG 0x0040
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_REG 0x0044
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_REG 0x0048
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_REG 0x004c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_REG 0x0050
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_REG 0x0054
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_REG 0x0058
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_REG 0x005c
+#define RGBDENOISE_HW_BYPASS_SDW_REG 0x0060
+#define RGBDENOISE_SPNR_CTRL_SDW_REG 0x0064
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_SDW_REG 0x0068
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_SDW_REG 0x006c
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_SDW_REG 0x0070
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_SDW_REG 0x0074
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_SDW_REG 0x0078
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_SDW_REG 0x007c
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_SDW_REG 0x0080
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_SDW_REG 0x0084
+#define RGBDENOISE_SPNR_SPATIAL_COEFF_0_3_SDW_REG 0x0088
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_SDW_REG 0x008c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_SDW_REG 0x0090
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_SDW_REG 0x0094
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_SDW_REG 0x0098
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_SDW_REG 0x009c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_SDW_REG 0x00a0
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_SDW_REG 0x00a4
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_SDW_REG 0x00a8
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_SDW_REG 0x00ac
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_SDW_REG 0x00b0
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_SDW_REG 0x00b4
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_SDW_REG 0x00b8
+
+static int rppx1_bdrgb_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, RGBDENOISE_VERSION_REG)) {
+ case 6:
+ mod->info.bdrgb.colorbits = 12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_bdrgb_ops = {
+ .probe = rppx1_bdrgb_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
new file mode 100644
index 000000000000..de7008befd8e
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define BLS_VERSION_REG 0x0000
+
+#define BLS_CTRL_REG 0x0004
+#define BLS_CTRL_BLS_WIN2 BIT(3)
+#define BLS_CTRL_BLS_WIN1 BIT(2)
+#define BLS_CTRL_BLS_MODE_MEASURED BIT(1)
+#define BLS_CTRL_BLS_EN BIT(0)
+
+#define BLS_SAMPLES_REG 0x0008
+#define BLS_H1_START_REG 0x000c
+#define BLS_H1_STOP_REG 0x0010
+#define BLS_V1_START_REG 0x0014
+#define BLS_V1_STOP_REG 0x0018
+#define BLS_H2_START_REG 0x001c
+#define BLS_H2_STOP_REG 0x0020
+#define BLS_V2_START_REG 0x0024
+#define BLS_V2_STOP_REG 0x0028
+#define BLS_A_FIXED_REG 0x002c
+#define BLS_B_FIXED_REG 0x0030
+#define BLS_C_FIXED_REG 0x0034
+#define BLS_D_FIXED_REG 0x0038
+#define BLS_A_MEASURED_REG 0x003c
+#define BLS_B_MEASURED_REG 0x0040
+#define BLS_C_MEASURED_REG 0x0044
+#define BLS_D_MEASURED_REG 0x0048
+
+static int rppx1_bls_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, BLS_VERSION_REG)) {
+ case 3:
+ case 5:
+ mod->info.bls.colorbits = 12;
+ break;
+ case 2:
+ case 4:
+ mod->info.bls.colorbits = 20;
+ break;
+ case 6:
+ mod->info.bls.colorbits = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_bls_ops = {
+ .probe = rppx1_bls_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
new file mode 100644
index 000000000000..5ed8c60982ba
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define CAC_VERSION_REG 0x0000
+#define CAC_CTRL_REG 0x0004
+#define CAC_COUNT_START_REG 0x0008
+#define CAC_A_REG 0x000c
+#define CAC_B_REG 0x0010
+#define CAC_C_REG 0x0014
+#define CAC_X_NORM_REG 0x0018
+#define CAC_Y_NORM_REG 0x001c
+
+static int rppx1_cac_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, CAC_VERSION_REG) != 3)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_cac_ops = {
+ .probe = rppx1_cac_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
new file mode 100644
index 000000000000..4754b0bbce0a
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define CCOR_VERSION_REG 0x0000
+
+#define CCOR_COEFF_REG_NUM 9
+#define CCOR_COEFF_REG(n) (0x0004 + (4 * (n)))
+
+#define CCOR_OFFSET_R_REG 0x0028
+#define CCOR_OFFSET_G_REG 0x002c
+#define CCOR_OFFSET_B_REG 0x0030
+
+#define CCOR_CONFIG_TYPE_REG 0x0034
+#define CCOR_CONFIG_TYPE_USE_OFFSETS_AS_PRE_OFFSETS BIT(1)
+#define CCOR_CONFIG_TYPE_CCOR_RANGE_AVAILABLE BIT(0)
+
+#define CCOR_RANGE_REG 0x0038
+#define CCOR_RANGE_CCOR_C_RANGE BIT(1)
+#define CCOR_RANGE_CCOR_Y_RANGE BIT(0)
+
+static int rppx1_ccor_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, CCOR_VERSION_REG)) {
+ case 3:
+ mod->info.ccor.colorbits = 12;
+ break;
+ case 4:
+ mod->info.ccor.colorbits = 20;
+ break;
+ case 5:
+ mod->info.ccor.colorbits = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mod->info.ccor.type = rpp_module_read(mod, CCOR_CONFIG_TYPE_REG);
+
+ return 0;
+}
+
+static int rppx1_ccor_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Configure matrix in bypass mode. */
+ rpp_module_write(mod, CCOR_COEFF_REG(0), 0x1000);
+ rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0000);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(3), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(4), 0x1000);
+ rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0000);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(7), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(8), 0x1000);
+
+ rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000);
+ rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000000);
+ rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000000);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ccor_ops = {
+ .probe = rppx1_ccor_probe,
+ .start = rppx1_ccor_start,
+};
+
+static int rppx1_ccor_csm_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Reuse bypass matrix setup. */
+ if (fmt->code == MEDIA_BUS_FMT_RGB888_1X24)
+ return rppx1_ccor_start(mod, fmt);
+
+ /* Color Transformation RGB to YUV according to ITU-R BT.709. */
+ rpp_module_write(mod, CCOR_COEFF_REG(0), 0x0367);
+ rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0b71);
+ rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0128);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(3), 0xfe2b);
+ rpp_module_write(mod, CCOR_COEFF_REG(4), 0xf9d5);
+ rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0800);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0800);
+ rpp_module_write(mod, CCOR_COEFF_REG(7), 0xf8bc);
+ rpp_module_write(mod, CCOR_COEFF_REG(8), 0xff44);
+
+ rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000);
+ rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000800);
+ rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000800);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ccor_csm_ops = {
+ .probe = rppx1_ccor_probe,
+ .start = rppx1_ccor_csm_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
new file mode 100644
index 000000000000..5e233896cfc8
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define FILT_VERSION_REG 0x0000
+
+#define DEMOSAIC_REG 0x0004
+#define DEMOSAIC_DEMOSAIC_BYPASS BIT(16)
+#define DEMOSAIC_DEMOSAIC_TH_MASK GENMASK(15, 0)
+
+#define FILT_MODE_REG 0x0008
+#define FILT_MODE_FILT_LP_SELECT_MASK GENMASK(11, 8)
+#define FILT_MODE_FILT_CHR_H_MODE_MASK GENMASK(7, 6)
+#define FILT_MODE_FILT_CHR_V_MODE_MASK GENMASK(5, 4)
+#define FILT_MODE_FILT_MODE BIT(1)
+#define FILT_MODE_FILT_ENABLE BIT(0)
+
+#define FILT_THRESH_BL0_REG 0x000c
+#define FILT_THRESH_BL1_REG 0x0010
+#define FILT_THRESH_SH0_REG 0x0014
+#define FILT_THRESH_SH1_REG 0x0018
+#define FILT_LUM_WEIGHT_REG 0x001c
+#define FILT_FAC_SH1_REG 0x0020
+#define FILT_FAC_SH0_REG 0x0024
+#define FILT_FAC_MID_REG 0x0028
+#define FILT_FAC_BL0_REG 0x002c
+#define FILT_FAC_BL1_REG 0x0030
+
+static int rppx1_db_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, FILT_VERSION_REG) != 5)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_db_ops = {
+ .probe = rppx1_db_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
new file mode 100644
index 000000000000..ae0b65976452
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define DPCC_VERSION_REG 0x0000
+
+#define DPCC_MODE_REG 0x0004
+#define DPCC_MODE_STAGE1_ENABLE BIT(2)
+#define DPCC_MODE_GRAYSCALE_MODE BIT(1)
+#define DPCC_MODE_DPCC_ENABLE BIT(0)
+
+#define DPCC_OUTPUT_MODE_REG 0x0008
+#define DPCC_SET_USE_REG 0x000c
+#define DPCC_METHODS_SET_1_REG 0x0010
+#define DPCC_METHODS_SET_2_REG 0x0014
+#define DPCC_METHODS_SET_3_REG 0x0018
+#define DPCC_LINE_THRESH_1_REG 0x001c
+#define DPCC_LINE_MAD_FAC_1_REG 0x0020
+#define DPCC_PG_FAC_1_REG 0x0024
+#define DPCC_RND_THRESH_1_REG 0x0028
+#define DPCC_RG_FAC_1_REG 0x002c
+#define DPCC_LINE_THRESH_2_REG 0x0030
+#define DPCC_LINE_MAD_FAC_2_REG 0x0034
+#define DPCC_PG_FAC_2_REG 0x0038
+#define DPCC_RND_THRESH_2_REG 0x003c
+#define DPCC_RG_FAC_2_REG 0x0040
+#define DPCC_LINE_THRESH_3_REG 0x0044
+#define DPCC_LINE_MAD_FAC_3_REG 0x0048
+#define DPCC_PG_FAC_3_REG 0x004c
+#define DPCC_RND_THRESH_3_REG 0x0050
+#define DPCC_RG_FAC_3_REG 0x0054
+#define DPCC_RO_LIMITS_REG 0x0058
+#define DPCC_RND_OFFS_REG 0x005c
+#define DPCC_BPT_CTRL_REG 0x0060
+#define DPCC_BP_NUMBER_REG 0x0064
+#define DPCC_BP_TADDR_REG 0x0068
+#define DPCC_BP_POSITION_REG 0x006c
+
+static int rppx1_dpcc_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, DPCC_VERSION_REG)) {
+ case 2:
+ case 4:
+ case 6:
+ mod->info.dpcc.colorbits = 12;
+ break;
+ case 3:
+ case 5:
+ case 7:
+ mod->info.dpcc.colorbits = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_dpcc_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Bypass stage1 and DPCC. */
+ rpp_module_write(mod, DPCC_MODE_REG, 0);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_dpcc_ops = {
+ .probe = rppx1_dpcc_probe,
+ .start = rppx1_dpcc_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
new file mode 100644
index 000000000000..0c40300e13ad
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define EXM_VERSION_REG 0x0000
+#define EXM_START_REG 0x0004
+
+#define EXM_CTRL_REG 0x0008
+#define EXM_CTRL_EXM_UPDATE_ENABLE BIT(0)
+
+#define EXM_MODE_REG 0x000c
+#define EXM_CHANNEL_SEL_REG 0x0010
+#define EXM_LAST_MEAS_LINE_REG 0x0014
+#define EXM_COEFF_R_REG 0x0018
+#define EXM_COEFF_G_GR_REG 0x001c
+#define EXM_COEFF_B_REG 0x0020
+#define EXM_COEFF_GB_REG 0x0024
+#define EXM_H_OFFS_REG 0x0028
+#define EXM_V_OFFS_REG 0x002c
+#define EXM_H_SIZE_REG 0x0030
+#define EXM_V_SIZE_REG 0x0034
+#define EXM_FORCED_UPD_START_LINE_REG 0x0038
+#define EXM_VSTART_STATUS_REG 0x003c
+
+#define EXM_MEAN_REG_NUM 25
+#define EXM_MEAN_REG(n) (0x0040 + (4 * (n)))
+
+static int rppx1_exm_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, EXM_VERSION_REG)) {
+ case 1:
+ mod->info.exm.resultbits = 8;
+ break;
+ case 3:
+ mod->info.exm.resultbits = 20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_exm_ops = {
+ .probe = rppx1_exm_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
new file mode 100644
index 000000000000..d6c7f951cf29
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define GAMMA_OUT_VERSION_REG 0x0000
+
+#define GAMMA_OUT_ENABLE_REG 0x0004
+#define GAMMA_OUT_ENABLE_GAMMA_OUT_EN BIT(0)
+
+#define GAMMA_OUT_MODE_REG 0x0008
+#define GAMMA_OUT_MODE_GAMMA_OUT_EQU_SEGM BIT(0)
+
+#define GAMMA_OUT_Y_REG_NUM 17
+#define GAMMA_OUT_Y_REG(n) (0x000c + (4 * (n)))
+
+static int rppx1_ga_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, GAMMA_OUT_VERSION_REG)) {
+ case 1:
+ mod->info.ga.colorbits = 12;
+ break;
+ case 2:
+ mod->info.ga.colorbits = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_ga_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Disable stage. */
+ rpp_module_write(mod, GAMMA_OUT_ENABLE_REG, 0);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ga_ops = {
+ .probe = rppx1_ga_probe,
+ .start = rppx1_ga_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
new file mode 100644
index 000000000000..cab498ece5a8
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define HIST_VERSION_REG 0x0000
+
+#define HIST_CTRL_REG 0x0004
+#define HIST_CTRL_HIST_UPDATE_ENABLE BIT(0)
+
+#define HIST_MODE_REG 0x0008
+#define HIST_MODE_HIST_MODE_MASK GENMASK(2, 0)
+#define HIST_MODE_HIST_MODE_DISABLE 0
+#define HIST_MODE_HIST_MODE_YRGB 1
+#define HIST_MODE_HIST_MODE_R 2
+#define HIST_MODE_HIST_MODE_GR 3
+#define HIST_MODE_HIST_MODE_B 4
+#define HIST_MODE_HIST_MODE_GB 5
+
+#define HIST_CHANNEL_SEL_REG 0x000c
+#define HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0)
+
+#define HIST_LAST_MEAS_LINE_REG 0x0010
+#define HIST_SUBSAMPLING_REG 0x0014
+#define HIST_COEFF_R_REG 0x0018
+#define HIST_COEFF_G_REG 0x001c
+#define HIST_COEFF_B_REG 0x0020
+#define HIST_H_OFFS_REG 0x0024
+#define HIST_V_OFFS_REG 0x0028
+#define HIST_H_SIZE_REG 0x002c
+#define HIST_V_SIZE_REG 0x0030
+
+#define HIST_SAMPLE_RANGE_REG 0x0034
+#define HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK GENMASK(28, 24)
+#define HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK GENMASK(23, 0)
+
+#define HIST_WEIGHT_00TO30_REG 0x0038
+#define HIST_WEIGHT_40TO21_REG 0x003c
+#define HIST_WEIGHT_31TO12_REG 0x0040
+#define HIST_WEIGHT_22TO03_REG 0x0044
+#define HIST_WEIGHT_13TO43_REG 0x0048
+#define HIST_WEIGHT_04TO34_REG 0x004c
+#define HIST_WEIGHT_44_REG 0x0050
+#define HIST_FORCED_UPD_START_LINE_REG 0x0054
+#define HIST_FORCED_UPDATE_REG 0x0058
+#define HIST_VSTART_STATUS_REG 0x005c
+
+#define HIST_BIN_REG_NUM 32
+#define HIST_BIN_REG(n) (0x0060 + (4 * (n)))
+
+static int rppx1_hist_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, HIST_VERSION_REG)) {
+ case 3:
+ mod->info.hist.colorbits = 12;
+ break;
+ case 4:
+ mod->info.hist.colorbits = 20;
+ break;
+ case 5:
+ mod->info.hist.colorbits = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_hist_ops = {
+ .probe = rppx1_hist_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
new file mode 100644
index 000000000000..5b846b415a49
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define HIST256_VERSION_REG 0x0000
+#define HIST256_MODE_REG 0x0004
+#define HIST256_MODE_HIST256_MODE BIT(0)
+
+#define HIST256_CHANNEL_SEL_REG 0x0008
+#define HIST256_CHANNEL_SEL_CHANNEL_SELECT GENMASK(2, 0)
+
+#define HIST256_H_OFFS_REG 0x000c
+#define HIST256_V_OFFS_REG 0x0010
+#define HIST256_H_SIZE_REG 0x0014
+#define HIST256_V_SIZE_REG 0x0018
+#define HIST256_SAMPLE_OFFSET_REG 0x001c
+#define HIST256_SAMPLE_SCALE_REG 0x0020
+#define HIST256_MEAS_RESULT_ADDR_AUTOINCR_REG 0x0024
+#define HIST256_MEAS_RESULT_ADDR_REG 0x0028
+#define HIST256_MEAS_RESULT_DATA_REG 0x002c
+
+#define HIST256_LOG_ENABLE_REG 0x0030
+#define HIST256_LOG_ENABLE_HIST256_LOG_EN BIT(0)
+
+#define HIST256_LOG_DX_LO_REG 0x0034
+#define HIST256_LOG_DX_HI_REG 0x0038
+
+#define HIST256_Y_REG_NUM 17
+#define HIST256_Y_REG(n) (0x0040 + (4 * (n)))
+
+static int rppx1_hist256_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, HIST256_VERSION_REG) != 2)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_hist256_ops = {
+ .probe = rppx1_hist256_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_is.c b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c
new file mode 100644
index 000000000000..3637a2e677ca
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define IS_VERSION 0x0000
+#define IS_H_OFFS 0x0008
+#define IS_V_OFFS 0x000c
+#define IS_H_SIZE 0x0010
+#define IS_V_SIZE 0x0014
+#define IS_H_OFFS_SHD 0x0024
+#define IS_V_OFFS_SHD 0x0028
+#define IS_H_SIZE_SHD 0x002c
+#define IS_V_SIZE_SHD 0x0030
+
+static int rppx1_is_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, IS_VERSION) != 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_is_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ rpp_module_write(mod, IS_H_OFFS, 0);
+ rpp_module_write(mod, IS_V_OFFS, 0);
+ rpp_module_write(mod, IS_H_SIZE, fmt->width);
+ rpp_module_write(mod, IS_V_SIZE, fmt->height);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_is_ops = {
+ .probe = rppx1_is_probe,
+ .start = rppx1_is_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
new file mode 100644
index 000000000000..f595f56a292e
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+/* NOTE: The module is called LIN the registers GAMMA_IN. */
+#define LIN_VERSION_REG 0x0000
+
+#define LIN_ENABLE_REG 0x0004
+#define LIN_ENABLE_GAMMA_IN_EN BIT(0)
+
+#define LIN_DX_LO_REG 0x0008
+#define LIN_DX_HI_REG 0x000c
+
+#define LIN_R_Y_REG_NUM 17
+#define LIN_R_Y_REG(n) (0x0010 + (4 * (n)))
+
+#define LIN_G_Y_REG_NUM 17
+#define LIN_G_Y_REG(n) (0x0054 + (4 * (n)))
+
+#define LIN_B_Y_REG_NUM 17
+#define LIN_B_Y_REG(n) (0x0098 + (4 * (n)))
+
+static int rppx1_lin_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, LIN_VERSION_REG)) {
+ case 7:
+ mod->info.lin.colorbits = 12;
+ break;
+ case 8:
+ mod->info.lin.colorbits = 20;
+ break;
+ case 9:
+ mod->info.lin.colorbits = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_lin_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ rpp_module_clrset(mod, LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN, 0);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_lin_ops = {
+ .probe = rppx1_lin_probe,
+ .start = rppx1_lin_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
new file mode 100644
index 000000000000..e8acdf744956
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define LSC_VERSION_REG 0x0000
+
+#define LSC_CTRL_REG 0x0004
+#define LSC_CTRL_LSC_EN BIT(0)
+
+#define LSC_R_TABLE_ADDR_REG 0x0008
+#define LSC_GR_TABLE_ADDR_REG 0x000c
+#define LSC_B_TABLE_ADDR_REG 0x0010
+#define LSC_GB_TABLE_ADDR_REG 0x0014
+#define LSC_R_TABLE_DATA_REG 0x0018
+#define LSC_GR_TABLE_DATA_REG 0x001c
+#define LSC_B_TABLE_DATA_REG 0x0020
+#define LSC_GB_TABLE_DATA_REG 0x0024
+#define LSC_XGRAD_01_REG 0x0028
+#define LSC_XGRAD_23_REG 0x002c
+#define LSC_XGRAD_45_REG 0x0030
+#define LSC_XGRAD_67_REG 0x0034
+#define LSC_XGRAD_89_REG 0x0038
+#define LSC_XGRAD_1011_REG 0x003c
+#define LSC_XGRAD_1213_REG 0x0040
+#define LSC_XGRAD_1415_REG 0x0044
+#define LSC_YGRAD_01_REG 0x0048
+#define LSC_YGRAD_23_REG 0x004c
+#define LSC_YGRAD_45_REG 0x0050
+#define LSC_YGRAD_67_REG 0x0054
+#define LSC_YGRAD_89_REG 0x0058
+#define LSC_YGRAD_1011_REG 0x005c
+#define LSC_YGRAD_1213_REG 0x0060
+#define LSC_YGRAD_1415_REG 0x0064
+#define LSC_XSIZE_01_REG 0x0068
+#define LSC_XSIZE_23_REG 0x006c
+#define LSC_XSIZE_45_REG 0x0070
+#define LSC_XSIZE_67_REG 0x0074
+#define LSC_XSIZE_89_REG 0x0078
+#define LSC_XSIZE_1011_REG 0x007c
+#define LSC_XSIZE_1213_REG 0x0080
+#define LSC_XSIZE_1415_REG 0x0084
+#define LSC_YSIZE_01_REG 0x0088
+#define LSC_YSIZE_23_REG 0x008c
+#define LSC_YSIZE_45_REG 0x0090
+#define LSC_YSIZE_67_REG 0x0094
+#define LSC_YSIZE_89_REG 0x0098
+#define LSC_YSIZE_1011_REG 0x009c
+#define LSC_YSIZE_1213_REG 0x00a0
+#define LSC_YSIZE_1415_REG 0x00a4
+#define LSC_TABLE_SEL_REG 0x00a8
+#define LSC_STATUS_REG 0x00ac
+
+static int rppx1_lsc_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, LSC_VERSION_REG) != 0x04)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_lsc_ops = {
+ .probe = rppx1_lsc_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
new file mode 100644
index 000000000000..693cf5ed1689
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define LTM_VERSION_REG 0x0000
+
+#define LTM_CTRL_REG 0x0004
+#define LTM_CTRL_LTM_ENABLE BIT(0)
+
+#define LTM_RGB_WEIGHTS_REG 0x0008
+#define LTM_CLB_LINESIZE_REG 0x000c
+#define LTM_TONECURVE_1_REG 0x0010
+#define LTM_TONECURVE_2_REG 0x0014
+#define LTM_TONECURVE_3_REG 0x0018
+#define LTM_TONECURVE_4_REG 0x001c
+#define LTM_TONECURVE_5_REG 0x0020
+#define LTM_TONECURVE_6_REG 0x0024
+#define LTM_TONECURVE_YM_REG(n) (0x0028 + (4 * (n)))
+#define LTM_L0W_REG 0x00ec
+#define LTM_L0W_R_REG 0x00f0
+#define LTM_L0D_REG 0x00f4
+#define LTM_L0D_R_REG 0x00f8
+#define LTM_KMIND_REG 0x00fc
+#define LTM_KMAXD_REG 0x0100
+#define LTM_KDIFFD_REG 0x0104
+#define LTM_KDIFFD_R_REG 0x0108
+#define LTM_KW_REG 0x010c
+#define LTM_KW_R_REG 0x0110
+#define LTM_CGAIN_REG 0x0114
+#define LTM_LPRCH_R_HIGH_REG 0x0118
+#define LTM_LPRCH_R_LOW_REG 0x011c
+
+static int rppx1_ltm_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, LTM_VERSION_REG) != 8)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ltm_ops = {
+ .probe = rppx1_ltm_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
new file mode 100644
index 000000000000..efc3d09db5eb
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define LTM_MEAS_VERSION_REG 0x0000
+
+#define LTM_MEAS_CTRL_REG 0x0004
+#define LTM_MEAS_CTRL_LTM_MEAS_ENABLE BIT(0)
+
+#define LTM_MEAS_RGB_WEIGHTS_REG 0x0008
+#define LTM_MEAS_H_OFFS_REG 0x000c
+#define LTM_MEAS_V_OFFS_REG 0x0010
+#define LTM_MEAS_H_SIZE_REG 0x0014
+#define LTM_MEAS_V_SIZE_REG 0x0018
+
+#define LTM_MEAS_PRC_THRESH_NUM 8
+#define LTM_MEAS_PRC_THRESH_REG(n) (0x001c + (4 * (n)))
+
+#define LTM_MEAS_PRC_REG_NUM 8
+#define LTM_MEAS_PRC_REG(n) (0x003c + (4 * (n)))
+
+#define LTM_MEAS_L_MIN_REG 0x005c
+#define LTM_MEAS_L_MAX_REG 0x0060
+#define LTM_MEAS_L_GMEAN_REG 0x0064
+
+static int rppx1_ltmmeas_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, LTM_MEAS_VERSION_REG) != 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ltmmeas_ops = {
+ .probe = rppx1_ltmmeas_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
new file mode 100644
index 000000000000..742c81844912
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define OUT_IF_VERSION_REG 0x0000
+
+#define OUT_IF_ON_REG 0x0004
+#define OUT_IF_ON_RPP_ON BIT(0)
+
+#define OUT_IF_OFF_REG 0x0008
+
+#define OUT_IF_NR_FRAMES_REG 0x000c
+#define OUT_IF_NR_FRAMES_NR_FRAMES GENMASK(9, 0)
+
+#define OUT_IF_NR_FRAMES_CNT_REG 0x0010
+#define FLAGS_SHD_REG 0x0018
+
+static int rppx1_outif_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, OUT_IF_VERSION_REG) != 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_outif_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ rpp_module_clrset(mod, OUT_IF_NR_FRAMES_REG,
+ OUT_IF_NR_FRAMES_NR_FRAMES, 0);
+
+ rpp_module_write(mod, OUT_IF_ON_REG, OUT_IF_ON_RPP_ON);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_outif_ops = {
+ .probe = rppx1_outif_probe,
+ .start = rppx1_outif_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
new file mode 100644
index 000000000000..63d61e1dc447
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define OUTREGS_VERSION_REG 0x0000
+
+#define OUT_MODE_REG 0x0004
+#define OUT_MODE_UNSELECTED_MODE_MASK GENMASK(11, 8)
+#define OUT_MODE_UNSELECTED_MODE_MAIN (0x1 << 8)
+#define OUT_MODE_UNSELECTED_MODE_PRE1 (0x2 << 8)
+#define OUT_MODE_UNSELECTED_MODE_PRE2 (0x4 << 8)
+#define OUT_MODE_IN_SEL_MASK GENMASK(3, 0)
+#define OUT_MODE_IN_SEL_MAIN 1
+#define OUT_MODE_IN_SEL_PRE1 2
+#define OUT_MODE_IN_SEL_PRE2 4
+
+#define OUT_CONV_422_METHOD_REG 0x0008
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_MASK GENMASK(1, 0)
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1 0
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED2 1
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_NON_CO_SITED 2
+
+#define OUTREGS_FORMAT_REG 0x000c
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_MASK GENMASK(1, 0)
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_RGB 0
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422 1
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV420 2
+
+static int rppx1_outregs_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, OUTREGS_VERSION_REG) != 2)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_outregs_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ u32 format;
+
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_YUYV12_1X24:
+ format = OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ format = OUTREGS_FORMAT_OUTPUT_FORMAT_RGB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rpp_module_clrset(mod, OUT_MODE_REG,
+ OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MASK,
+ OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MAIN);
+
+ rpp_module_clrset(mod, OUT_CONV_422_METHOD_REG,
+ OUT_CONV_422_METHOD_CONV_422_METHOD_MASK,
+ OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1);
+
+ rpp_module_clrset(mod, OUTREGS_FORMAT_REG,
+ OUTREGS_FORMAT_OUTPUT_FORMAT_MASK, format);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_outregs_ops = {
+ .probe = rppx1_outregs_probe,
+ .start = rppx1_outregs_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
new file mode 100644
index 000000000000..f6773a452bd1
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define RMAP_DATA_VERSION_REG 0x0000
+
+#define RMAP_CTRL_REG 0x0004
+#define RMAP_CTRL_BYPASS_LONG BIT(2)
+
+#define RMAP_WBTHRESHOLD_LONG_REG 0x0008
+#define RMAP_WBTHRESHOLD_SHORT_REG 0x000c
+#define RMAP_RESERVED_1_REG 0x0010
+#define RMAP_WBGAIN_LONG_RED_REG 0x0014
+#define RMAP_WBGAIN_LONG_BLUE_REG 0x0018
+#define RMAP_WBGAIN_SHORT_RED_REG 0x001c
+#define RMAP_WBGAIN_SHORT_BLUE_REG 0x0020
+#define RMAP_RESERVED_2_REG 0x0024
+#define RMAP_RESERVED_3_REG 0x0028
+#define RMAP_MAP_FAC_SHORT_REG 0x002c
+#define RMAP_RESERVED_4_REG 0x0030
+#define RMAP_MIN_THRES_SHORT_REG 0x0034
+#define RMAP_MAX_THRES_SHORT_REG 0x0038
+#define RMAP_STEPSIZE_SHORT_REG 0x003c
+#define RMAP_MIN_THRES_LONG_REG 0x0040
+#define RMAP_MAX_THRES_LONG_REG 0x0044
+#define RMAP_STEPSIZE_LONG_REG 0x0048
+#define RMAP_CLB_LINESIZE_REG 0x004c
+
+static int rppx1_rmap_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, RMAP_DATA_VERSION_REG)) {
+ case 8:
+ mod->info.rmap.colorbits_high = 20;
+ mod->info.rmap.colorbits_low = 12;
+ break;
+ case 9:
+ mod->info.rmap.colorbits_high = 24;
+ mod->info.rmap.colorbits_low = 12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_rmap_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Bypass radiance mapping and use the long exposure channel (PRE1). */
+ rpp_module_write(mod, RMAP_CTRL_REG, RMAP_CTRL_BYPASS_LONG);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_rmap_ops = {
+ .probe = rppx1_rmap_probe,
+ .start = rppx1_rmap_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
new file mode 100644
index 000000000000..c04f92508f6d
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define RMAP_MEAS_VERSION_REG 0x0000
+#define RMAP_MEAS_MODE_REG 0x0004
+#define RMAP_MEAS_SUBSAMPLING_REG 0x0008
+#define RMAP_MEAS_RESERVED_1_REG 0x000c
+#define RMAP_MEAS_MIN_THRES_SHORT_REG 0x0010
+#define RMAP_MEAS_MAX_THRES_SHORT_REG 0x0014
+#define RMAP_MEAS_MAX_THRES_LONG_REG 0x0018
+#define RMAP_MEAS_H_OFFS_REG 0x001c
+#define RMAP_MEAS_V_OFFS_REG 0x0020
+#define RMAP_MEAS_H_SIZE_REG 0x0024
+#define RMAP_MEAS_V_SIZE_REG 0x0028
+#define RMAP_MEAS_LAST_MEAS_LINE_REG 0x002c
+#define RMAP_MEAS_LS_RESULTSHORT0_REG 0x0030
+#define RMAP_MEAS_LS_RESULTLONG0_REG 0x0034
+#define RMAP_MEAS_RESERVED_2_REG 0x0038
+#define RMAP_MEAS_RESERVED_3_REG 0x003c
+#define RMAP_MEAS_LS_RESULTSHORT1_REG 0x0040
+#define RMAP_MEAS_LS_RESULTLONG1_REG 0x0044
+#define RMAP_MEAS_RESERVED_4_REG 0x0048
+#define RMAP_MEAS_RESERVED_5_REG 0x004c
+
+static int rppx1_rmapmeas_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, RMAP_MEAS_VERSION_REG)) {
+ case 3:
+ mod->info.rmapmeas.colorbits_high = 24;
+ mod->info.rmapmeas.colorbits_low = 12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_rmapmeas_ops = {
+ .probe = rppx1_rmapmeas_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
new file mode 100644
index 000000000000..5bec022e8f05
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define SHRPCNR_VERSION_REG 0x0000
+
+#define SHRPCNR_CTRL_REG 0x0004
+#define SHRPCNR_CTRL_CAD_EN BIT(3)
+#define SHRPCNR_CTRL_DESAT_EN BIT(2)
+#define SHRPCNR_CTRL_CNR_EN BIT(1)
+#define SHRPCNR_CTRL_SHARPEN_EN BIT(0)
+
+#define SHRPCNR_PARAM_REG 0x0008
+#define SHRPCNR_PARAM_SHARP_FACTOR_MASK GENMASK(19, 12)
+#define SHRPCNR_PARAM_CORING_THR_MASK GENMASK(11, 0)
+
+#define SHRPCNR_MAT_1_REG 0x000c
+#define SHRPCNR_MAT_2_REG 0x0010
+#define SHRPCNR_CLB_LINESIZE_REG 0x0014
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_0_REG 0x0018
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_1_REG 0x001c
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_2_REG 0x0020
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_3_REG 0x0024
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_4_REG 0x0028
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_5_REG 0x002c
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_6_REG 0x0030
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_7_REG 0x0034
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_8_REG 0x0038
+#define SHRPCNR_YUV2RGB_CCOR_OFFSET_R_REG 0x003c
+#define SHRPCNR_YUV2RGB_CCOR_OFFSET_G_REG 0x0040
+#define SHRPCNR_YUV2RGB_CCOR_OFFSET_B_REG 0x0044
+
+#define SHRPCNR_CNR_THRES_REG 0x0048
+#define SHRPCNR_CNR_THRES_CNR_THRES_CR_MASK GENMASK(27, 16)
+#define SHRPCNR_CNR_THRES_CNR_THRES_CB_MASK GENMASK(11, 0)
+
+#define SHRPCNR_CRED_THRES_REG 0x004c
+#define SHRPCNR_CRED_SLOPE_REG 0x0050
+#define SHRPCNR_CAD_RESTORE_LVL_REG 0x0054
+#define SHRPCNR_CAD_THRESH_V_UNEG_REG 0x0058
+#define SHRPCNR_CAD_THRESH_V_UPOS_REG 0x005c
+#define SHRPCNR_CAD_THRESH_U_REG 0x0060
+
+static int rppx1_shrp_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, SHRPCNR_VERSION_REG)) {
+ case 2:
+ mod->info.shrp.colorbits = 12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_shrp_ops = {
+ .probe = rppx1_shrp_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
new file mode 100644
index 000000000000..3d197d914d07
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define AWB_MEAS_VERSION_REG 0x0000
+
+#define AWB_MEAS_PROP_REG 0x0004
+#define AWB_MEAS_PROP_MEAS_MODE_RGB BIT(16) /* 0: YCbCr 1: RGB */
+#define AWB_MEAS_PROP_YMAX BIT(2)
+#define AWB_MEAS_PROP_AWB_MODE_ON BIT(1)
+
+#define AWB_MEAS_H_OFFS_REG 0x0008
+#define AWB_MEAS_V_OFFS_REG 0x000c
+#define AWB_MEAS_H_SIZE_REG 0x0010
+#define AWB_MEAS_V_SIZE_REG 0x0014
+#define AWB_MEAS_FRAMES_REG 0x0018
+#define AWB_MEAS_REF_CB_MAX_B_REG 0x001c
+#define AWB_MEAS_REF_CR_MAX_R_REG 0x0020
+#define AWB_MEAS_MAX_Y_REG 0x0024
+#define AWB_MEAS_MIN_Y_MAX_G_REG 0x0028
+#define AWB_MEAS_MAX_CSUM_REG 0x002c
+#define AWB_MEAS_MIN_C_REG 0x0030
+#define AWB_MEAS_WHITE_CNT_REG 0x0034
+#define AWB_MEAS_MEAN_Y_G_REG 0x0038
+#define AWB_MEAS_MEAN_CB_B_REG 0x003c
+#define AWB_MEAS_MEAN_CR_R_REG 0x0040
+
+#define AWB_MEAS_CCOR_COEFF_NUM 9
+#define AWB_MEAS_CCOR_COEFF_REG(n) (0x0044 + (4 * (n)))
+
+#define AWB_MEAS_CCOR_OFFSET_R_REG 0x0068
+#define AWB_MEAS_CCOR_OFFSET_G_REG 0x006c
+#define AWB_MEAS_CCOR_OFFSET_B_REG 0x0070
+
+static int rppx1_wbmeas_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, AWB_MEAS_VERSION_REG)) {
+ case 1:
+ mod->info.wbmeas.colorbits = 8;
+ break;
+ case 2:
+ mod->info.wbmeas.colorbits = 20;
+ break;
+ case 3:
+ mod->info.wbmeas.colorbits = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_wbmeas_ops = {
+ .probe = rppx1_wbmeas_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
new file mode 100644
index 000000000000..73789c48c057
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rpp_module.h"
+
+#define XYZ2LUV_VERSION_REG 0x0000
+#define XYZ2LUV_U_REF_REG 0x0004
+#define XYZ2LUV_V_REF_REG 0x0008
+#define XYZ2LUV_LUMA_OUT_FAC_REG 0x000c
+#define XYZ2LUV_CHROMA_OUT_FAC_REG 0x0010
+
+static int rppx1_xyz2luv_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, XYZ2LUV_VERSION_REG) != 4)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_xyz2luv_ops = {
+ .probe = rppx1_xyz2luv_probe,
+};
diff --git a/include/media/rppx1.h b/include/media/rppx1.h
new file mode 100644
index 000000000000..cb3470c27ceb
--- /dev/null
+++ b/include/media/rppx1.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+#ifndef __MEDIA_DCT_RPPX1_H__
+#define __MEDIA_DCT_RPPX1_H__
+
+#include <linux/v4l2-mediabus.h>
+#include <linux/media/dreamchip/rppx1-config.h>
+
+#include <media/videobuf2-core.h>
+
+struct rppx1;
+
+struct rppx1 *rppx1_create(void __iomem *base, struct device *dev);
+
+void rppx1_destroy(struct rppx1 *rpp);
+
+int rppx1_start(struct rppx1 *rpp, const struct v4l2_mbus_framefmt *input,
+ const struct v4l2_mbus_framefmt *hv,
+ const struct v4l2_mbus_framefmt *mv);
+
+int rppx1_stop(struct rppx1 *rpp);
+
+bool rppx1_interrupt(struct rppx1 *rpp, u32 *isc);
+
+typedef int (*rppx1_reg_write)(void *priv, u32 offset, u32 value);
+int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
+ rppx1_reg_write write, void *priv);
+
+void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf);
+
+#endif /* __MEDIA_DCT_RPPX1_H__ */
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
new file mode 100644
index 000000000000..26627be6f483
--- /dev/null
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Dreamchip RPP-X1 ISP Driver - Userspace API
+ *
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#ifndef __UAPI_RPP_X1_CONFIG_H
+#define __UAPI_RPP_X1_CONFIG_H
+
+#include <linux/types.h>
+#include <linux/media/v4l2-isp.h>
+
+/**
+ * struct rppx1_window - Measurement window
+ *
+ * RPP-X1 measurement window. Different blocks use a window or multiple
+ * windows for measurement purposes. This defines a common type for all of
+ * them. The number of relevant bits depends on the block where the window is
+ * used and is specified in the per-block description
+ *
+ * @h_offs: horizontal offset from the left of the frame in pixels
+ * @v_offs: vertical offset from the top of the frame in pixels
+ * @h_size: horizontal size of the window in pixels
+ * @v_size: vertical size of the window in pixels
+ */
+struct rppx1_window {
+ __u16 h_offs;
+ __u16 v_offs;
+ __u16 h_size;
+ __u16 v_size;
+};
+
+/* ---------------------------------------------------------------------------
+ * Parameter Structures
+ *
+ * Native RPP-X1 precision. Fields use __u32 where the hardware provides
+ * wider-than-8-bit results.
+ */
+
+/**
+ * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
+ *
+ * Some types are reported twice as the same block might be instantiated in
+ * multiple pipes.
+ */
+#define RPPX1_PARAMS_MAX_SIZE 0
+
+/* ---------------------------------------------------------------------------
+ * Statistics Structures
+ *
+ * Native RPP-X1 precision. Fields use __u32 where the hardware provides
+ * wider-than-8-bit results.
+ */
+
+/**
+ * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
+ *
+ * Some types are reported twice as the same block might be instantiated in
+ * multiple pipes.
+ */
+#define RPPX1_STATS_MAX_SIZE 0
+
+#endif /* __UAPI_RPP_X1_CONFIG_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 02/14] media: rppx1: Add framework to support Dreamchip RPPX1 ISP
2026-05-04 1:05 ` [v8 02/14] media: rppx1: Add framework to support Dreamchip RPPX1 ISP Niklas Söderlund
@ 2026-05-06 8:53 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 8:53 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Niklas
On Mon, May 04, 2026 at 03:05:44AM +0200, Niklas Söderlund wrote:
> Add a framework driver for Dreamchip RPPX1 ISP. The driver aims to
> provide a framework for other V4L2 based drivers to drive the RPPX1
> functionality. The reason for this split is that the RPPX1 IP itself do
> not provide any DMA engines to drive data to/from the device, instead it
> depends on other IP blocks to implement these features.
>
> While the peripherals around the RPPX1 ISP used in different designs and
> by different vendors are different the RPPX1 core itself is the same.
> For this reason the framework solution to be able to split the Dreamchip
> RPPX1 driver from vendors usage of it have been picked in hope to reduce
> duplication of the common parts.
>
> The design is to try and keep the surface of this framework as small as
> possible. The intention of this change is to be able to fill all needs
> of this.
>
> * Two functions to create and destroy a RPPX1 instance, rppx1_create()
> and rppx1_destory(). These are intended to be called in the users
> probe and remove code paths.
>
> * Two functions to start and stop the RPPX1 processing, rppx1_start()
> and rppx1_stop(). These are intended to be called in the users
> stream on and stream off code paths.
>
> * One function to ask the RPPX1 to process parameters buffer prepared
> by user space, rppx1_params(). The intention is to call this
> function when the parameter buffer is queued to the V4L2 driver and
> the result stored by the driver until the time it needs to be
> written to the RPPX1. It's the users responsibility to write it
> either using MMIO or other means.
>
> * One function to fill in a statistic buffer based on the current
> status of the RPPX1, rppx1_stats_fill_isr(). The intention is that
> the user call's this in its interrupt handler when it knows the
> RPPX1 is done processing a frame.
>
> * One function to ack and retrieve the interrupts generated by the
> RPPX1, rppx1_interrupt(). The intention is to call this function
> when the users interrupt handler detects the RPPX1 have raised and
> interrupt. There is no need for the user to understand, or act, on
> the actual RPPX1 interrupt, but it can if it wants too.
>
> The initial support in the framework is limited and do not implement any
> ISP processing algorithms other then configuring the RPPX1 to process
> any Bayer (8-, 10, or 12-bit) image and produce either a RGB or YUYV
> output. It do however probe all function blocks of the RPPX1 and provide
> an interface to interact with both parameter and statistic bufferers.
> The user of the framework will not change as algorithms for the
> different function blocks of the ISP are being added.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> ---
> * Changes since v7
> - Merge with fixups done before the pixel format change.
Me and Jai have piled almost 60 fixup commits here
https://git.ideasonboard.com/renesas-v4h-isp/linux/commits/branch/v4h/jmondi/rppx1
which include the introduction of the uAPI header and porting of the
driver to new uAPI. I think it's fair to ask for this work to be
recorded even if it won't be visible upstream:
Please add:
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Co-developed-by: Jacopo Mondi <jacopo.mondi+renesas@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi+renesas@ideasonboard.com>
to the next patches in the series (and this one if you want to
introduce the empty uAPI header here. More below on this).
>
> * Changes since v6
> - Use kzalloc_obj
> - Set device pointer
>
> * Changes since v5
> - Make use of v4l2-isp.
> - Make VIDEO_DCT_RPPX1 a non visible symbol. It makes no sens to build
> it without a consumer.
>
> * Changes since v2
> - Add missing include to slab.h
>
> * Changes since v3
> - Fix small build issues found by 0day on non-arm ARCH.
> ---
> MAINTAINERS | 7 +
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/dreamchip/Kconfig | 3 +
> drivers/media/platform/dreamchip/Makefile | 6 +
> .../media/platform/dreamchip/rppx1/Kconfig | 12 +
> .../media/platform/dreamchip/rppx1/Makefile | 33 ++
> .../platform/dreamchip/rppx1/rpp_module.c | 40 +++
> .../platform/dreamchip/rppx1/rpp_module.h | 145 ++++++++
> .../platform/dreamchip/rppx1/rpp_params.c | 72 ++++
> .../platform/dreamchip/rppx1/rpp_stats.c | 27 ++
> .../media/platform/dreamchip/rppx1/rppx1.c | 339 ++++++++++++++++++
> .../media/platform/dreamchip/rppx1/rppx1.h | 99 +++++
> .../platform/dreamchip/rppx1/rppx1_acq.c | 147 ++++++++
> .../platform/dreamchip/rppx1/rppx1_awbg.c | 30 ++
> .../media/platform/dreamchip/rppx1/rppx1_bd.c | 52 +++
> .../platform/dreamchip/rppx1/rppx1_bdrgb.c | 80 +++++
> .../platform/dreamchip/rppx1/rppx1_bls.c | 59 +++
> .../platform/dreamchip/rppx1/rppx1_cac.c | 29 ++
> .../platform/dreamchip/rppx1/rppx1_ccor.c | 106 ++++++
> .../media/platform/dreamchip/rppx1/rppx1_db.c | 44 +++
> .../platform/dreamchip/rppx1/rppx1_dpcc.c | 76 ++++
> .../platform/dreamchip/rppx1/rppx1_exm.c | 51 +++
> .../media/platform/dreamchip/rppx1/rppx1_ga.c | 49 +++
> .../platform/dreamchip/rppx1/rppx1_hist.c | 76 ++++
> .../platform/dreamchip/rppx1/rppx1_hist256.c | 46 +++
> .../media/platform/dreamchip/rppx1/rppx1_is.c | 42 +++
> .../platform/dreamchip/rppx1/rppx1_lin.c | 58 +++
> .../platform/dreamchip/rppx1/rppx1_lsc.c | 68 ++++
> .../platform/dreamchip/rppx1/rppx1_ltm.c | 48 +++
> .../platform/dreamchip/rppx1/rppx1_ltmmeas.c | 41 +++
> .../platform/dreamchip/rppx1/rppx1_outif.c | 45 +++
> .../platform/dreamchip/rppx1/rppx1_outregs.c | 75 ++++
> .../platform/dreamchip/rppx1/rppx1_rmap.c | 64 ++++
> .../platform/dreamchip/rppx1/rppx1_rmapmeas.c | 47 +++
> .../platform/dreamchip/rppx1/rppx1_shrp.c | 64 ++++
> .../platform/dreamchip/rppx1/rppx1_wbmeas.c | 61 ++++
> .../platform/dreamchip/rppx1/rppx1_xyz2luv.c | 26 ++
> include/media/rppx1.h | 34 ++
> .../uapi/linux/media/dreamchip/rppx1-config.h | 66 ++++
If you want to do it this way (add an empty uapi file and populate it
as we add blocks) please break the introduction of the uAPI header out
to a separate commit and retain Jai's authorship.
I don't think it it necessary to split adding blocks in different
commits and I think it would be better to add the uapi file in one go
for easier bisection.
> 40 files changed, 2369 insertions(+)
> create mode 100644 drivers/media/platform/dreamchip/Kconfig
> create mode 100644 drivers/media/platform/dreamchip/Makefile
> create mode 100644 drivers/media/platform/dreamchip/rppx1/Kconfig
> create mode 100644 drivers/media/platform/dreamchip/rppx1/Makefile
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_module.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_module.h
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_params.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1.h
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_db.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_is.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
> create mode 100644 include/media/rppx1.h
> create mode 100644 include/uapi/linux/media/dreamchip/rppx1-config.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index fb3c08e42a66..b7bdfa1b4816 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7787,6 +7787,13 @@ F: drivers/block/drbd/
> F: include/linux/drbd*
> F: lib/lru_cache.c
>
> +DREAMCHIP RPPX1 ISP
> +M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Please add me and Jai here
> +L: linux-media@vger.kernel.org
> +S: Maintained
> +F: drivers/media/platform/dreamchip/rppx1/
> +F: include/uapi/linux/media/dreamchip/rppx1-config.h
Once the formats are documented they should be added here
> +
> DRIVER COMPONENT FRAMEWORK
> L: dri-devel@lists.freedesktop.org
> F: drivers/base/component.c
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 3f0b7bb68cc9..3621b46e1e8d 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -71,6 +71,7 @@ source "drivers/media/platform/atmel/Kconfig"
> source "drivers/media/platform/broadcom/Kconfig"
> source "drivers/media/platform/cadence/Kconfig"
> source "drivers/media/platform/chips-media/Kconfig"
> +source "drivers/media/platform/dreamchip/Kconfig"
> source "drivers/media/platform/imagination/Kconfig"
> source "drivers/media/platform/intel/Kconfig"
> source "drivers/media/platform/marvell/Kconfig"
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 6d5f79ddfcc3..0cc4bfe0da32 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -14,6 +14,7 @@ obj-y += atmel/
> obj-y += broadcom/
> obj-y += cadence/
> obj-y += chips-media/
> +obj-y += dreamchip/
> obj-y += imagination/
> obj-y += intel/
> obj-y += marvell/
> diff --git a/drivers/media/platform/dreamchip/Kconfig b/drivers/media/platform/dreamchip/Kconfig
> new file mode 100644
> index 000000000000..d177d4ee79ae
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/Kconfig
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +source "drivers/media/platform/dreamchip/rppx1/Kconfig"
> diff --git a/drivers/media/platform/dreamchip/Makefile b/drivers/media/platform/dreamchip/Makefile
> new file mode 100644
> index 000000000000..ba47ba2d136e
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/Makefile
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Makefile for the Dreamchip device drivers.
> +#
> +
> +obj-y += rppx1/
> diff --git a/drivers/media/platform/dreamchip/rppx1/Kconfig b/drivers/media/platform/dreamchip/rppx1/Kconfig
> new file mode 100644
> index 000000000000..0998a7d10bf2
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config VIDEO_DCT_RPPX1
> + tristate
> + depends on V4L_PLATFORM_DRIVERS
> + select V4L2_ISP
> + help
> + Support library for Dreamchip HDR RPP X1 High Dynamic Range Real-time
> + Pixel Processor (RPP). The library can be used by other drivers who
> + utilises the RPP as part of an ISP implementation.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called rppx1.
> diff --git a/drivers/media/platform/dreamchip/rppx1/Makefile b/drivers/media/platform/dreamchip/rppx1/Makefile
> new file mode 100644
> index 000000000000..b2bd6b5d68bc
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/Makefile
> @@ -0,0 +1,33 @@
> +# SPDX-License-Identifier: GPL-2.0
> +dct-rpp-x1-objs = \
> + rpp_module.o \
> + rpp_params.o \
> + rpp_stats.o \
> + rppx1.o \
> + rppx1_acq.o \
> + rppx1_awbg.o \
> + rppx1_bd.o \
> + rppx1_bdrgb.o \
> + rppx1_bls.o \
> + rppx1_cac.o \
> + rppx1_ccor.o \
> + rppx1_db.o \
> + rppx1_dpcc.o \
> + rppx1_exm.o \
> + rppx1_ga.o \
> + rppx1_hist.o \
> + rppx1_hist256.o \
> + rppx1_is.o \
> + rppx1_lin.o \
> + rppx1_lsc.o \
> + rppx1_ltm.o \
> + rppx1_ltmmeas.o \
> + rppx1_outif.o \
> + rppx1_outregs.o \
> + rppx1_rmap.o \
> + rppx1_rmapmeas.o \
> + rppx1_shrp.o \
> + rppx1_wbmeas.o \
> + rppx1_xyz2luv.o
> +
> +obj-$(CONFIG_VIDEO_DCT_RPPX1) += dct-rpp-x1.o
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.c b/drivers/media/platform/dreamchip/rppx1/rpp_module.c
> new file mode 100644
> index 000000000000..cd923b7ff5c1
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.c
> @@ -0,0 +1,40 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include <linux/slab.h>
I don't think this is needed
> +
> +#include "rppx1.h"
> +#include "rpp_module.h"
> +
> +int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp,
> + const struct rpp_module_ops *ops, u32 base)
> +{
> + mod->rpp = rpp;
> + mod->base = base;
> + mod->ops = ops;
> +
> + if (ops->probe)
> + return ops->probe(mod);
> +
> + return 0;
> +}
> +
> +void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value)
> +{
> + rppx1_write(mod->rpp, mod->base + offset, value);
> +}
> +
> +u32 rpp_module_read(struct rpp_module *mod, u32 offset)
> +{
> + return rppx1_read(mod->rpp, mod->base + offset);
> +}
> +
> +void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value)
> +{
> + u32 reg = rpp_module_read(mod, offset) & ~mask;
> +
> + rpp_module_write(mod, offset, reg | value);
> +}
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> new file mode 100644
> index 000000000000..742904973e35
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> @@ -0,0 +1,145 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#ifndef __RPPX1_MODULE_H__
> +#define __RPPX1_MODULE_H__
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/v4l2-mediabus.h>
> +
> +#include <linux/media/dreamchip/rppx1-config.h>
> +
> +#include <media/rppx1.h>
> +
> +struct rpp_module_ops;
> +
> +enum rpp_raw_pattern {
> + RPP_RGGB = 0,
> + RPP_GRBG,
> + RPP_GBRG,
> + RPP_BGGR,
> +};
> +
> +struct rpp_module {
> + struct rppx1 *rpp;
> + u32 base;
> +
> + const struct rpp_module_ops *ops;
> +
> + union {
> + struct {
> + enum rpp_raw_pattern raw_pattern;
> + } acq;
> + struct {
> + unsigned int colorbits;
> + } bdrgb;
> + struct {
> + unsigned int colorbits;
> + } bls;
> + struct {
> + unsigned int colorbits;
> + unsigned int type;
> + } ccor;
> + struct {
> + unsigned int colorbits;
> + } dpcc;
> + struct {
> + unsigned int resultbits;
> + } exm;
> + struct {
> + unsigned int colorbits;
> + } ga;
> + struct {
> + unsigned int colorbits;
> + } hist;
> + struct {
> + unsigned int colorbits;
> + } lin;
> + struct {
> + unsigned int colorbits_high;
> + unsigned int colorbits_low;
> + } rmap;
> + struct {
> + unsigned int colorbits_high;
> + unsigned int colorbits_low;
> + } rmapmeas;
> + struct {
> + unsigned int colorbits;
> + } shrp;
> + struct {
> + unsigned int colorbits;
> + } wbmeas;
> + } info;
> +};
> +
> +int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp,
> + const struct rpp_module_ops *ops, u32 base);
> +
> +void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value);
> +u32 rpp_module_read(struct rpp_module *mod, u32 offset);
> +void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
> +
> +union rppx1_params_block {
> + struct v4l2_isp_params_block_header header;
> +};
> +
> +union rppx1_stats_block {
> + struct v4l2_isp_params_block_header header;
> +};
> +
> +struct rpp_module_ops {
> + int (*probe)(struct rpp_module *mod);
> + int (*start)(struct rpp_module *mod, const struct v4l2_mbus_framefmt *fmt);
> +
> + int (*fill_params)(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv);
> + int (*fill_stats)(struct rpp_module *mod,
> + union rppx1_stats_block *block);
> +};
> +
> +extern const struct rpp_module_ops rppx1_acq_ops;
> +extern const struct rpp_module_ops rppx1_awbg_ops;
> +extern const struct rpp_module_ops rppx1_bd_ops;
> +extern const struct rpp_module_ops rppx1_bdrgb_ops;
> +extern const struct rpp_module_ops rppx1_bls_ops;
> +extern const struct rpp_module_ops rppx1_cac_ops;
> +extern const struct rpp_module_ops rppx1_ccor_ops;
> +extern const struct rpp_module_ops rppx1_ccor_csm_ops;
> +extern const struct rpp_module_ops rppx1_db_ops;
> +extern const struct rpp_module_ops rppx1_dpcc_ops;
> +extern const struct rpp_module_ops rppx1_exm_ops;
> +extern const struct rpp_module_ops rppx1_ga_ops;
> +extern const struct rpp_module_ops rppx1_hist256_ops;
> +extern const struct rpp_module_ops rppx1_hist_ops;
> +extern const struct rpp_module_ops rppx1_is_ops;
> +extern const struct rpp_module_ops rppx1_lin_ops;
> +extern const struct rpp_module_ops rppx1_lsc_ops;
> +extern const struct rpp_module_ops rppx1_ltm_ops;
> +extern const struct rpp_module_ops rppx1_ltmmeas_ops;
> +extern const struct rpp_module_ops rppx1_outif_ops;
> +extern const struct rpp_module_ops rppx1_outregs_ops;
> +extern const struct rpp_module_ops rppx1_rmapmeas_ops;
> +extern const struct rpp_module_ops rppx1_rmap_ops;
> +extern const struct rpp_module_ops rppx1_shrp_ops;
> +extern const struct rpp_module_ops rppx1_wbmeas_ops;
> +extern const struct rpp_module_ops rppx1_xyz2luv_ops;
> +
> +#define rpp_module_call(mod, op, args...) \
> + ({ \
> + struct rpp_module *__mod = (mod); \
> + int __result; \
> + if (!__mod) \
> + __result = -ENODEV; \
> + else if (!__mod->ops->op) \
> + __result = 0; \
> + else \
> + __result = __mod->ops->op(__mod, ##args); \
> + __result; \
> + })
> +
> +#endif /* __RPPX1_MODULE_H__ */
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> new file mode 100644
> index 000000000000..a5feb18f3bd5
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include <media/v4l2-isp.h>
> +#include <media/videobuf2-v4l2.h>
> +
> +#include "rppx1.h"
> +
> +#define RPPX1_PARAMS_BLOCK_INFO(block, data) \
> + [RPPX1_PARAMS_BLOCK_TYPE_ ## block] = { \
> + .size = sizeof(struct rppx1_ ## data ## _params), \
> + }
> +
> +static const struct v4l2_isp_block_type_info
> +rppx1_ext_params_blocks_info[] = {
> +};
> +
> +int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> + rppx1_reg_write write, void *priv)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct v4l2_isp_params_buffer *cfg;
> + size_t block_offset;
> + int ret;
> +
> + ret = v4l2_isp_params_validate_buffer_size(rpp->dev, vb, max_size);
> + if (ret)
> + return ret;
> +
> + cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> +
> + ret = v4l2_isp_params_validate_buffer(rpp->dev, vb,
> + (struct v4l2_isp_params_buffer *)cfg,
The cast is probably not needed
> + rppx1_ext_params_blocks_info,
> + ARRAY_SIZE(rppx1_ext_params_blocks_info));
> + if (ret)
> + return ret;
> +
> + /* Walk the list of parameter blocks and process them. */
> + block_offset = 0;
> + while (block_offset < cfg->data_size) {
> + const union rppx1_params_block *block =
> + (const union rppx1_params_block *)&cfg->data[block_offset];
> + struct rpp_module *module;
> + int ret;
> +
> + block_offset += block->header.size;
> +
> + switch (block->header.type) {
> + default:
> + module = NULL;
> + break;
> + }
> +
> + if (!module) {
If you want to do this you should initialize module to NULL. Otherwise
you can simply pr_warn (why not dev_warn() or even _dbg() ?) and
continue inside the switch ?
> + pr_warn("Not handled RPPX1 block type: 0x%04x\n", block->header.type);
> + continue;
> + }
> +
> + ret = rpp_module_call(module, fill_params, block, write, priv);
> + if (ret) {
> + pr_err("Error processing RPPX1 block type: 0x%04x\n", block->header.type);
Similar here, you have access to rpp->dev, use dev_err() (or _dbg() as
the error can be triggered by a userspace)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rppx1_params);
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> new file mode 100644
> index 000000000000..8f43e56ba361
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> @@ -0,0 +1,27 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rppx1.h"
> +#include "rpp_module.h"
> +
> +#include <media/v4l2-isp.h>
> +
> +#define RPPX1_STATS_BLOCK_INFO(type, block) \
> + [RPPX1_STATS_BLOCK_TYPE_ ## type] = { \
> + .size = sizeof(struct rppx1_ ## block ## _stats), \
> + }
> +
> +#define rppx1_init_stats_block(rpp, buf, type) \
> + ((union rppx1_stats_block *) \
> + v4l2_isp_stats_init_block((rpp)->dev, (buf), \
> + rppx1_stats_blocks_info, \
> + ARRAY_SIZE(rppx1_stats_blocks_info), \
> + (type), RPPX1_STATS_MAX_SIZE)) \
> +
> +void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
> +{
> +}
> +EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
You will need to rebase on https://patchwork.linuxtv.org/project/linux-media/list/?series=24772
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.c b/drivers/media/platform/dreamchip/rppx1/rppx1.c
> new file mode 100644
> index 000000000000..2998c5f2a42e
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1.c
> @@ -0,0 +1,339 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + *
> + * Support library for Dreamchip HDR RPPX1 High Dynamic Range Real-time Pixel
> + * Processor.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include "rppx1.h"
> +
> +/* RPP_HDR Base Addresses */
> +#define RPPX1_HDRREGS_BASE 0x0000
> +#define RPPX1_HDR_IRQ_BASE 0x0200
> +#define RPPX1_RPP_OUT_BASE 0x0800
> +#define RPPX1_RPP_RMAP_BASE 0x0c00
> +#define RPPX1_RPP_RMAP_MEAS_BASE 0x1000
> +#define RPPX1_RPP_MAIN_PRE1_BASE 0x2000
> +#define RPPX1_RPP_MAIN_PRE2_BASE 0x4000
> +#define RPPX1_RPP_MAIN_POST_BASE 0xa000
> +#define RPPX1_RPP_MVOUT_BASE 0xc000
> +#define RPPX1_RPP_FUSA_BASE 0xf000
> +
> +#define RPPX1_RPP_HDRREGS_VERSION_REG (RPPX1_HDRREGS_BASE + 0x0000)
> +#define RPPX1_RPP_HDR_UPD_REG (RPPX1_HDRREGS_BASE + 0x0004)
> +#define RPPX1_RESERVED_3_REG (RPPX1_HDRREGS_BASE + 0x0008)
> +#define RPPX1_RPP_HDR_INFORM_ENABLE_REG (RPPX1_HDRREGS_BASE + 0x000c)
> +#define RPPX1_RPP_HDR_OUT_IF_ON_REG (RPPX1_HDRREGS_BASE + 0x0010)
> +#define RPPX1_RPP_HDR_OUT_IF_OFF_REG (RPPX1_HDRREGS_BASE + 0x0014)
> +#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG (RPPX1_HDRREGS_BASE + 0x0018)
> +
> +#define RPPX1_RPP_ISM (RPPX1_HDR_IRQ_BASE + 0x00)
> +#define RPPX1_RPP_RIS (RPPX1_HDR_IRQ_BASE + 0x04)
> +#define RPPX1_RPP_MIS (RPPX1_HDR_IRQ_BASE + 0x08)
> +#define RPPX1_RPP_ISC (RPPX1_HDR_IRQ_BASE + 0x0c)
> +
> +/* RPP_OUT/MV_OUT Pipelines - Base Addresses */
> +#define RPPX1_GAMMA_OUT_BASE 0x0000 /* HV, MV */
> +#define RPPX1_IS_BASE 0x00c0 /* HV, MV */
> +#define RPPX1_CSM_BASE 0x0100 /* HV, MV */
> +#define RPPX1_OUT_IF_BASE 0x0200 /* HV, MV */
> +#define RPPX1_RPP_OUTREGS_BASE 0x02c0 /* HV, MV */
> +#define RPPX1_LUV_BASE 0x0300 /* MV */
> +
> +/* PRE1/PRE2/POST Pipelines - Base Addresses */
> +#define RPPX1_ACQ_BASE 0x0080 /* PRE1, PRE2 */
> +#define RPPX1_BLS_BASE 0x0100 /* PRE1, PRE2 */
> +#define RPPX1_GAMMA_IN_BASE 0x0200 /* PRE1, PRE2 */
> +#define RPPX1_LSC_BASE 0x0400 /* PRE1, PRE2 */
> +#define RPPX1_AWB_GAIN_BASE 0x0500 /* PRE1, PRE2, POST */
> +#define RPPX1_DPCC_BASE 0x0600 /* PRE1, PRE2 */
> +#define RPPX1_DPF_BASE 0x0700 /* PRE1, PRE2 */
> +#define RPPX1_FILT_BASE 0x0800 /* POST */
> +#define RPPX1_CAC_BASE 0x0880 /* POST */
> +#define RPPX1_CCOR_BASE 0x0900 /* POST */
> +#define RPPX1_HIST_BASE 0x0a00 /* PRE1, PRE2, POST */
> +#define RPPX1_HIST256_BASE 0x0b00 /* PRE1 */
> +#define RPPX1_EXM_BASE 0x0c00 /* PRE1, PRE2 */
> +#define RPPX1_LTM_BASE 0x1000 /* POST */
> +#define RPPX1_LTM_MEAS_BASE 0x1200 /* POST */
> +#define RPPX1_WBMEAS_BASE 0x1700 /* POST */
> +#define RPPX1_BDRGB_BASE 0x1800 /* POST */
> +#define RPPX1_SHRP_BASE 0x1a00 /* POST */
> +
> +/* Functional Safety Module Base Addresses */
> +#define RPPX1_FMU_BASE 0x0100
> +
> +#define RPPX1_RPP_HDR_FMU_FSM (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x00)
> +#define RPPX1_RPP_HDR_FMU_RFS (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x04)
> +#define RPPX1_RPP_HDR_FMU_MFS (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x08)
> +#define RPPX1_RPP_HDR_FMU_FSC (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x0c)
> +
> +void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value)
> +{
> + iowrite32(value, rpp->base + offset);
> +}
> +
> +u32 rppx1_read(struct rppx1 *rpp, u32 offset)
> +{
> + u32 ret = ioread32(rpp->base + offset);
> + return ret;
return ioread32(rpp->base + offset);
> +}
> +
> +bool rppx1_interrupt(struct rppx1 *rpp, u32 *isc)
> +{
> + u32 status, raw, fault;
> +
> + fault = rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS);
> + if (fault) {
> + pr_err("%s: fault 0x%08x\n", __func__, fault);
Can't you use dev_err() ?
Please have a look at all the usages of pr_err/pr_warn and replace
them with dev_dbg()
> + rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC, fault);
> + }
> +
> + /* Read raw interrupt status. */
> + raw = rppx1_read(rpp, RPPX1_RPP_RIS);
> + status = rppx1_read(rpp, RPPX1_RPP_MIS);
> +
> + /* Propagate the isc status. */
> + if (isc)
> + *isc = status | raw;
> +
> + /* Clear enabled interrupts */
> + rppx1_write(rpp, RPPX1_RPP_ISC, status);
> +
> + return !!(status & RPPX1_IRQ_ID_OUT_FRAME);
> +}
> +EXPORT_SYMBOL_GPL(rppx1_interrupt);
> +
> +void rppx1_destroy(struct rppx1 *rpp)
> +{
> + kfree(rpp);
> +}
> +EXPORT_SYMBOL_GPL(rppx1_destroy);
> +
> +/*
> + * Allocate the private data structure and verify the hardware is present.
> + */
> +struct rppx1 *rppx1_create(void __iomem *base, struct device *dev)
> +{
> + struct rppx1 *rpp;
> + u32 reg;
> +
> + /* Allocate library structure */
> + rpp = kzalloc_obj(*rpp);
> + if (!rpp)
> + return NULL;
I wonder if we can use the managed version of devm_kzalloc or we're
going to create dependencies between the rcar-isp platform device and
the rpp library driver.
> +
> + rpp->base = base;
> + rpp->dev = dev;
> +
> + /* Check communication with RPP and verify it truly is a X1. */
> + reg = rppx1_read(rpp, RPPX1_RPP_HDRREGS_VERSION_REG);
> + if (reg != 3) {
> + pr_err("Unsupported HDR version (%u)\n", reg);
ditto
> + rppx1_destroy(rpp);
> + return NULL;
> + }
> +
> + /* Probe the PRE1 pipeline. */
> + if (rpp_module_probe(&rpp->pre1.acq, rpp, &rppx1_acq_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_ACQ_BASE) ||
> + rpp_module_probe(&rpp->pre1.bls, rpp, &rppx1_bls_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_BLS_BASE) ||
> + rpp_module_probe(&rpp->pre1.lin, rpp, &rppx1_lin_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_GAMMA_IN_BASE) ||
> + rpp_module_probe(&rpp->pre1.lsc, rpp, &rppx1_lsc_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_LSC_BASE) ||
> + rpp_module_probe(&rpp->pre1.awbg, rpp, &rppx1_awbg_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_AWB_GAIN_BASE) ||
> + rpp_module_probe(&rpp->pre1.dpcc, rpp, &rppx1_dpcc_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPCC_BASE) ||
> + rpp_module_probe(&rpp->pre1.bd, rpp, &rppx1_bd_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPF_BASE) ||
> + rpp_module_probe(&rpp->pre1.hist, rpp, &rppx1_hist_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST_BASE) ||
> + rpp_module_probe(&rpp->pre1.hist256, rpp, &rppx1_hist256_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST256_BASE) ||
> + rpp_module_probe(&rpp->pre1.exm, rpp, &rppx1_exm_ops,
> + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_EXM_BASE))
> + goto err;
> +
> + /* Probe the PRE2 pipeline. */
> + if (rpp_module_probe(&rpp->pre2.acq, rpp, &rppx1_acq_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_ACQ_BASE) ||
> + rpp_module_probe(&rpp->pre2.bls, rpp, &rppx1_bls_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_BLS_BASE) ||
> + rpp_module_probe(&rpp->pre2.lin, rpp, &rppx1_lin_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_GAMMA_IN_BASE) ||
> + rpp_module_probe(&rpp->pre2.lsc, rpp, &rppx1_lsc_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_LSC_BASE) ||
> + rpp_module_probe(&rpp->pre2.awbg, rpp, &rppx1_awbg_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_AWB_GAIN_BASE) ||
> + rpp_module_probe(&rpp->pre2.dpcc, rpp, &rppx1_dpcc_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPCC_BASE) ||
> + rpp_module_probe(&rpp->pre2.bd, rpp, &rppx1_bd_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPF_BASE) ||
> + rpp_module_probe(&rpp->pre2.hist, rpp, &rppx1_hist_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_HIST_BASE) ||
> + rpp_module_probe(&rpp->pre2.exm, rpp, &rppx1_exm_ops,
> + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_EXM_BASE))
> + goto err;
> +
> + /* Probe the POST pipeline. */
> + if (rpp_module_probe(&rpp->post.awbg, rpp, &rppx1_awbg_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_AWB_GAIN_BASE) ||
> + rpp_module_probe(&rpp->post.ccor, rpp, &rppx1_ccor_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_CCOR_BASE) ||
> + rpp_module_probe(&rpp->post.hist, rpp, &rppx1_hist_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_HIST_BASE) ||
> + rpp_module_probe(&rpp->post.db, rpp, &rppx1_db_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_FILT_BASE) ||
> + rpp_module_probe(&rpp->post.cac, rpp, &rppx1_cac_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_CAC_BASE) ||
> + rpp_module_probe(&rpp->post.ltm, rpp, &rppx1_ltm_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_BASE) ||
> + rpp_module_probe(&rpp->post.ltmmeas, rpp, &rppx1_ltmmeas_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_MEAS_BASE) ||
> + rpp_module_probe(&rpp->post.wbmeas, rpp, &rppx1_wbmeas_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_WBMEAS_BASE) ||
> + rpp_module_probe(&rpp->post.bdrgb, rpp, &rppx1_bdrgb_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_BDRGB_BASE) ||
> + rpp_module_probe(&rpp->post.shrp, rpp, &rppx1_shrp_ops,
> + RPPX1_RPP_MAIN_POST_BASE + RPPX1_SHRP_BASE))
> + goto err;
> +
> + /* Probe the Human Vision pipeline. */
> + if (rpp_module_probe(&rpp->hv.ga, rpp, &rppx1_ga_ops,
> + RPPX1_RPP_OUT_BASE + RPPX1_GAMMA_OUT_BASE) ||
> + rpp_module_probe(&rpp->hv.is, rpp, &rppx1_is_ops,
> + RPPX1_RPP_OUT_BASE + RPPX1_IS_BASE) ||
> + rpp_module_probe(&rpp->hv.ccor, rpp, &rppx1_ccor_csm_ops,
> + RPPX1_RPP_OUT_BASE + RPPX1_CSM_BASE) ||
> + rpp_module_probe(&rpp->hv.outif, rpp, &rppx1_outif_ops,
> + RPPX1_RPP_OUT_BASE + RPPX1_OUT_IF_BASE) ||
> + rpp_module_probe(&rpp->hv.outregs, rpp, &rppx1_outregs_ops,
> + RPPX1_RPP_OUT_BASE + RPPX1_RPP_OUTREGS_BASE))
> + goto err;
> +
> + /* Probe the Machine Vision pipeline. */
> + if (rpp_module_probe(&rpp->mv.ga, rpp, &rppx1_ga_ops,
> + RPPX1_RPP_MVOUT_BASE + RPPX1_GAMMA_OUT_BASE) ||
> + rpp_module_probe(&rpp->mv.is, rpp, &rppx1_is_ops,
> + RPPX1_RPP_MVOUT_BASE + RPPX1_IS_BASE) ||
> + rpp_module_probe(&rpp->mv.ccor, rpp, &rppx1_ccor_csm_ops,
> + RPPX1_RPP_MVOUT_BASE + RPPX1_CSM_BASE) ||
> + rpp_module_probe(&rpp->mv.outif, rpp, &rppx1_outif_ops,
> + RPPX1_RPP_MVOUT_BASE + RPPX1_OUT_IF_BASE) ||
> + rpp_module_probe(&rpp->mv.outregs, rpp, &rppx1_outregs_ops,
> + RPPX1_RPP_MVOUT_BASE + RPPX1_RPP_OUTREGS_BASE) ||
> + rpp_module_probe(&rpp->mv.xyz2luv, rpp, &rppx1_xyz2luv_ops,
> + RPPX1_RPP_MVOUT_BASE + RPPX1_LUV_BASE))
> + goto err;
> +
> + /* Probe the standalone Radiance Mapping modules. */
> + if (rpp_module_probe(&rpp->rmap, rpp, &rppx1_rmap_ops,
> + RPPX1_RPP_RMAP_BASE) ||
> + rpp_module_probe(&rpp->rmapmeas, rpp, &rppx1_rmapmeas_ops,
> + RPPX1_RPP_RMAP_MEAS_BASE))
> + goto err;
Should we only probe the modules we currently support with params and
stats ?
> +
> + return rpp;
> +err:
> + rppx1_destroy(rpp);
> +
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(rppx1_create);
> +
> +int rppx1_start(struct rppx1 *rpp,
> + const struct v4l2_mbus_framefmt *input,
> + const struct v4l2_mbus_framefmt *hv,
> + const struct v4l2_mbus_framefmt *mv)
> +{
> + if (rpp_module_call(&rpp->pre1.acq, start, input) ||
> + rpp_module_call(&rpp->pre1.bls, start, input) ||
> + rpp_module_call(&rpp->pre1.lin, start, input) ||
> + rpp_module_call(&rpp->pre1.lsc, start, input) ||
> + rpp_module_call(&rpp->pre1.awbg, start, input) ||
> + rpp_module_call(&rpp->pre1.dpcc, start, input) ||
> + rpp_module_call(&rpp->pre1.bd, start, input) ||
> + rpp_module_call(&rpp->pre1.hist, start, input) ||
> + rpp_module_call(&rpp->pre1.exm, start, input) ||
> + rpp_module_call(&rpp->pre1.hist256, start, input))
> + return -EINVAL;
> +
> + if (rpp_module_call(&rpp->rmap, start, NULL) ||
> + rpp_module_call(&rpp->rmapmeas, start, NULL))
> + return -EINVAL;
> +
> + if (rpp_module_call(&rpp->post.awbg, start, input) ||
> + rpp_module_call(&rpp->post.db, start, input) ||
> + rpp_module_call(&rpp->post.cac, start, input) ||
> + rpp_module_call(&rpp->post.ccor, start, input) ||
> + rpp_module_call(&rpp->post.ltm, start, input) ||
> + rpp_module_call(&rpp->post.bdrgb, start, input) ||
> + rpp_module_call(&rpp->post.shrp, start, input) ||
> + rpp_module_call(&rpp->post.ltmmeas, start, input) ||
> + rpp_module_call(&rpp->post.wbmeas, start, input) ||
> + rpp_module_call(&rpp->post.hist, start, input))
> + return -EINVAL;
> +
> + if (hv && (rpp_module_call(&rpp->hv.ga, start, hv) ||
> + rpp_module_call(&rpp->hv.ccor, start, hv) ||
> + rpp_module_call(&rpp->hv.outregs, start, hv) ||
> + rpp_module_call(&rpp->hv.is, start, hv) ||
> + rpp_module_call(&rpp->hv.outif, start, hv)))
> + return -EINVAL;
> +
> + if (mv && (rpp_module_call(&rpp->mv.ga, start, mv) ||
> + rpp_module_call(&rpp->mv.ccor, start, mv) ||
> + rpp_module_call(&rpp->mv.xyz2luv, start, mv) ||
> + rpp_module_call(&rpp->mv.outregs, start, mv) ||
> + rpp_module_call(&rpp->mv.is, start, mv) ||
> + rpp_module_call(&rpp->mv.outif, start, mv)))
> + return -EINVAL;
> +
> + rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, 0x00000001);
> +
> + /* Clear fault interrupts. */
> + rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG, 0x00000001);
> + rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSM, 0x000001c0);
> + rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC, rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS));
> + rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG, 0x00000000);
> +
> + /* Set interrupt mask. */
> + rppx1_write(rpp, RPPX1_RPP_ISM, RPPX1_IRQ_ID_OUT_FRAME);
> +
> + rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, 0x00000001);
> + rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, 0x00000002);
> +
> + /* Clear any pending interrupts. */
> + rppx1_interrupt(rpp, NULL);
> +
> + /* Enable input formatters. */
> + rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG, 1);
Should magic numbers be replaced with macros ?
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rppx1_start);
> +
> +int rppx1_stop(struct rppx1 *rpp)
> +{
> + /* Disable input formatters. */
> + rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG, 0);
> +
> + /* Clear any pending interrupts. */
> + rppx1_interrupt(rpp, NULL);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rppx1_stop);
> +
> +MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
> +MODULE_DESCRIPTION("Dreamchip HDR RPPX1 support library");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.h b/drivers/media/platform/dreamchip/rppx1/rppx1.h
> new file mode 100644
> index 000000000000..dcf43826d308
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1.h
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __MEDIA_RPPX1_H__
> +#define __MEDIA_RPPX1_H__
> +
> +#include <linux/types.h>
> +
> +#include "rpp_module.h"
> +
> +#define RPPX1_IRQ_ID_256HIST BIT(27)
> +#define RPPX1_IRQ_ID_PRE2_DPCC BIT(25)
> +#define RPPX1_IRQ_ID_PRE1_DPCC BIT(24)
> +#define RPPX1_IRQ_ID_MV_OUT_FRAME_OUT BIT(23)
> +#define RPPX1_IRQ_ID_MV_OUT_OFF BIT(22)
> +#define RPPX1_IRQ_ID_POST_AWB_MEAS BIT(21)
> +#define RPPX1_IRQ_ID_POST_HIST_MEAS BIT(20)
> +#define RPPX1_IRQ_ID_POST_TM BIT(19)
> +#define RPPX1_IRQ_ID_PRE1_EXM BIT(18)
> +#define RPPX1_IRQ_ID_PRE1_HIST BIT(17)
> +#define RPPX1_IRQ_ID_PRE1_FRAME_IN BIT(16)
> +#define RPPX1_IRQ_ID_PRE1_HSTART BIT(15)
> +#define RPPX1_IRQ_ID_PRE1_VSTART BIT(14)
> +#define RPPX1_IRQ_ID_PRE2_EXM BIT(13)
> +#define RPPX1_IRQ_ID_PRE2_HIST BIT(12)
> +#define RPPX1_IRQ_ID_PRE2_FRAME_IN BIT(11)
> +#define RPPX1_IRQ_ID_PRE2_HSTART BIT(10)
> +#define RPPX1_IRQ_ID_PRE2_VSTART BIT(9)
> +#define RPPX1_IRQ_ID_OUT_FRAME BIT(3)
> +#define RPPX1_IRQ_ID_OUT_OFF BIT(2)
> +#define RPPX1_IRQ_ID_RMAP_MEAS BIT(1)
> +#define RPPX1_IRQ_ID_RMAP_DONE BIT(0)
> +
> +struct rppx1 {
> + struct device *dev;
#include <linux/device.h>
> + void __iomem *base;
Does __iomem come from
#include <linux/compiler_types.h> ?
> +
> + struct {
> + struct rpp_module acq;
> + struct rpp_module bls;
> + struct rpp_module lin;
> + struct rpp_module lsc;
> + struct rpp_module awbg;
> + struct rpp_module dpcc;
> + struct rpp_module bd;
> + struct rpp_module hist;
> + struct rpp_module hist256;
> + struct rpp_module exm;
> + } pre1;
> +
> + struct {
> + struct rpp_module acq;
> + struct rpp_module bls;
> + struct rpp_module lin;
> + struct rpp_module lsc;
> + struct rpp_module awbg;
> + struct rpp_module dpcc;
> + struct rpp_module bd;
> + struct rpp_module hist;
> + struct rpp_module exm;
> + } pre2;
> +
> + struct {
> + struct rpp_module awbg;
> + struct rpp_module ccor;
> + struct rpp_module hist;
> + struct rpp_module db;
> + struct rpp_module cac;
> + struct rpp_module ltm;
> + struct rpp_module ltmmeas;
> + struct rpp_module wbmeas;
> + struct rpp_module bdrgb;
> + struct rpp_module shrp;
> + } post;
> +
> + struct {
> + struct rpp_module ga;
> + struct rpp_module is;
> + struct rpp_module ccor;
> + struct rpp_module outif;
> + struct rpp_module outregs;
> + } hv;
> +
> + struct {
> + struct rpp_module ga;
> + struct rpp_module is;
> + struct rpp_module ccor;
> + struct rpp_module outif;
> + struct rpp_module outregs;
> + struct rpp_module xyz2luv;
> + } mv;
> +
> + struct rpp_module rmap;
> + struct rpp_module rmapmeas;
> +};
> +
> +void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value);
> +u32 rppx1_read(struct rppx1 *rpp, u32 offset);
> +
> +#endif /* __MEDIA_RPPX1_H__ */
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
> new file mode 100644
> index 000000000000..45f619ccb684
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
> @@ -0,0 +1,147 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define ACQ_VERSION_REG 0x0000
> +
> +#define ACQ_CTRL_REG 0x0004
> +#define ACQ_CTRL_ALTERNATIVE_CFG_MODE_ENABLE BIT(8)
> +#define ACQ_CTRL_RPP_MODE_MASK GENMASK(3, 1)
> +#define ACQ_CTRL_RPP_MODE_RAWBT601 (0 << 1)
> +#define ACQ_CTRL_RPP_MODE_BT656 (1 << 1)
> +#define ACQ_CTRL_RPP_MODE_BT601 (2 << 1)
> +#define ACQ_CTRL_RPP_MODE_BAYER (3 << 1)
> +#define ACQ_CTRL_RPP_MODE_DATA (4 << 1)
> +#define ACQ_CTRL_RPP_MODE_BAYERRGB (5 << 1)
> +#define ACQ_CTRL_RPP_MODE_RAWBT656 (6 << 1)
> +#define ACQ_CTRL_INFORM_EN_ENABLE BIT(0)
> +
> +#define ACQ_PROP_REG 0x0008
> +
> +#define ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB BIT(30)
> +#define ACQ_PROP_YUV_OUT_SEL BIT(25)
> +#define ACQ_PROP_MUX_DMA_SEL BIT(24)
> +#define ACQ_PROP_SECOND_INPUT_TYPE BIT(18)
> +#define ACQ_PROP_LATENCY_FIFO_INPUT_SELECTION BIT(15)
> +#define ACQ_PROP_INPUT_SELECTION_MASK GENMASK(14, 12)
> +#define ACQ_PROP_INPUT_SELECTION_8BIT (0 << 12)
> +#define ACQ_PROP_INPUT_SELECTION_10BIT (1 << 12)
> +#define ACQ_PROP_INPUT_SELECTION_12BIT (2 << 12)
> +#define ACQ_PROP_BAYER_PAT_MASK GENMASK(4, 3)
> +#define ACQ_PROP_BAYER_PAT_RGRG (0 << 3)
> +#define ACQ_PROP_BAYER_PAT_GRGR (1 << 3)
> +#define ACQ_PROP_BAYER_PAT_GBGB (2 << 3)
> +#define ACQ_PROP_BAYER_PAT_BGBG (3 << 3)
> +#define ACQ_PROP_VSYNC_POL BIT(2)
> +#define ACQ_PROP_HSYNC_POL BIT(1)
> +#define ACQ_PROP_SAMPLE_EDGE BIT(0)
> +
> +#define ACQ_H_OFFS_REG 0x000c
> +#define ACQ_V_OFFS_REG 0x0010
> +#define ACQ_H_SIZE_REG 0x0014
> +#define ACQ_V_SIZE_REG 0x0018
> +#define ACQ_OUT_H_OFFS_REG 0x001c
> +#define ACQ_OUT_V_OFFS_REG 0x0020
> +#define ACQ_OUT_H_SIZE_REG 0x0024
> +#define ACQ_OUT_V_SIZE_REG 0x0028
> +#define FLAGS_SHD_REG 0x002c
> +#define ACQ_OUT_H_OFFS_SHD_REG 0x0030
> +#define ACQ_OUT_V_OFFS_SHD_REG 0x0034
> +#define ACQ_OUT_H_SIZE_SHD_REG 0x0038
> +#define ACQ_OUT_V_SIZE_SHD_REG 0x003c
> +
> +static int rppx1_acq_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, ACQ_VERSION_REG) != 0x0b)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int rppx1_acq_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + u32 bayerpat, selection;
> +
> + rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_RPP_MODE_MASK,
> + ACQ_CTRL_RPP_MODE_BAYER);
> +
> + rpp_module_write(mod, ACQ_H_OFFS_REG, 0);
> + rpp_module_write(mod, ACQ_V_OFFS_REG, 0);
> + rpp_module_write(mod, ACQ_H_SIZE_REG, fmt->width);
> + rpp_module_write(mod, ACQ_V_SIZE_REG, fmt->height);
> + rpp_module_write(mod, ACQ_OUT_H_OFFS_REG, 0);
> + rpp_module_write(mod, ACQ_OUT_V_OFFS_REG, 0);
> + rpp_module_write(mod, ACQ_OUT_H_SIZE_REG, fmt->width);
> + rpp_module_write(mod, ACQ_OUT_V_SIZE_REG, fmt->height);
> +
> + switch (fmt->code) {
> + case MEDIA_BUS_FMT_SBGGR8_1X8:
> + case MEDIA_BUS_FMT_SBGGR10_1X10:
> + case MEDIA_BUS_FMT_SBGGR12_1X12:
> + mod->info.acq.raw_pattern = RPP_BGGR;
> + bayerpat = ACQ_PROP_BAYER_PAT_BGBG;
> + break;
> + case MEDIA_BUS_FMT_SGBRG8_1X8:
> + case MEDIA_BUS_FMT_SGBRG10_1X10:
> + case MEDIA_BUS_FMT_SGBRG12_1X12:
> + mod->info.acq.raw_pattern = RPP_GBRG;
> + bayerpat = ACQ_PROP_BAYER_PAT_GBGB;
> + break;
> + case MEDIA_BUS_FMT_SGRBG8_1X8:
> + case MEDIA_BUS_FMT_SGRBG10_1X10:
> + case MEDIA_BUS_FMT_SGRBG12_1X12:
> + mod->info.acq.raw_pattern = RPP_GRBG;
> + bayerpat = ACQ_PROP_BAYER_PAT_GRGR;
> + break;
> + case MEDIA_BUS_FMT_SRGGB8_1X8:
> + case MEDIA_BUS_FMT_SRGGB10_1X10:
> + case MEDIA_BUS_FMT_SRGGB12_1X12:
> + mod->info.acq.raw_pattern = RPP_RGGB;
> + bayerpat = ACQ_PROP_BAYER_PAT_RGRG;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt->code) {
> + case MEDIA_BUS_FMT_SBGGR8_1X8:
> + case MEDIA_BUS_FMT_SGBRG8_1X8:
> + case MEDIA_BUS_FMT_SGRBG8_1X8:
> + case MEDIA_BUS_FMT_SRGGB8_1X8:
> + selection = ACQ_PROP_INPUT_SELECTION_8BIT;
> + break;
> + case MEDIA_BUS_FMT_SBGGR10_1X10:
> + case MEDIA_BUS_FMT_SGBRG10_1X10:
> + case MEDIA_BUS_FMT_SGRBG10_1X10:
> + case MEDIA_BUS_FMT_SRGGB10_1X10:
> + selection = ACQ_PROP_INPUT_SELECTION_10BIT;
> + break;
> + case MEDIA_BUS_FMT_SBGGR12_1X12:
> + case MEDIA_BUS_FMT_SGBRG12_1X12:
> + case MEDIA_BUS_FMT_SGRBG12_1X12:
> + case MEDIA_BUS_FMT_SRGGB12_1X12:
> + selection = ACQ_PROP_INPUT_SELECTION_12BIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + rpp_module_write(mod, ACQ_PROP_REG, bayerpat | selection |
> + ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB);
> +
> + rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_INFORM_EN_ENABLE,
> + ACQ_CTRL_INFORM_EN_ENABLE);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_acq_ops = {
> + .probe = rppx1_acq_probe,
> + .start = rppx1_acq_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
> new file mode 100644
> index 000000000000..e20bc369ca8c
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
> @@ -0,0 +1,30 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define AWB_GAIN_VERSION_REG 0x0000
> +
> +#define AWB_ENABLE_REG 0x0004
> +#define AWB_ENABLE_AWB_GAIN_EN BIT(0)
> +
> +#define AWB_GAIN_GR_REG 0x0008
> +#define AWB_GAIN_GB_REG 0x000c
> +#define AWB_GAIN_R_REG 0x0010
> +#define AWB_GAIN_B_REG 0x0014
> +
> +static int rppx1_awbg_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, AWB_GAIN_VERSION_REG) != 3)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_awbg_ops = {
> + .probe = rppx1_awbg_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
> new file mode 100644
> index 000000000000..acbfbcd59591
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
> @@ -0,0 +1,52 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define DPF_VERSION_REG 0x0000
> +
> +#define DPF_MODE_REG 0x0004
> +#define DPF_MODE_USE_NF_GAIN BIT(9)
> +#define DPF_MODE_LSC_GAIN_COMP BIT(8)
> +#define DPF_MODE_NLL_SEGMENTATION BIT(6)
> +#define DPF_MODE_RB_FILTER_SIZE BIT(5)
> +#define DPF_MODE_R_FILTER_OFF BIT(4)
> +#define DPF_MODE_GR_FILTER_OFF BIT(3)
> +#define DPF_MODE_GB_FILTER_OFF BIT(2)
> +#define DPF_MODE_B_FILTER_OFF BIT(1)
> +#define DPF_MODE_DPF_ENABLE BIT(0)
> +
> +#define DPF_STRENGTH_R_REG 0x0008
> +#define DPF_STRENGTH_G_REG 0x000c
> +#define DPF_STRENGTH_B_REG 0x0010
> +#define DPF_S_WEIGHT_G_1_4_REG 0x0014
> +#define DPF_S_WEIGHT_G_5_6_REG 0x0018
> +#define DPF_S_WEIGHT_RB_1_4_REG 0x001c
> +#define DPF_S_WEIGHT_RB_5_6_REG 0x0020
> +
> +#define DPF_NLL_G_COEFF_REG_NUM 17
> +#define DPF_NLL_G_COEFF_REG(n) (0x0024 + (4 * (n)))
> +
> +#define DPF_NLL_RB_COEFF_REG_NUM 17
> +#define DPF_NLL_RB_COEFF_REG(n) (0x0068 + (4 * (n)))
> +
> +#define DPF_NF_GAIN_R_REG 0x00ac
> +#define DPF_NF_GAIN_GR_REG 0x00b0
> +#define DPF_NF_GAIN_GB_REG 0x00b4
> +#define DPF_NF_GAIN_B_REG 0x00b8
> +
> +static int rppx1_bd_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, DPF_VERSION_REG) != 5)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_bd_ops = {
> + .probe = rppx1_bd_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
> new file mode 100644
> index 000000000000..292f0b7bfd3f
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
> @@ -0,0 +1,80 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define RGBDENOISE_VERSION_REG 0x0000
> +
> +#define RGBDENOISE_HW_BYPASS_REG 0x0004
> +#define RGBDENOISE_HW_BYPASS_BYPASS_EN BIT(0)
> +
> +#define RGBDENOISE_SPNR_CTRL_REG 0x0008
> +#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_C_MASK GENMASK(11, 8)
> +#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_Y_MASK GENMASK(7, 4)
> +#define RGBDENOISE_SPNR_CTRL_C2NR_EN BIT(0)
> +
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_REG 0x000c
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_REG 0x0010
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_REG 0x0014
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_REG 0x0018
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_REG 0x001c
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_REG 0x0020
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_REG 0x0024
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_REG 0x0028
> +#define RGBDENOISE_SPNR_SPATIAL_COEF_0_3_REG 0x002c
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_REG 0x0030
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_REG 0x0034
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_REG 0x0038
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_REG 0x003c
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_REG 0x0040
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_REG 0x0044
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_REG 0x0048
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_REG 0x004c
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_REG 0x0050
> +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_REG 0x0054
> +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_REG 0x0058
> +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_REG 0x005c
> +#define RGBDENOISE_HW_BYPASS_SDW_REG 0x0060
> +#define RGBDENOISE_SPNR_CTRL_SDW_REG 0x0064
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_SDW_REG 0x0068
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_SDW_REG 0x006c
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_SDW_REG 0x0070
> +#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_SDW_REG 0x0074
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_SDW_REG 0x0078
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_SDW_REG 0x007c
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_SDW_REG 0x0080
> +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_SDW_REG 0x0084
> +#define RGBDENOISE_SPNR_SPATIAL_COEFF_0_3_SDW_REG 0x0088
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_SDW_REG 0x008c
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_SDW_REG 0x0090
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_SDW_REG 0x0094
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_SDW_REG 0x0098
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_SDW_REG 0x009c
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_SDW_REG 0x00a0
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_SDW_REG 0x00a4
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_SDW_REG 0x00a8
> +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_SDW_REG 0x00ac
> +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_SDW_REG 0x00b0
> +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_SDW_REG 0x00b4
> +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_SDW_REG 0x00b8
> +
> +static int rppx1_bdrgb_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, RGBDENOISE_VERSION_REG)) {
> + case 6:
> + mod->info.bdrgb.colorbits = 12;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_bdrgb_ops = {
> + .probe = rppx1_bdrgb_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
> new file mode 100644
> index 000000000000..de7008befd8e
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
> @@ -0,0 +1,59 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define BLS_VERSION_REG 0x0000
> +
> +#define BLS_CTRL_REG 0x0004
> +#define BLS_CTRL_BLS_WIN2 BIT(3)
> +#define BLS_CTRL_BLS_WIN1 BIT(2)
> +#define BLS_CTRL_BLS_MODE_MEASURED BIT(1)
> +#define BLS_CTRL_BLS_EN BIT(0)
> +
> +#define BLS_SAMPLES_REG 0x0008
> +#define BLS_H1_START_REG 0x000c
> +#define BLS_H1_STOP_REG 0x0010
> +#define BLS_V1_START_REG 0x0014
> +#define BLS_V1_STOP_REG 0x0018
> +#define BLS_H2_START_REG 0x001c
> +#define BLS_H2_STOP_REG 0x0020
> +#define BLS_V2_START_REG 0x0024
> +#define BLS_V2_STOP_REG 0x0028
> +#define BLS_A_FIXED_REG 0x002c
> +#define BLS_B_FIXED_REG 0x0030
> +#define BLS_C_FIXED_REG 0x0034
> +#define BLS_D_FIXED_REG 0x0038
> +#define BLS_A_MEASURED_REG 0x003c
> +#define BLS_B_MEASURED_REG 0x0040
> +#define BLS_C_MEASURED_REG 0x0044
> +#define BLS_D_MEASURED_REG 0x0048
> +
> +static int rppx1_bls_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, BLS_VERSION_REG)) {
> + case 3:
> + case 5:
> + mod->info.bls.colorbits = 12;
> + break;
> + case 2:
> + case 4:
> + mod->info.bls.colorbits = 20;
> + break;
> + case 6:
> + mod->info.bls.colorbits = 24;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_bls_ops = {
> + .probe = rppx1_bls_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
> new file mode 100644
> index 000000000000..5ed8c60982ba
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
> @@ -0,0 +1,29 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define CAC_VERSION_REG 0x0000
> +#define CAC_CTRL_REG 0x0004
> +#define CAC_COUNT_START_REG 0x0008
> +#define CAC_A_REG 0x000c
> +#define CAC_B_REG 0x0010
> +#define CAC_C_REG 0x0014
> +#define CAC_X_NORM_REG 0x0018
> +#define CAC_Y_NORM_REG 0x001c
> +
> +static int rppx1_cac_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, CAC_VERSION_REG) != 3)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_cac_ops = {
> + .probe = rppx1_cac_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
> new file mode 100644
> index 000000000000..4754b0bbce0a
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
> @@ -0,0 +1,106 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define CCOR_VERSION_REG 0x0000
> +
> +#define CCOR_COEFF_REG_NUM 9
> +#define CCOR_COEFF_REG(n) (0x0004 + (4 * (n)))
> +
> +#define CCOR_OFFSET_R_REG 0x0028
> +#define CCOR_OFFSET_G_REG 0x002c
> +#define CCOR_OFFSET_B_REG 0x0030
> +
> +#define CCOR_CONFIG_TYPE_REG 0x0034
> +#define CCOR_CONFIG_TYPE_USE_OFFSETS_AS_PRE_OFFSETS BIT(1)
> +#define CCOR_CONFIG_TYPE_CCOR_RANGE_AVAILABLE BIT(0)
> +
> +#define CCOR_RANGE_REG 0x0038
> +#define CCOR_RANGE_CCOR_C_RANGE BIT(1)
> +#define CCOR_RANGE_CCOR_Y_RANGE BIT(0)
> +
> +static int rppx1_ccor_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, CCOR_VERSION_REG)) {
> + case 3:
> + mod->info.ccor.colorbits = 12;
> + break;
> + case 4:
> + mod->info.ccor.colorbits = 20;
> + break;
> + case 5:
> + mod->info.ccor.colorbits = 24;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + mod->info.ccor.type = rpp_module_read(mod, CCOR_CONFIG_TYPE_REG);
> +
> + return 0;
> +}
> +
> +static int rppx1_ccor_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + /* Configure matrix in bypass mode. */
> + rpp_module_write(mod, CCOR_COEFF_REG(0), 0x1000);
> + rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0000);
> + rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0000);
> +
> + rpp_module_write(mod, CCOR_COEFF_REG(3), 0x0000);
> + rpp_module_write(mod, CCOR_COEFF_REG(4), 0x1000);
> + rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0000);
> +
> + rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0000);
> + rpp_module_write(mod, CCOR_COEFF_REG(7), 0x0000);
> + rpp_module_write(mod, CCOR_COEFF_REG(8), 0x1000);
> +
> + rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000);
> + rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000000);
> + rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000000);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_ccor_ops = {
> + .probe = rppx1_ccor_probe,
> + .start = rppx1_ccor_start,
> +};
> +
> +static int rppx1_ccor_csm_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + /* Reuse bypass matrix setup. */
> + if (fmt->code == MEDIA_BUS_FMT_RGB888_1X24)
> + return rppx1_ccor_start(mod, fmt);
> +
> + /* Color Transformation RGB to YUV according to ITU-R BT.709. */
> + rpp_module_write(mod, CCOR_COEFF_REG(0), 0x0367);
> + rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0b71);
> + rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0128);
> +
> + rpp_module_write(mod, CCOR_COEFF_REG(3), 0xfe2b);
> + rpp_module_write(mod, CCOR_COEFF_REG(4), 0xf9d5);
> + rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0800);
> +
> + rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0800);
> + rpp_module_write(mod, CCOR_COEFF_REG(7), 0xf8bc);
> + rpp_module_write(mod, CCOR_COEFF_REG(8), 0xff44);
> +
> + rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000);
> + rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000800);
> + rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000800);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_ccor_csm_ops = {
> + .probe = rppx1_ccor_probe,
> + .start = rppx1_ccor_csm_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
> new file mode 100644
> index 000000000000..5e233896cfc8
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
> @@ -0,0 +1,44 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define FILT_VERSION_REG 0x0000
> +
> +#define DEMOSAIC_REG 0x0004
> +#define DEMOSAIC_DEMOSAIC_BYPASS BIT(16)
> +#define DEMOSAIC_DEMOSAIC_TH_MASK GENMASK(15, 0)
> +
> +#define FILT_MODE_REG 0x0008
> +#define FILT_MODE_FILT_LP_SELECT_MASK GENMASK(11, 8)
> +#define FILT_MODE_FILT_CHR_H_MODE_MASK GENMASK(7, 6)
> +#define FILT_MODE_FILT_CHR_V_MODE_MASK GENMASK(5, 4)
> +#define FILT_MODE_FILT_MODE BIT(1)
> +#define FILT_MODE_FILT_ENABLE BIT(0)
> +
> +#define FILT_THRESH_BL0_REG 0x000c
> +#define FILT_THRESH_BL1_REG 0x0010
> +#define FILT_THRESH_SH0_REG 0x0014
> +#define FILT_THRESH_SH1_REG 0x0018
> +#define FILT_LUM_WEIGHT_REG 0x001c
> +#define FILT_FAC_SH1_REG 0x0020
> +#define FILT_FAC_SH0_REG 0x0024
> +#define FILT_FAC_MID_REG 0x0028
> +#define FILT_FAC_BL0_REG 0x002c
> +#define FILT_FAC_BL1_REG 0x0030
> +
> +static int rppx1_db_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, FILT_VERSION_REG) != 5)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_db_ops = {
> + .probe = rppx1_db_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
> new file mode 100644
> index 000000000000..ae0b65976452
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
> @@ -0,0 +1,76 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define DPCC_VERSION_REG 0x0000
> +
> +#define DPCC_MODE_REG 0x0004
> +#define DPCC_MODE_STAGE1_ENABLE BIT(2)
> +#define DPCC_MODE_GRAYSCALE_MODE BIT(1)
> +#define DPCC_MODE_DPCC_ENABLE BIT(0)
> +
> +#define DPCC_OUTPUT_MODE_REG 0x0008
> +#define DPCC_SET_USE_REG 0x000c
> +#define DPCC_METHODS_SET_1_REG 0x0010
> +#define DPCC_METHODS_SET_2_REG 0x0014
> +#define DPCC_METHODS_SET_3_REG 0x0018
> +#define DPCC_LINE_THRESH_1_REG 0x001c
> +#define DPCC_LINE_MAD_FAC_1_REG 0x0020
> +#define DPCC_PG_FAC_1_REG 0x0024
> +#define DPCC_RND_THRESH_1_REG 0x0028
> +#define DPCC_RG_FAC_1_REG 0x002c
> +#define DPCC_LINE_THRESH_2_REG 0x0030
> +#define DPCC_LINE_MAD_FAC_2_REG 0x0034
> +#define DPCC_PG_FAC_2_REG 0x0038
> +#define DPCC_RND_THRESH_2_REG 0x003c
> +#define DPCC_RG_FAC_2_REG 0x0040
> +#define DPCC_LINE_THRESH_3_REG 0x0044
> +#define DPCC_LINE_MAD_FAC_3_REG 0x0048
> +#define DPCC_PG_FAC_3_REG 0x004c
> +#define DPCC_RND_THRESH_3_REG 0x0050
> +#define DPCC_RG_FAC_3_REG 0x0054
> +#define DPCC_RO_LIMITS_REG 0x0058
> +#define DPCC_RND_OFFS_REG 0x005c
> +#define DPCC_BPT_CTRL_REG 0x0060
> +#define DPCC_BP_NUMBER_REG 0x0064
> +#define DPCC_BP_TADDR_REG 0x0068
> +#define DPCC_BP_POSITION_REG 0x006c
> +
> +static int rppx1_dpcc_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, DPCC_VERSION_REG)) {
> + case 2:
> + case 4:
> + case 6:
> + mod->info.dpcc.colorbits = 12;
> + break;
> + case 3:
> + case 5:
> + case 7:
> + mod->info.dpcc.colorbits = 24;
> + break;
> + default:
> + return -EINVAL;
> + }
Here and in all other modules, now that userspace is expected to
perform the right shifting by itself, 'colorbits' is probably not
needed anymore in any module.
> +
> + return 0;
> +}
> +
> +static int rppx1_dpcc_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + /* Bypass stage1 and DPCC. */
> + rpp_module_write(mod, DPCC_MODE_REG, 0);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_dpcc_ops = {
> + .probe = rppx1_dpcc_probe,
> + .start = rppx1_dpcc_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
> new file mode 100644
> index 000000000000..0c40300e13ad
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define EXM_VERSION_REG 0x0000
> +#define EXM_START_REG 0x0004
> +
> +#define EXM_CTRL_REG 0x0008
> +#define EXM_CTRL_EXM_UPDATE_ENABLE BIT(0)
> +
> +#define EXM_MODE_REG 0x000c
> +#define EXM_CHANNEL_SEL_REG 0x0010
> +#define EXM_LAST_MEAS_LINE_REG 0x0014
> +#define EXM_COEFF_R_REG 0x0018
> +#define EXM_COEFF_G_GR_REG 0x001c
> +#define EXM_COEFF_B_REG 0x0020
> +#define EXM_COEFF_GB_REG 0x0024
> +#define EXM_H_OFFS_REG 0x0028
> +#define EXM_V_OFFS_REG 0x002c
> +#define EXM_H_SIZE_REG 0x0030
> +#define EXM_V_SIZE_REG 0x0034
> +#define EXM_FORCED_UPD_START_LINE_REG 0x0038
> +#define EXM_VSTART_STATUS_REG 0x003c
> +
> +#define EXM_MEAN_REG_NUM 25
> +#define EXM_MEAN_REG(n) (0x0040 + (4 * (n)))
> +
> +static int rppx1_exm_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, EXM_VERSION_REG)) {
> + case 1:
> + mod->info.exm.resultbits = 8;
> + break;
> + case 3:
> + mod->info.exm.resultbits = 20;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_exm_ops = {
> + .probe = rppx1_exm_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
> new file mode 100644
> index 000000000000..d6c7f951cf29
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
> @@ -0,0 +1,49 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define GAMMA_OUT_VERSION_REG 0x0000
> +
> +#define GAMMA_OUT_ENABLE_REG 0x0004
> +#define GAMMA_OUT_ENABLE_GAMMA_OUT_EN BIT(0)
> +
> +#define GAMMA_OUT_MODE_REG 0x0008
> +#define GAMMA_OUT_MODE_GAMMA_OUT_EQU_SEGM BIT(0)
> +
> +#define GAMMA_OUT_Y_REG_NUM 17
> +#define GAMMA_OUT_Y_REG(n) (0x000c + (4 * (n)))
> +
> +static int rppx1_ga_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, GAMMA_OUT_VERSION_REG)) {
> + case 1:
> + mod->info.ga.colorbits = 12;
> + break;
> + case 2:
> + mod->info.ga.colorbits = 24;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rppx1_ga_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + /* Disable stage. */
> + rpp_module_write(mod, GAMMA_OUT_ENABLE_REG, 0);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_ga_ops = {
> + .probe = rppx1_ga_probe,
> + .start = rppx1_ga_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
> new file mode 100644
> index 000000000000..cab498ece5a8
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
> @@ -0,0 +1,76 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define HIST_VERSION_REG 0x0000
> +
> +#define HIST_CTRL_REG 0x0004
> +#define HIST_CTRL_HIST_UPDATE_ENABLE BIT(0)
> +
> +#define HIST_MODE_REG 0x0008
> +#define HIST_MODE_HIST_MODE_MASK GENMASK(2, 0)
> +#define HIST_MODE_HIST_MODE_DISABLE 0
> +#define HIST_MODE_HIST_MODE_YRGB 1
> +#define HIST_MODE_HIST_MODE_R 2
> +#define HIST_MODE_HIST_MODE_GR 3
> +#define HIST_MODE_HIST_MODE_B 4
> +#define HIST_MODE_HIST_MODE_GB 5
> +
> +#define HIST_CHANNEL_SEL_REG 0x000c
> +#define HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0)
> +
> +#define HIST_LAST_MEAS_LINE_REG 0x0010
> +#define HIST_SUBSAMPLING_REG 0x0014
> +#define HIST_COEFF_R_REG 0x0018
> +#define HIST_COEFF_G_REG 0x001c
> +#define HIST_COEFF_B_REG 0x0020
> +#define HIST_H_OFFS_REG 0x0024
> +#define HIST_V_OFFS_REG 0x0028
> +#define HIST_H_SIZE_REG 0x002c
> +#define HIST_V_SIZE_REG 0x0030
> +
> +#define HIST_SAMPLE_RANGE_REG 0x0034
> +#define HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK GENMASK(28, 24)
> +#define HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK GENMASK(23, 0)
> +
> +#define HIST_WEIGHT_00TO30_REG 0x0038
> +#define HIST_WEIGHT_40TO21_REG 0x003c
> +#define HIST_WEIGHT_31TO12_REG 0x0040
> +#define HIST_WEIGHT_22TO03_REG 0x0044
> +#define HIST_WEIGHT_13TO43_REG 0x0048
> +#define HIST_WEIGHT_04TO34_REG 0x004c
> +#define HIST_WEIGHT_44_REG 0x0050
> +#define HIST_FORCED_UPD_START_LINE_REG 0x0054
> +#define HIST_FORCED_UPDATE_REG 0x0058
> +#define HIST_VSTART_STATUS_REG 0x005c
> +
> +#define HIST_BIN_REG_NUM 32
> +#define HIST_BIN_REG(n) (0x0060 + (4 * (n)))
> +
> +static int rppx1_hist_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, HIST_VERSION_REG)) {
> + case 3:
> + mod->info.hist.colorbits = 12;
> + break;
> + case 4:
> + mod->info.hist.colorbits = 20;
> + break;
> + case 5:
> + mod->info.hist.colorbits = 24;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_hist_ops = {
> + .probe = rppx1_hist_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
> new file mode 100644
> index 000000000000..5b846b415a49
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
> @@ -0,0 +1,46 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define HIST256_VERSION_REG 0x0000
> +#define HIST256_MODE_REG 0x0004
> +#define HIST256_MODE_HIST256_MODE BIT(0)
> +
> +#define HIST256_CHANNEL_SEL_REG 0x0008
> +#define HIST256_CHANNEL_SEL_CHANNEL_SELECT GENMASK(2, 0)
> +
> +#define HIST256_H_OFFS_REG 0x000c
> +#define HIST256_V_OFFS_REG 0x0010
> +#define HIST256_H_SIZE_REG 0x0014
> +#define HIST256_V_SIZE_REG 0x0018
> +#define HIST256_SAMPLE_OFFSET_REG 0x001c
> +#define HIST256_SAMPLE_SCALE_REG 0x0020
> +#define HIST256_MEAS_RESULT_ADDR_AUTOINCR_REG 0x0024
> +#define HIST256_MEAS_RESULT_ADDR_REG 0x0028
> +#define HIST256_MEAS_RESULT_DATA_REG 0x002c
> +
> +#define HIST256_LOG_ENABLE_REG 0x0030
> +#define HIST256_LOG_ENABLE_HIST256_LOG_EN BIT(0)
> +
> +#define HIST256_LOG_DX_LO_REG 0x0034
> +#define HIST256_LOG_DX_HI_REG 0x0038
> +
> +#define HIST256_Y_REG_NUM 17
> +#define HIST256_Y_REG(n) (0x0040 + (4 * (n)))
> +
> +static int rppx1_hist256_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, HIST256_VERSION_REG) != 2)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_hist256_ops = {
> + .probe = rppx1_hist256_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_is.c b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c
> new file mode 100644
> index 000000000000..3637a2e677ca
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c
> @@ -0,0 +1,42 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define IS_VERSION 0x0000
> +#define IS_H_OFFS 0x0008
> +#define IS_V_OFFS 0x000c
> +#define IS_H_SIZE 0x0010
> +#define IS_V_SIZE 0x0014
> +#define IS_H_OFFS_SHD 0x0024
> +#define IS_V_OFFS_SHD 0x0028
> +#define IS_H_SIZE_SHD 0x002c
> +#define IS_V_SIZE_SHD 0x0030
> +
> +static int rppx1_is_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, IS_VERSION) != 1)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int rppx1_is_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + rpp_module_write(mod, IS_H_OFFS, 0);
> + rpp_module_write(mod, IS_V_OFFS, 0);
> + rpp_module_write(mod, IS_H_SIZE, fmt->width);
> + rpp_module_write(mod, IS_V_SIZE, fmt->height);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_is_ops = {
> + .probe = rppx1_is_probe,
> + .start = rppx1_is_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
> new file mode 100644
> index 000000000000..f595f56a292e
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +/* NOTE: The module is called LIN the registers GAMMA_IN. */
> +#define LIN_VERSION_REG 0x0000
> +
> +#define LIN_ENABLE_REG 0x0004
> +#define LIN_ENABLE_GAMMA_IN_EN BIT(0)
> +
> +#define LIN_DX_LO_REG 0x0008
> +#define LIN_DX_HI_REG 0x000c
> +
> +#define LIN_R_Y_REG_NUM 17
> +#define LIN_R_Y_REG(n) (0x0010 + (4 * (n)))
> +
> +#define LIN_G_Y_REG_NUM 17
> +#define LIN_G_Y_REG(n) (0x0054 + (4 * (n)))
> +
> +#define LIN_B_Y_REG_NUM 17
> +#define LIN_B_Y_REG(n) (0x0098 + (4 * (n)))
> +
> +static int rppx1_lin_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, LIN_VERSION_REG)) {
> + case 7:
> + mod->info.lin.colorbits = 12;
> + break;
> + case 8:
> + mod->info.lin.colorbits = 20;
> + break;
> + case 9:
> + mod->info.lin.colorbits = 24;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rppx1_lin_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + rpp_module_clrset(mod, LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN, 0);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_lin_ops = {
> + .probe = rppx1_lin_probe,
> + .start = rppx1_lin_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
> new file mode 100644
> index 000000000000..e8acdf744956
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define LSC_VERSION_REG 0x0000
> +
> +#define LSC_CTRL_REG 0x0004
> +#define LSC_CTRL_LSC_EN BIT(0)
> +
> +#define LSC_R_TABLE_ADDR_REG 0x0008
> +#define LSC_GR_TABLE_ADDR_REG 0x000c
> +#define LSC_B_TABLE_ADDR_REG 0x0010
> +#define LSC_GB_TABLE_ADDR_REG 0x0014
> +#define LSC_R_TABLE_DATA_REG 0x0018
> +#define LSC_GR_TABLE_DATA_REG 0x001c
> +#define LSC_B_TABLE_DATA_REG 0x0020
> +#define LSC_GB_TABLE_DATA_REG 0x0024
> +#define LSC_XGRAD_01_REG 0x0028
> +#define LSC_XGRAD_23_REG 0x002c
> +#define LSC_XGRAD_45_REG 0x0030
> +#define LSC_XGRAD_67_REG 0x0034
> +#define LSC_XGRAD_89_REG 0x0038
> +#define LSC_XGRAD_1011_REG 0x003c
> +#define LSC_XGRAD_1213_REG 0x0040
> +#define LSC_XGRAD_1415_REG 0x0044
> +#define LSC_YGRAD_01_REG 0x0048
> +#define LSC_YGRAD_23_REG 0x004c
> +#define LSC_YGRAD_45_REG 0x0050
> +#define LSC_YGRAD_67_REG 0x0054
> +#define LSC_YGRAD_89_REG 0x0058
> +#define LSC_YGRAD_1011_REG 0x005c
> +#define LSC_YGRAD_1213_REG 0x0060
> +#define LSC_YGRAD_1415_REG 0x0064
> +#define LSC_XSIZE_01_REG 0x0068
> +#define LSC_XSIZE_23_REG 0x006c
> +#define LSC_XSIZE_45_REG 0x0070
> +#define LSC_XSIZE_67_REG 0x0074
> +#define LSC_XSIZE_89_REG 0x0078
> +#define LSC_XSIZE_1011_REG 0x007c
> +#define LSC_XSIZE_1213_REG 0x0080
> +#define LSC_XSIZE_1415_REG 0x0084
> +#define LSC_YSIZE_01_REG 0x0088
> +#define LSC_YSIZE_23_REG 0x008c
> +#define LSC_YSIZE_45_REG 0x0090
> +#define LSC_YSIZE_67_REG 0x0094
> +#define LSC_YSIZE_89_REG 0x0098
> +#define LSC_YSIZE_1011_REG 0x009c
> +#define LSC_YSIZE_1213_REG 0x00a0
> +#define LSC_YSIZE_1415_REG 0x00a4
> +#define LSC_TABLE_SEL_REG 0x00a8
> +#define LSC_STATUS_REG 0x00ac
> +
> +static int rppx1_lsc_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, LSC_VERSION_REG) != 0x04)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_lsc_ops = {
> + .probe = rppx1_lsc_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
> new file mode 100644
> index 000000000000..693cf5ed1689
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
> @@ -0,0 +1,48 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define LTM_VERSION_REG 0x0000
> +
> +#define LTM_CTRL_REG 0x0004
> +#define LTM_CTRL_LTM_ENABLE BIT(0)
> +
> +#define LTM_RGB_WEIGHTS_REG 0x0008
> +#define LTM_CLB_LINESIZE_REG 0x000c
> +#define LTM_TONECURVE_1_REG 0x0010
> +#define LTM_TONECURVE_2_REG 0x0014
> +#define LTM_TONECURVE_3_REG 0x0018
> +#define LTM_TONECURVE_4_REG 0x001c
> +#define LTM_TONECURVE_5_REG 0x0020
> +#define LTM_TONECURVE_6_REG 0x0024
> +#define LTM_TONECURVE_YM_REG(n) (0x0028 + (4 * (n)))
> +#define LTM_L0W_REG 0x00ec
> +#define LTM_L0W_R_REG 0x00f0
> +#define LTM_L0D_REG 0x00f4
> +#define LTM_L0D_R_REG 0x00f8
> +#define LTM_KMIND_REG 0x00fc
> +#define LTM_KMAXD_REG 0x0100
> +#define LTM_KDIFFD_REG 0x0104
> +#define LTM_KDIFFD_R_REG 0x0108
> +#define LTM_KW_REG 0x010c
> +#define LTM_KW_R_REG 0x0110
> +#define LTM_CGAIN_REG 0x0114
> +#define LTM_LPRCH_R_HIGH_REG 0x0118
> +#define LTM_LPRCH_R_LOW_REG 0x011c
> +
> +static int rppx1_ltm_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, LTM_VERSION_REG) != 8)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_ltm_ops = {
> + .probe = rppx1_ltm_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
> new file mode 100644
> index 000000000000..efc3d09db5eb
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define LTM_MEAS_VERSION_REG 0x0000
> +
> +#define LTM_MEAS_CTRL_REG 0x0004
> +#define LTM_MEAS_CTRL_LTM_MEAS_ENABLE BIT(0)
> +
> +#define LTM_MEAS_RGB_WEIGHTS_REG 0x0008
> +#define LTM_MEAS_H_OFFS_REG 0x000c
> +#define LTM_MEAS_V_OFFS_REG 0x0010
> +#define LTM_MEAS_H_SIZE_REG 0x0014
> +#define LTM_MEAS_V_SIZE_REG 0x0018
> +
> +#define LTM_MEAS_PRC_THRESH_NUM 8
> +#define LTM_MEAS_PRC_THRESH_REG(n) (0x001c + (4 * (n)))
> +
> +#define LTM_MEAS_PRC_REG_NUM 8
> +#define LTM_MEAS_PRC_REG(n) (0x003c + (4 * (n)))
> +
> +#define LTM_MEAS_L_MIN_REG 0x005c
> +#define LTM_MEAS_L_MAX_REG 0x0060
> +#define LTM_MEAS_L_GMEAN_REG 0x0064
> +
> +static int rppx1_ltmmeas_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, LTM_MEAS_VERSION_REG) != 1)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_ltmmeas_ops = {
> + .probe = rppx1_ltmmeas_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
> new file mode 100644
> index 000000000000..742c81844912
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
> @@ -0,0 +1,45 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define OUT_IF_VERSION_REG 0x0000
> +
> +#define OUT_IF_ON_REG 0x0004
> +#define OUT_IF_ON_RPP_ON BIT(0)
> +
> +#define OUT_IF_OFF_REG 0x0008
> +
> +#define OUT_IF_NR_FRAMES_REG 0x000c
> +#define OUT_IF_NR_FRAMES_NR_FRAMES GENMASK(9, 0)
> +
> +#define OUT_IF_NR_FRAMES_CNT_REG 0x0010
> +#define FLAGS_SHD_REG 0x0018
> +
> +static int rppx1_outif_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, OUT_IF_VERSION_REG) != 1)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int rppx1_outif_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + rpp_module_clrset(mod, OUT_IF_NR_FRAMES_REG,
> + OUT_IF_NR_FRAMES_NR_FRAMES, 0);
> +
> + rpp_module_write(mod, OUT_IF_ON_REG, OUT_IF_ON_RPP_ON);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_outif_ops = {
> + .probe = rppx1_outif_probe,
> + .start = rppx1_outif_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
> new file mode 100644
> index 000000000000..63d61e1dc447
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
> @@ -0,0 +1,75 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define OUTREGS_VERSION_REG 0x0000
> +
> +#define OUT_MODE_REG 0x0004
> +#define OUT_MODE_UNSELECTED_MODE_MASK GENMASK(11, 8)
> +#define OUT_MODE_UNSELECTED_MODE_MAIN (0x1 << 8)
> +#define OUT_MODE_UNSELECTED_MODE_PRE1 (0x2 << 8)
> +#define OUT_MODE_UNSELECTED_MODE_PRE2 (0x4 << 8)
> +#define OUT_MODE_IN_SEL_MASK GENMASK(3, 0)
> +#define OUT_MODE_IN_SEL_MAIN 1
> +#define OUT_MODE_IN_SEL_PRE1 2
> +#define OUT_MODE_IN_SEL_PRE2 4
> +
> +#define OUT_CONV_422_METHOD_REG 0x0008
> +#define OUT_CONV_422_METHOD_CONV_422_METHOD_MASK GENMASK(1, 0)
> +#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1 0
> +#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED2 1
> +#define OUT_CONV_422_METHOD_CONV_422_METHOD_NON_CO_SITED 2
> +
> +#define OUTREGS_FORMAT_REG 0x000c
> +#define OUTREGS_FORMAT_OUTPUT_FORMAT_MASK GENMASK(1, 0)
> +#define OUTREGS_FORMAT_OUTPUT_FORMAT_RGB 0
> +#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422 1
> +#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV420 2
> +
> +static int rppx1_outregs_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, OUTREGS_VERSION_REG) != 2)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int rppx1_outregs_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + u32 format;
> +
> + switch (fmt->code) {
> + case MEDIA_BUS_FMT_YUYV12_1X24:
> + format = OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422;
> + break;
> + case MEDIA_BUS_FMT_RGB888_1X24:
> + format = OUTREGS_FORMAT_OUTPUT_FORMAT_RGB;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + rpp_module_clrset(mod, OUT_MODE_REG,
> + OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MASK,
> + OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MAIN);
> +
> + rpp_module_clrset(mod, OUT_CONV_422_METHOD_REG,
> + OUT_CONV_422_METHOD_CONV_422_METHOD_MASK,
> + OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1);
> +
> + rpp_module_clrset(mod, OUTREGS_FORMAT_REG,
> + OUTREGS_FORMAT_OUTPUT_FORMAT_MASK, format);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_outregs_ops = {
> + .probe = rppx1_outregs_probe,
> + .start = rppx1_outregs_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
> new file mode 100644
> index 000000000000..f6773a452bd1
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define RMAP_DATA_VERSION_REG 0x0000
> +
> +#define RMAP_CTRL_REG 0x0004
> +#define RMAP_CTRL_BYPASS_LONG BIT(2)
> +
> +#define RMAP_WBTHRESHOLD_LONG_REG 0x0008
> +#define RMAP_WBTHRESHOLD_SHORT_REG 0x000c
> +#define RMAP_RESERVED_1_REG 0x0010
> +#define RMAP_WBGAIN_LONG_RED_REG 0x0014
> +#define RMAP_WBGAIN_LONG_BLUE_REG 0x0018
> +#define RMAP_WBGAIN_SHORT_RED_REG 0x001c
> +#define RMAP_WBGAIN_SHORT_BLUE_REG 0x0020
> +#define RMAP_RESERVED_2_REG 0x0024
> +#define RMAP_RESERVED_3_REG 0x0028
> +#define RMAP_MAP_FAC_SHORT_REG 0x002c
> +#define RMAP_RESERVED_4_REG 0x0030
> +#define RMAP_MIN_THRES_SHORT_REG 0x0034
> +#define RMAP_MAX_THRES_SHORT_REG 0x0038
> +#define RMAP_STEPSIZE_SHORT_REG 0x003c
> +#define RMAP_MIN_THRES_LONG_REG 0x0040
> +#define RMAP_MAX_THRES_LONG_REG 0x0044
> +#define RMAP_STEPSIZE_LONG_REG 0x0048
> +#define RMAP_CLB_LINESIZE_REG 0x004c
> +
> +static int rppx1_rmap_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, RMAP_DATA_VERSION_REG)) {
> + case 8:
> + mod->info.rmap.colorbits_high = 20;
> + mod->info.rmap.colorbits_low = 12;
> + break;
> + case 9:
> + mod->info.rmap.colorbits_high = 24;
> + mod->info.rmap.colorbits_low = 12;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rppx1_rmap_start(struct rpp_module *mod,
> + const struct v4l2_mbus_framefmt *fmt)
> +{
> + /* Bypass radiance mapping and use the long exposure channel (PRE1). */
> + rpp_module_write(mod, RMAP_CTRL_REG, RMAP_CTRL_BYPASS_LONG);
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_rmap_ops = {
> + .probe = rppx1_rmap_probe,
> + .start = rppx1_rmap_start,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
> new file mode 100644
> index 000000000000..c04f92508f6d
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
> @@ -0,0 +1,47 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define RMAP_MEAS_VERSION_REG 0x0000
> +#define RMAP_MEAS_MODE_REG 0x0004
> +#define RMAP_MEAS_SUBSAMPLING_REG 0x0008
> +#define RMAP_MEAS_RESERVED_1_REG 0x000c
> +#define RMAP_MEAS_MIN_THRES_SHORT_REG 0x0010
> +#define RMAP_MEAS_MAX_THRES_SHORT_REG 0x0014
> +#define RMAP_MEAS_MAX_THRES_LONG_REG 0x0018
> +#define RMAP_MEAS_H_OFFS_REG 0x001c
> +#define RMAP_MEAS_V_OFFS_REG 0x0020
> +#define RMAP_MEAS_H_SIZE_REG 0x0024
> +#define RMAP_MEAS_V_SIZE_REG 0x0028
> +#define RMAP_MEAS_LAST_MEAS_LINE_REG 0x002c
> +#define RMAP_MEAS_LS_RESULTSHORT0_REG 0x0030
> +#define RMAP_MEAS_LS_RESULTLONG0_REG 0x0034
> +#define RMAP_MEAS_RESERVED_2_REG 0x0038
> +#define RMAP_MEAS_RESERVED_3_REG 0x003c
> +#define RMAP_MEAS_LS_RESULTSHORT1_REG 0x0040
> +#define RMAP_MEAS_LS_RESULTLONG1_REG 0x0044
> +#define RMAP_MEAS_RESERVED_4_REG 0x0048
> +#define RMAP_MEAS_RESERVED_5_REG 0x004c
> +
> +static int rppx1_rmapmeas_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, RMAP_MEAS_VERSION_REG)) {
> + case 3:
> + mod->info.rmapmeas.colorbits_high = 24;
> + mod->info.rmapmeas.colorbits_low = 12;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_rmapmeas_ops = {
> + .probe = rppx1_rmapmeas_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
> new file mode 100644
> index 000000000000..5bec022e8f05
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define SHRPCNR_VERSION_REG 0x0000
> +
> +#define SHRPCNR_CTRL_REG 0x0004
> +#define SHRPCNR_CTRL_CAD_EN BIT(3)
> +#define SHRPCNR_CTRL_DESAT_EN BIT(2)
> +#define SHRPCNR_CTRL_CNR_EN BIT(1)
> +#define SHRPCNR_CTRL_SHARPEN_EN BIT(0)
> +
> +#define SHRPCNR_PARAM_REG 0x0008
> +#define SHRPCNR_PARAM_SHARP_FACTOR_MASK GENMASK(19, 12)
> +#define SHRPCNR_PARAM_CORING_THR_MASK GENMASK(11, 0)
> +
> +#define SHRPCNR_MAT_1_REG 0x000c
> +#define SHRPCNR_MAT_2_REG 0x0010
> +#define SHRPCNR_CLB_LINESIZE_REG 0x0014
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_0_REG 0x0018
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_1_REG 0x001c
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_2_REG 0x0020
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_3_REG 0x0024
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_4_REG 0x0028
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_5_REG 0x002c
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_6_REG 0x0030
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_7_REG 0x0034
> +#define SHRPCNR_YUV2RGB_CCOR_COEFF_8_REG 0x0038
> +#define SHRPCNR_YUV2RGB_CCOR_OFFSET_R_REG 0x003c
> +#define SHRPCNR_YUV2RGB_CCOR_OFFSET_G_REG 0x0040
> +#define SHRPCNR_YUV2RGB_CCOR_OFFSET_B_REG 0x0044
> +
> +#define SHRPCNR_CNR_THRES_REG 0x0048
> +#define SHRPCNR_CNR_THRES_CNR_THRES_CR_MASK GENMASK(27, 16)
> +#define SHRPCNR_CNR_THRES_CNR_THRES_CB_MASK GENMASK(11, 0)
> +
> +#define SHRPCNR_CRED_THRES_REG 0x004c
> +#define SHRPCNR_CRED_SLOPE_REG 0x0050
> +#define SHRPCNR_CAD_RESTORE_LVL_REG 0x0054
> +#define SHRPCNR_CAD_THRESH_V_UNEG_REG 0x0058
> +#define SHRPCNR_CAD_THRESH_V_UPOS_REG 0x005c
> +#define SHRPCNR_CAD_THRESH_U_REG 0x0060
> +
> +static int rppx1_shrp_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, SHRPCNR_VERSION_REG)) {
> + case 2:
> + mod->info.shrp.colorbits = 12;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_shrp_ops = {
> + .probe = rppx1_shrp_probe,
> +};
There are quite some modules here that have no corresponding user in
libcamera and which are not exercized.
As long as we don't have a userspace user, I would refrein from adding
them to the driver to avoid committing to a uAPI before anyone
actually uses it.
If you want to keep the kernel module, fine, but no ABI for blocks
without a userspace user.
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> new file mode 100644
> index 000000000000..3d197d914d07
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> @@ -0,0 +1,61 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define AWB_MEAS_VERSION_REG 0x0000
> +
> +#define AWB_MEAS_PROP_REG 0x0004
> +#define AWB_MEAS_PROP_MEAS_MODE_RGB BIT(16) /* 0: YCbCr 1: RGB */
> +#define AWB_MEAS_PROP_YMAX BIT(2)
> +#define AWB_MEAS_PROP_AWB_MODE_ON BIT(1)
> +
> +#define AWB_MEAS_H_OFFS_REG 0x0008
> +#define AWB_MEAS_V_OFFS_REG 0x000c
> +#define AWB_MEAS_H_SIZE_REG 0x0010
> +#define AWB_MEAS_V_SIZE_REG 0x0014
> +#define AWB_MEAS_FRAMES_REG 0x0018
> +#define AWB_MEAS_REF_CB_MAX_B_REG 0x001c
> +#define AWB_MEAS_REF_CR_MAX_R_REG 0x0020
> +#define AWB_MEAS_MAX_Y_REG 0x0024
> +#define AWB_MEAS_MIN_Y_MAX_G_REG 0x0028
> +#define AWB_MEAS_MAX_CSUM_REG 0x002c
> +#define AWB_MEAS_MIN_C_REG 0x0030
> +#define AWB_MEAS_WHITE_CNT_REG 0x0034
> +#define AWB_MEAS_MEAN_Y_G_REG 0x0038
> +#define AWB_MEAS_MEAN_CB_B_REG 0x003c
> +#define AWB_MEAS_MEAN_CR_R_REG 0x0040
> +
> +#define AWB_MEAS_CCOR_COEFF_NUM 9
> +#define AWB_MEAS_CCOR_COEFF_REG(n) (0x0044 + (4 * (n)))
> +
> +#define AWB_MEAS_CCOR_OFFSET_R_REG 0x0068
> +#define AWB_MEAS_CCOR_OFFSET_G_REG 0x006c
> +#define AWB_MEAS_CCOR_OFFSET_B_REG 0x0070
> +
> +static int rppx1_wbmeas_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + switch (rpp_module_read(mod, AWB_MEAS_VERSION_REG)) {
> + case 1:
> + mod->info.wbmeas.colorbits = 8;
> + break;
> + case 2:
> + mod->info.wbmeas.colorbits = 20;
> + break;
> + case 3:
> + mod->info.wbmeas.colorbits = 24;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_wbmeas_ops = {
> + .probe = rppx1_wbmeas_probe,
> +};
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
> new file mode 100644
> index 000000000000..73789c48c057
> --- /dev/null
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
> @@ -0,0 +1,26 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +
> +#include "rpp_module.h"
> +
> +#define XYZ2LUV_VERSION_REG 0x0000
> +#define XYZ2LUV_U_REF_REG 0x0004
> +#define XYZ2LUV_V_REF_REG 0x0008
> +#define XYZ2LUV_LUMA_OUT_FAC_REG 0x000c
> +#define XYZ2LUV_CHROMA_OUT_FAC_REG 0x0010
> +
> +static int rppx1_xyz2luv_probe(struct rpp_module *mod)
> +{
> + /* Version check. */
> + if (rpp_module_read(mod, XYZ2LUV_VERSION_REG) != 4)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +const struct rpp_module_ops rppx1_xyz2luv_ops = {
> + .probe = rppx1_xyz2luv_probe,
> +};
> diff --git a/include/media/rppx1.h b/include/media/rppx1.h
> new file mode 100644
> index 000000000000..cb3470c27ceb
> --- /dev/null
> +++ b/include/media/rppx1.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2025 Renesas Electronics Corp.
> + * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
> + */
> +#ifndef __MEDIA_DCT_RPPX1_H__
> +#define __MEDIA_DCT_RPPX1_H__
> +
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/media/dreamchip/rppx1-config.h>
> +
> +#include <media/videobuf2-core.h>
> +
> +struct rppx1;
> +
> +struct rppx1 *rppx1_create(void __iomem *base, struct device *dev);
> +
> +void rppx1_destroy(struct rppx1 *rpp);
> +
> +int rppx1_start(struct rppx1 *rpp, const struct v4l2_mbus_framefmt *input,
> + const struct v4l2_mbus_framefmt *hv,
> + const struct v4l2_mbus_framefmt *mv);
> +
> +int rppx1_stop(struct rppx1 *rpp);
> +
> +bool rppx1_interrupt(struct rppx1 *rpp, u32 *isc);
> +
> +typedef int (*rppx1_reg_write)(void *priv, u32 offset, u32 value);
> +int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> + rppx1_reg_write write, void *priv);
> +
> +void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf);
> +
> +#endif /* __MEDIA_DCT_RPPX1_H__ */
> diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
> new file mode 100644
> index 000000000000..26627be6f483
> --- /dev/null
> +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
As said, I don't think it makes much sense to add an almost empty uapi
file.
I would add it in one commit.
Please retain Jai's authorship and add my Co-developed-by tag as I
rewrote most of the documentation and reworked most blocks.
> @@ -0,0 +1,66 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Dreamchip RPP-X1 ISP Driver - Userspace API
> + *
> + * Copyright (C) 2026 Renesas Electronics Corp.
> + * Copyright (C) 2026 Ideas on Board Oy
> + * Copyright (C) 2026 Ragnatech AB
> + */
> +
> +#ifndef __UAPI_RPP_X1_CONFIG_H
> +#define __UAPI_RPP_X1_CONFIG_H
> +
> +#include <linux/types.h>
> +#include <linux/media/v4l2-isp.h>
> +
> +/**
> + * struct rppx1_window - Measurement window
> + *
> + * RPP-X1 measurement window. Different blocks use a window or multiple
> + * windows for measurement purposes. This defines a common type for all of
> + * them. The number of relevant bits depends on the block where the window is
> + * used and is specified in the per-block description
> + *
> + * @h_offs: horizontal offset from the left of the frame in pixels
> + * @v_offs: vertical offset from the top of the frame in pixels
> + * @h_size: horizontal size of the window in pixels
> + * @v_size: vertical size of the window in pixels
> + */
> +struct rppx1_window {
> + __u16 h_offs;
> + __u16 v_offs;
> + __u16 h_size;
> + __u16 v_size;
> +};
> +
> +/* ---------------------------------------------------------------------------
> + * Parameter Structures
> + *
> + * Native RPP-X1 precision. Fields use __u32 where the hardware provides
> + * wider-than-8-bit results.
I think you could drop the first part: of course the RPP-X1 uAPI
header uses the RPP-X1 precision. You could add:
The same ISP block might be instantiated in multiple pipeliness
and operate on a different bitdepth/precision. For fields of
varying length among different instances of the same block, use
a data type that can accommodate the larger bitdepth/precision.
Or something similar
> + */
> +
> +/**
> + * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> + *
> + * Some types are reported twice as the same block might be instantiated in
> + * multiple pipes.
> + */
> +#define RPPX1_PARAMS_MAX_SIZE 0
> +
> +/* ---------------------------------------------------------------------------
> + * Statistics Structures
> + *
> + * Native RPP-X1 precision. Fields use __u32 where the hardware provides
> + * wider-than-8-bit results.
Same here
> + */
> +
> +/**
> + * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
> + *
> + * Some types are reported twice as the same block might be instantiated in
> + * multiple pipes.
> + */
> +#define RPPX1_STATS_MAX_SIZE 0
> +
> +#endif /* __UAPI_RPP_X1_CONFIG_H */
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [v8 03/14] media: rcar-isp: Add support for ISPCORE
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
2026-05-04 1:05 ` [v8 01/14] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Niklas Söderlund
2026-05-04 1:05 ` [v8 02/14] media: rppx1: Add framework to support Dreamchip RPPX1 ISP Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 15:02 ` Jacopo Mondi
2026-05-04 1:05 ` [v8 04/14] media: rppx1: wbmeas: Add support for white balance measurement Niklas Söderlund
` (11 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
The Renesas R-Car ISP block consists of two different IP blocks, one
CSI-2 Channel Selector (CSISP) and one traditional ISP for image
operation (ISPCORE). The R-Car ISP driver currently supports the CSISP
functionality as part of the video capture pipeline, this change adds
support for the ISPCORE functionality.
The ISPCORE functionality is further split in two parts, a Renesas
specific part and a Dream Chip Real-time Pixel Processor IP part
(RPPX1). The Renesas part deals with I/O to/from the block while the
RPPX1 part deals with the actual ISP functions.
The RPPX1 functionality is implemented in a support framework (DCT
RPPX1) as this block can be used by different vendors or setups. This
change deals with the Renesas part of exposing the V4L2 elements needed
for a user-space interface to the RPPX1 and deals with the DMA to/from
the RPP block. It also facilitates the user-space V4L2 API to allow
configuring the RPPX1 using the DCT RPPX1 support framework.
The functionality exposed are one input video device where RAW bayer
frames can be queued for processing, one output video device where the
debayerd image can be read as either ABGR32 or NV16M format. Further
more a video device to queue the image processing parameters to
configure the RPPX1 IPS as well as a video device to read statistics
about the processed image is available.
There is no change in the operation of the CSISP functionality.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
* Changes since v7
- Fix kdoc tag.
- Update to init ISP device pointer.
* Changes since v5
- Use NULL instead of 0 when init rcar_isp_core in probe.
* Changes since v4
- Add depend on VIDEO_RENESAS_VSP1
* Changes since v3
- Make sure VSPX is stopped before queueing next job, starting with
v6.18-rc1 the two can get out of sync.
- Fix a possible race with VSPX when stopping streaming.
* Changes since v2
- Rework the start procedure so the ISP is reset without from a context
that can sleep.
---
.../media/platform/renesas/rcar-isp/Kconfig | 2 +
.../media/platform/renesas/rcar-isp/Makefile | 2 +-
.../media/platform/renesas/rcar-isp/core-io.c | 1017 +++++++++++++++++
.../media/platform/renesas/rcar-isp/core.c | 826 +++++++++++++
.../media/platform/renesas/rcar-isp/csisp.c | 48 +-
.../platform/renesas/rcar-isp/risp-core.h | 170 +++
6 files changed, 2057 insertions(+), 8 deletions(-)
create mode 100644 drivers/media/platform/renesas/rcar-isp/core-io.c
create mode 100644 drivers/media/platform/renesas/rcar-isp/core.c
create mode 100644 drivers/media/platform/renesas/rcar-isp/risp-core.h
diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig
index 242f6a23851f..bacc15c250fe 100644
--- a/drivers/media/platform/renesas/rcar-isp/Kconfig
+++ b/drivers/media/platform/renesas/rcar-isp/Kconfig
@@ -5,10 +5,12 @@ config VIDEO_RCAR_ISP
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV && OF
depends on ARCH_RENESAS || COMPILE_TEST
+ depends on VIDEO_RENESAS_VSP1
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select RESET_CONTROLLER
select V4L2_FWNODE
+ select VIDEO_DCT_RPPX1
help
Support for Renesas R-Car Image Signal Processor (ISP).
Enable this to support the Renesas R-Car Image Signal
diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile
index b542118c831e..c0c80303682c 100644
--- a/drivers/media/platform/renesas/rcar-isp/Makefile
+++ b/drivers/media/platform/renesas/rcar-isp/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-rcar-isp-objs = csisp.o
+rcar-isp-objs = csisp.o core.o core-io.o
obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
diff --git a/drivers/media/platform/renesas/rcar-isp/core-io.c b/drivers/media/platform/renesas/rcar-isp/core-io.c
new file mode 100644
index 000000000000..7fce4da41abc
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/core-io.c
@@ -0,0 +1,1017 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
+#include <media/v4l2-mc.h>
+
+#include <linux/media/dreamchip/rppx1-config.h>
+
+#include "risp-core.h"
+
+#define risp_io_err(d, fmt, arg...) dev_err((d)->core->dev, fmt, ##arg)
+
+static struct risp_buffer *risp_io_vb2buf(struct vb2_v4l2_buffer *vb)
+{
+ return container_of(vb, struct risp_buffer, vb);
+}
+
+static int risp_io_open(struct file *file)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ struct rcar_isp_core *core = io->core;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = reset_control_deassert(core->csrstc);
+ if (ret)
+ goto err_csrstc;
+
+ ret = clk_prepare_enable(core->clk);
+ if (ret)
+ goto err_clk;
+
+ ret = mutex_lock_interruptible(&io->lock);
+ if (ret)
+ goto err_pm;
+
+ file->private_data = io;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ goto err_unlock;
+
+ ret = v4l2_pipeline_pm_get(&io->vdev.entity);
+ if (ret < 0)
+ goto err_open;
+
+ mutex_unlock(&io->lock);
+
+ return 0;
+err_open:
+ v4l2_fh_release(file);
+err_unlock:
+ mutex_unlock(&io->lock);
+err_pm:
+ pm_runtime_put(core->dev);
+err_clk:
+ clk_disable_unprepare(core->clk);
+err_csrstc:
+ reset_control_assert(core->csrstc);
+
+ return ret;
+}
+
+static int risp_io_release(struct file *file)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ struct rcar_isp_core *core = io->core;
+ int ret;
+
+ mutex_lock(&io->lock);
+
+ ret = _vb2_fop_release(file, NULL);
+
+ v4l2_pipeline_pm_put(&io->vdev.entity);
+
+ mutex_unlock(&io->lock);
+
+ clk_disable_unprepare(core->clk);
+
+ pm_runtime_put(core->dev);
+
+ reset_control_assert(core->csrstc);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations risp_io_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = risp_io_open,
+ .release = risp_io_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .read = vb2_fop_read,
+};
+
+/* -----------------------------------------------------------------------------
+ * Common queue
+ */
+
+static int risp_io_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+ const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
+
+ if (*nplanes) {
+ if (*nplanes > pix->num_planes)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < pix->num_planes; i++)
+ if (sizes[i] < pix->plane_fmt[i].sizeimage)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *nplanes = pix->num_planes;
+ for (unsigned int i = 0; i < pix->num_planes; i++)
+ sizes[i] = pix->plane_fmt[i].sizeimage;
+ } else {
+ if (*nplanes) {
+ if (sizes[0] < io->format.fmt.meta.buffersize)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *nplanes = 1;
+ sizes[0] = io->format.fmt.meta.buffersize;
+ }
+
+ /* Initialize buffer queue */
+ INIT_LIST_HEAD(&io->buffers);
+
+ return 0;
+};
+
+static int risp_io_buffer_prepare_set(struct rcar_isp_core_io *io,
+ struct vb2_buffer *vb, unsigned int plane,
+ unsigned long size)
+{
+ if (vb2_plane_size(vb, plane) < size) {
+ risp_io_err(io, "Buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, plane), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, plane, size);
+
+ return 0;
+}
+
+static int risp_io_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ int ret = 0;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(vb->vb2_queue->type)) {
+ const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
+
+ for (unsigned int i = 0; i < pix->num_planes; i++) {
+ ret = risp_io_buffer_prepare_set(io, vb, i,
+ pix->plane_fmt[i].sizeimage);
+ if (ret)
+ break;
+ }
+ } else {
+ ret = risp_io_buffer_prepare_set(io, vb, 0,
+ io->format.fmt.meta.buffersize);
+ }
+
+ return ret;
+}
+
+static void risp_io_buffer_queue(struct vb2_buffer *vb)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+
+ guard(mutex)(&io->core->io_lock);
+
+ list_add_tail(&buf->list, &io->buffers);
+
+ if (risp_core_job_prepare(io->core))
+ risp_io_err(io, "Failed to prepare job\n");
+}
+
+static void risp_io_return_buffers(struct rcar_isp_core_io *io,
+ enum vb2_buffer_state state)
+{
+ struct risp_buffer *buf, *node;
+
+ lockdep_assert_held(&io->core->io_lock);
+
+ list_for_each_entry_safe(buf, node, &io->buffers, list) {
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->list);
+ }
+}
+
+static int risp_io_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+ int ret;
+
+ scoped_guard(mutex, &io->core->io_lock) {
+ if (io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width !=
+ io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width ||
+ io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height !=
+ io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height) {
+ risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
+ return -EPIPE;
+ }
+
+ io->streaming = true;
+ }
+
+ ret = risp_core_start_streaming(io->core);
+ if (ret) {
+ guard(mutex)(&io->core->io_lock);
+
+ risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void risp_io_stop_streaming(struct vb2_queue *vq)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+
+ scoped_guard(mutex, &io->core->io_lock) {
+ io->streaming = false;
+ risp_core_stop_streaming(io->core);
+ risp_io_return_buffers(io, VB2_BUF_STATE_ERROR);
+ }
+
+ /*
+ * Wait for buffers part of the jobs not yet processed. Note that this
+ * might complete buffers out of order.
+ */
+ vb2_wait_for_all_buffers(&io->queue);
+}
+
+/* -----------------------------------------------------------------------------
+ * Common V4L2 IOCTLs
+ */
+
+static int risp_io_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Input Exposure
+ */
+
+static int risp_io_input_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+ struct rcar_isp_core *core = io->core;
+ struct device *bus_master;
+ int ret;
+
+ ret = risp_io_queue_setup(vq, nbuffers, nplanes, sizes, alloc_devs);
+ if (ret)
+ return ret;
+
+ bus_master = vsp1_isp_get_bus_master(core->vspx.dev);
+ if (IS_ERR_OR_NULL(bus_master)) {
+ risp_io_err(io, "Missing reference to bus-master device\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Allocate buffers using the bus_master device associated with the
+ * VSPX associated to this ISP instance.
+ */
+ alloc_devs[0] = bus_master;
+
+ return 0;
+};
+
+static const struct vb2_ops risp_io_input_qops = {
+ .queue_setup = risp_io_input_queue_setup,
+ .buf_prepare = risp_io_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_pix_format_mplane risp_io_input_default_format = {
+ .width = 1920,
+ .height = 1080,
+ .field = V4L2_FIELD_NONE,
+ .pixelformat = V4L2_PIX_FMT_SGRBG8,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .num_planes = 1,
+ .plane_fmt = {
+ [0] = {
+ .sizeimage = 1920 * 1080,
+ .bytesperline = 1920,
+ },
+ },
+};
+
+static const struct risp_io_input_format {
+ unsigned int fourcc;
+ unsigned int bpp;
+} risp_io_input_formats[] = {
+ { .fourcc = V4L2_PIX_FMT_SBGGR8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SGBRG8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SGRBG8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SRGGB8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SBGGR10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGBRG10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGRBG10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SRGGB10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SBGGR12, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGBRG12, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGRBG12, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SRGGB12, .bpp = 2 },
+};
+
+static void risp_io_input_try_format(struct rcar_isp_core_io *io,
+ struct v4l2_pix_format_mplane *pix)
+{
+ unsigned int bpp = 0;
+
+ v4l_bound_align_image(&pix->width, 128, 5120, 2,
+ &pix->height, 128, 4096, 2, 0);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
+ if (risp_io_input_formats[i].fourcc == pix->pixelformat) {
+ bpp = risp_io_input_formats[i].bpp;
+ break;
+ }
+ }
+
+ if (!bpp) {
+ pix->pixelformat = risp_io_input_formats[0].fourcc;
+ bpp = risp_io_input_formats[0].bpp;
+ }
+
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_RAW;
+
+ pix->num_planes = 1;
+ pix->plane_fmt[0].bytesperline = pix->width * bpp;
+ pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
+}
+
+static int risp_io_input_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(risp_io_input_formats))
+ return -EINVAL;
+
+ f->pixelformat = risp_io_input_formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int risp_io_input_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ f->fmt.pix_mp = io->format.fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_input_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ risp_io_input_try_format(io, &f->fmt.pix_mp);
+
+ io->format.fmt.pix_mp = f->fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_input_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ risp_io_input_try_format(io, &f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int risp_io_input_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ bool found = false;
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
+ if (risp_io_input_formats[i].fourcc == fsize->pixel_format) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+ fsize->stepwise.min_width = 128;
+ fsize->stepwise.max_width = 5120;
+ fsize->stepwise.step_width = 2;
+
+ fsize->stepwise.min_height = 128;
+ fsize->stepwise.max_height = 4096;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops risp_io_input_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_vid_out = risp_io_input_enum_fmt,
+ .vidioc_g_fmt_vid_out_mplane = risp_io_input_g_fmt,
+ .vidioc_s_fmt_vid_out_mplane = risp_io_input_s_fmt,
+ .vidioc_try_fmt_vid_out_mplane = risp_io_input_try_fmt,
+ .vidioc_enum_framesizes = risp_io_input_enum_framesizes,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Parameters
+ *
+ */
+
+/* Max 2048 address + value pairs in one VSPX buffer, increase if needed. */
+#define RISP_IO_PARAMS_BUF_SIZE 16384
+
+static int risp_io_params_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct rcar_isp_core *core = io->core;
+ size_t size;
+ int ret;
+
+ memset(&buf->vsp_buffer, 0, sizeof(buf->vsp_buffer));
+
+ size = RISP_IO_PARAMS_BUF_SIZE;
+ ret = vsp1_isp_alloc_buffer(core->vspx.dev, size, &buf->vsp_buffer);
+ if (ret)
+ return -EINVAL;
+
+ memset(buf->vsp_buffer.cpu_addr, 0, RISP_IO_PARAMS_BUF_SIZE);
+
+ return 0;
+}
+
+static void risp_io_params_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct rcar_isp_core *core = io->core;
+
+ vsp1_isp_free_buffer(core->vspx.dev, &buf->vsp_buffer);
+}
+
+struct risp_conf_dma_write_desc {
+ u32 *buf;
+ u32 base;
+ unsigned int count;
+};
+
+static int risp_conf_dma_prepare(void *priv, u32 offset, u32 value)
+{
+ struct risp_conf_dma_write_desc *desc = priv;
+
+ /* Bounds check, 8 bytes = address (4)+ value (4). */
+ if ((desc->count + 1) * 8 > RISP_IO_PARAMS_BUF_SIZE)
+ return -ENOMEM;
+
+ (*desc->buf++) = desc->base | offset;
+ (*desc->buf++) = value;
+
+ desc->count++;
+
+ return 0;
+}
+
+static int risp_io_params_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+ struct risp_conf_dma_write_desc desc;
+ u32 *cpu_addr;
+ int ret;
+
+ /* Prepare params. */
+ cpu_addr = (u32 *)buf->vsp_buffer.cpu_addr;
+
+ desc.buf = cpu_addr + 2;
+ desc.base = io->core->rppaddr;
+ desc.count = 0;
+
+ /* Fill params body. */
+ ret = rppx1_params(io->core->rpp, vb, io->format.fmt.meta.buffersize,
+ risp_conf_dma_prepare, &desc);
+ if (ret)
+ return ret;
+
+ /* Fill params header. */
+ cpu_addr[0] = desc.count;
+ cpu_addr[1] = 0x0;
+
+ return 0;
+}
+
+static const struct vb2_ops risp_io_params_qops = {
+ .queue_setup = risp_io_queue_setup,
+ .buf_init = risp_io_params_buf_init,
+ .buf_cleanup = risp_io_params_buf_cleanup,
+ .buf_prepare = risp_io_params_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_meta_format risp_io_params_default_format = {
+ .dataformat = V4L2_META_FMT_RPP_X1_PARAMS,
+ .buffersize = v4l2_isp_buffer_size(RPPX1_PARAMS_MAX_SIZE),
+};
+
+static int risp_io_params_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_META_OUTPUT || f->index)
+ return -EINVAL;
+
+ f->pixelformat = io->format.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static int risp_io_params_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != V4L2_BUF_TYPE_META_OUTPUT)
+ return -EINVAL;
+
+ *meta = io->format.fmt.meta;
+
+ return 0;
+}
+
+static int risp_io_params_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ return risp_io_params_g_fmt(file, priv, f);
+}
+
+static const struct v4l2_ioctl_ops risp_io_params_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_meta_out = risp_io_params_enum_fmt,
+ .vidioc_g_fmt_meta_out = risp_io_params_g_fmt,
+ .vidioc_s_fmt_meta_out = risp_io_params_s_fmt,
+ .vidioc_try_fmt_meta_out = risp_io_params_g_fmt,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Statistics
+ */
+
+static const struct vb2_ops risp_io_stats_qops = {
+ .queue_setup = risp_io_queue_setup,
+ .buf_prepare = risp_io_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_meta_format risp_io_stats_default_format = {
+ .dataformat = V4L2_META_FMT_RPP_X1_STATS,
+ .buffersize = v4l2_isp_buffer_size(RPPX1_STATS_MAX_SIZE),
+};
+
+static int risp_io_stats_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_META_CAPTURE || f->index)
+ return -EINVAL;
+
+ f->pixelformat = io->format.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static int risp_io_stats_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != V4L2_BUF_TYPE_META_CAPTURE)
+ return -EINVAL;
+
+ *meta = io->format.fmt.meta;
+
+ return 0;
+}
+
+static int risp_io_stats_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ return risp_io_stats_g_fmt(file, priv, f);
+}
+
+static const struct v4l2_ioctl_ops risp_io_stats_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_meta_cap = risp_io_stats_enum_fmt,
+ .vidioc_g_fmt_meta_cap = risp_io_stats_g_fmt,
+ .vidioc_s_fmt_meta_cap = risp_io_stats_s_fmt,
+ .vidioc_try_fmt_meta_cap = risp_io_stats_g_fmt,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Video capture
+ */
+
+static const struct vb2_ops risp_io_capture_qops = {
+ .queue_setup = risp_io_queue_setup,
+ .buf_prepare = risp_io_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_pix_format_mplane risp_io_capture_default_format = {
+ .width = 1920,
+ .height = 1080,
+ .pixelformat = V4L2_PIX_FMT_XBGR32,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_DEFAULT,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .num_planes = 1,
+ .plane_fmt = {
+ [0] = {
+ .bytesperline = ALIGN(1920 * 4, 256),
+ .sizeimage = ALIGN(1920 * 4, 256) * 1080,
+ },
+ },
+};
+
+static void risp_io_capture_try_format(struct rcar_isp_core_io *io,
+ struct v4l2_pix_format_mplane *pix)
+{
+ v4l_bound_align_image(&pix->width, 128, 5120, 2,
+ &pix->height, 128, 4096, 2, 0);
+
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV16M:
+ pix->num_planes = 2;
+ pix->plane_fmt[0].bytesperline = ALIGN(pix->width, 256);
+ pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
+ pix->plane_fmt[1].bytesperline = ALIGN(pix->width, 256);
+ pix->plane_fmt[1].sizeimage = pix->plane_fmt[1].bytesperline * pix->height;
+ break;
+ default:
+ pix->pixelformat = V4L2_PIX_FMT_XBGR32;
+ pix->num_planes = 1;
+ pix->plane_fmt[0].bytesperline = ALIGN(pix->width * 4, 256);
+ pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
+ break;
+ }
+}
+
+static int risp_io_capture_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (f->index) {
+ case 0:
+ f->pixelformat = V4L2_PIX_FMT_NV16M;
+ break;
+ case 1:
+ f->pixelformat = V4L2_PIX_FMT_XBGR32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int risp_io_capture_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ f->fmt.pix_mp = io->format.fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_capture_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ risp_io_capture_try_format(io, &f->fmt.pix_mp);
+
+ io->format.fmt.pix_mp = f->fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_capture_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ risp_io_capture_try_format(io, &f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int risp_io_capture_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ switch (fsize->pixel_format) {
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_XBGR32:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+ fsize->stepwise.min_width = 128;
+ fsize->stepwise.max_width = 5120;
+ fsize->stepwise.step_width = 2;
+
+ fsize->stepwise.min_height = 128;
+ fsize->stepwise.max_height = 4096;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops risp_io_capture_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_vid_cap = risp_io_capture_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = risp_io_capture_g_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = risp_io_capture_s_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = risp_io_capture_try_fmt,
+ .vidioc_enum_framesizes = risp_io_capture_enum_framesizes,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Create and remove IO video devices
+ */
+
+int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
+ struct rcar_isp_core_io *io, unsigned int pad)
+{
+ struct video_device *vdev = &io->vdev;
+ struct vb2_queue *q = &io->queue;
+ int ret;
+
+ switch (pad) {
+ case RISP_CORE_INPUT1:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s input1",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_TX;
+ vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ vdev->ioctl_ops = &risp_io_input_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->ops = &risp_io_input_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SOURCE;
+ io->format.fmt.pix_mp = risp_io_input_default_format;
+ break;
+
+ case RISP_CORE_PARAMS:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s params",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_TX;
+ vdev->device_caps = V4L2_CAP_META_OUTPUT;
+ vdev->ioctl_ops = &risp_io_params_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ q->ops = &risp_io_params_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SOURCE;
+ io->format.fmt.meta = risp_io_params_default_format;
+ break;
+
+ case RISP_CORE_STATS:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s stats",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE;
+ vdev->ioctl_ops = &risp_io_stats_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ q->ops = &risp_io_stats_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SINK;
+ io->format.fmt.meta = risp_io_stats_default_format;
+ break;
+
+ case RISP_CORE_OUTPUT1:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s output1",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ vdev->ioctl_ops = &risp_io_capture_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->ops = &risp_io_capture_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SINK;
+ io->format.fmt.pix_mp = risp_io_capture_default_format;
+ break;
+ }
+
+ io->core = core;
+
+ mutex_init(&io->lock);
+ INIT_LIST_HEAD(&io->buffers);
+
+ /* Create media graph pad. */
+ ret = media_entity_pads_init(&io->vdev.entity, 1, &io->pad);
+ if (ret)
+ return ret;
+
+ /* Create queue */
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->lock = &io->lock;
+ q->drv_priv = io;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct risp_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->dev = dev;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ risp_io_err(io, "Failed to initialize VB2 queue\n");
+ return ret;
+ }
+
+ /* Create video device */
+ vdev->v4l2_dev = &core->v4l2_dev;
+ vdev->queue = &io->queue;
+
+ vdev->release = video_device_release_empty;
+ vdev->lock = &io->lock;
+ vdev->fops = &risp_io_fops;
+
+ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ risp_io_err(io, "Failed to register video device\n");
+ return ret;
+ }
+
+ video_set_drvdata(&io->vdev, io);
+
+ v4l2_info(&core->v4l2_dev, "Device registered as %s\n",
+ video_device_node_name(vdev));
+
+ switch (pad) {
+ case RISP_CORE_INPUT1:
+ case RISP_CORE_PARAMS:
+ ret = media_create_pad_link(&io->vdev.entity, 0,
+ &core->subdev.entity, pad,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ break;
+ case RISP_CORE_STATS:
+ case RISP_CORE_OUTPUT1:
+ ret = media_create_pad_link(&core->subdev.entity, pad,
+ &io->vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ break;
+ }
+
+ return ret;
+}
+
+void risp_core_io_destory(struct rcar_isp_core_io *io)
+{
+ if (!video_is_registered(&io->vdev))
+ return;
+
+ video_unregister_device(&io->vdev);
+}
diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c
new file mode 100644
index 000000000000..20dd0fb3dc71
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/core.c
@@ -0,0 +1,826 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/vsp1.h>
+
+#include "risp-core.h"
+
+#define ISP_CS_STREAMER_MODE_REG 0x7000
+#define ISP_CS_STREAMER_MODE_STREAMER_EN 0xf
+
+#define ISP_CS_STREAMER_VBLANK_REG 0x7004
+#define ISP_CS_STREAMER_HBLANK_REG 0x7008
+
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG 0x7100
+#define ISP_CS_STREAMER_CONFIG_DMA_REG_ADDRESS_UPPER_8BIT_MASK GENMASK(31, 24)
+#define ISP_CS_STREAMER_CONFIG_DMA_ENABLE0 BIT(0)
+
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG 0x2100
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 BIT(31)
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_CONFIG_DATA_START_REG_ADDRESS_MASK GENMASK(15, 0)
+
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG 0x2104
+
+#define ISP_CORE_ISPCORE_INT_STATUS 0x80000
+#define ISP_CORE_ISPCORE_INT_ENABLE 0x80004
+#define ISPCORE_DMA_IMAGE_FRAME_MODE(i, f) (0x84000 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(i, f) (0x84004 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(i, f) (0x84008 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(i, f) (0x8400c + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(i, f) (0x84010 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(i, f) (0x84014 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP2(i, f) (0x84018 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP3(i, f) (0x8401c + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(i, f) (0x84020 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(i, f) (0x84024 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP2(i, f) (0x84028 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP3(i, f) (0x8402c + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_AXI_ID(i, f) (0x84030 + 0x1000 * (i) + 0x100 * (f))
+
+#define ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(i) (0x84400 + 0x1000 * (i))
+#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_MASK GENMASK(31, 16)
+#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT 16
+
+#define ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(i) (0x84800 + 0x1000 * (i))
+
+static void risp_cs_write(struct rcar_isp_core *core, u32 offset, u32 value)
+{
+ iowrite32(value, core->csbase + offset);
+}
+
+static u32 risp_cs_read(struct rcar_isp_core *core, u32 offset)
+{
+ return ioread32(core->csbase + offset);
+}
+
+static void risp_core_write(struct rcar_isp_core *core, u32 offset, u32 value)
+{
+ iowrite32(value, core->base + offset);
+}
+
+static u32 risp_core_read(struct rcar_isp_core *core, u32 offset)
+{
+ return ioread32(core->base + offset);
+}
+
+static void risp_core_job_run_params(struct rcar_isp_core *core,
+ struct vsp1_isp_job_desc *vspx_job,
+ struct risp_buffer *buf)
+{
+ u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
+ bool have_config = !!params_buf[0];
+ u32 ctrl0, ctrl1, ctrl2;
+
+ /*
+ * If we have a configuration but not asked the VSPX to programme it,
+ * use MMIO to write the configuration. This might be needed to work
+ * around limitations of the VSPX ConfigDMA.
+ */
+ if (have_config && !vspx_job->config.pairs) {
+ for (unsigned int i = 0; i < params_buf[0]; i++)
+ risp_core_write(core, params_buf[2 + i * 2] & 0xffff,
+ params_buf[3 + i * 2]);
+
+ /* Disable ConfigDMA. */
+ have_config = false;
+ }
+
+ ctrl0 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG) &
+ ~ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
+ ctrl1 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG) &
+ ~(ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 | 0xffff);
+ ctrl2 = 0;
+
+ if (have_config) {
+ ctrl0 |= ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
+ ctrl1 |= ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 |
+ (params_buf[2] & 0xffff);
+ ctrl2 = params_buf[3];
+ }
+
+ risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG, ctrl0);
+ risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG, ctrl1);
+ risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG, ctrl2);
+}
+
+static void risp_core_job_run_output(struct rcar_isp_core *core,
+ struct risp_buffer *buf)
+{
+ const struct v4l2_format *fmt = &core->io[RISP_CORE_OUTPUT1].format;
+ dma_addr_t mem;
+ u32 reg;
+
+ for (unsigned int frame = 0; frame < 4; frame++) {
+ reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(0, frame);
+ mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ risp_core_write(core, reg, mem);
+
+ /* Only NV16 uses 2 planes. */
+ if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV16M)
+ continue;
+
+ reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(0, frame);
+ mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
+ risp_core_write(core, reg, mem);
+ }
+}
+
+static void risp_core_job_run(struct rcar_isp_core *core)
+{
+ struct rcar_isp_job *job;
+
+ lockdep_assert_held(&core->lock);
+
+ /* ISP not yet started, nothing to do. */
+ if (!core->streaming)
+ return;
+
+ /* If we have active buffers in the ISP core, nothing to do. */
+ if (core->vspx.job)
+ return;
+
+ job = list_first_entry_or_null(&core->risp_jobs,
+ struct rcar_isp_job,
+ job_queue);
+ if (!job)
+ return;
+
+ list_del(&job->job_queue);
+
+ core->vspx.job = job;
+
+ /* Program the ISP register before kicking the VSPX. */
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf = job->buffers[i];
+
+ switch (i) {
+ case RISP_CORE_PARAMS:
+ risp_core_job_run_params(core, &job->vspx_job, buf);
+ break;
+ case RISP_CORE_OUTPUT1:
+ risp_core_job_run_output(core, buf);
+ break;
+ }
+ }
+
+ if (vsp1_isp_job_run(core->vspx.dev, &job->vspx_job)) {
+ /*
+ * Release all buffers in this job if running on the VSPX
+ * failed. Userspace should recover from this, no new jobs are
+ * scheduled.
+ */
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf = job->buffers[i];
+
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
+ core->vspx.job = NULL;
+ kfree(job);
+
+ dev_err(core->dev, "Failed to run job");
+ }
+}
+
+static int risp_core_pixfmt_to_vspx(u32 pixfmt)
+{
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ return V4L2_PIX_FMT_GREY;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ return V4L2_PIX_FMT_Y10;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ return V4L2_PIX_FMT_Y12;
+ default:
+ return -EINVAL;
+ }
+}
+
+int risp_core_job_prepare(struct rcar_isp_core *core)
+{
+ struct vsp1_isp_job_desc *vspx_job;
+ int vspx_pixfmt = -EINVAL;
+ struct rcar_isp_job *job;
+ int ret;
+
+ lockdep_assert_held(&core->io_lock);
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ if (list_empty(&core->io[i].buffers))
+ return 0;
+ }
+
+ /* Memory is released when the job is consumed. */
+ job = kzalloc(sizeof(*job), GFP_KERNEL);
+ if (!job)
+ return -ENOMEM;
+
+ vspx_job = &job->vspx_job;
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf;
+
+ /*
+ * Extract buffer from the IO queue and save a reference in
+ * the job description. Buffers will be completed when the
+ * corresponding frame will be completed by the ISP.
+ */
+ buf = list_first_entry_or_null(&core->io[i].buffers,
+ struct risp_buffer, list);
+ if (!buf) {
+ ret = -EINVAL;
+ goto error_return_buffers;
+ }
+
+ switch (i) {
+ case RISP_CORE_INPUT1: {
+ u32 isp_pixfmt = core->io[i].format.fmt.pix_mp.pixelformat;
+
+ vspx_pixfmt = risp_core_pixfmt_to_vspx(isp_pixfmt);
+
+ vspx_job->img.fmt = core->io[i].format.fmt.pix_mp;
+ vspx_job->img.fmt.pixelformat = vspx_pixfmt;
+ vspx_job->img.mem =
+ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf,
+ 0);
+ break;
+ }
+ case RISP_CORE_PARAMS: {
+ /*
+ * Work around undocumented behavior of the ConfigDMA
+ * interface by using MMIO if 16 or less pairs are to
+ * be programmed.
+ *
+ * Programing 15 or less pairs corrupts the image data
+ * following the config buffer, programing exactly 16
+ * pairs freeze the whole VSPX.
+ */
+ u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
+
+ if (params_buf[0] <= 16) {
+ vspx_job->config.pairs = 0;
+ } else {
+ vspx_job->config.pairs = params_buf[0];
+ vspx_job->config.mem = buf->vsp_buffer.dma_addr;
+ }
+ break;
+ }
+ }
+
+ list_del(&buf->list);
+ job->buffers[i] = buf;
+ }
+
+ if (vspx_pixfmt < 0) {
+ ret = -EINVAL;
+ goto error_return_buffers;
+ }
+
+ ret = vsp1_isp_job_prepare(core->vspx.dev, vspx_job);
+ if (ret)
+ goto error_return_buffers;
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ list_add_tail(&job->job_queue, &core->risp_jobs);
+ risp_core_job_run(core);
+ }
+
+ return 0;
+
+error_return_buffers:
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ if (!job->buffers[i])
+ continue;
+
+ vb2_buffer_done(&job->buffers[i]->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+ kfree(job);
+ return ret;
+}
+
+static int risp_core_config_output(struct rcar_isp_core *core,
+ unsigned int index,
+ const struct v4l2_pix_format_mplane *pix)
+{
+ /* For all frame capture slots. */
+ for (unsigned int frame = 0; frame < 4; frame++) {
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV16M:
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
+ 1);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
+ 0 << 24 | 0 << 16 | 4 << 8 | 16 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
+ 0 << 24 | 0 << 16 | 7 << 8 | 7 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
+ 0 << 28 | 0 << 24 |
+ 0 << 20 | 0 << 16 |
+ 3 << 12 | 0 << 8 |
+ 3 << 4 | 0 << 0);
+
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
+ pix->plane_fmt[0].bytesperline);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(index, frame),
+ pix->plane_fmt[0].bytesperline);
+ break;
+ case V4L2_PIX_FMT_XBGR32:
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
+ 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
+ 0 << 24 | 0 << 16 | 0 << 8 | 0 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
+ 0 << 24 | 0 << 16 | 0 << 8 | 23 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
+ 0 << 28 | 0 << 24 |
+ 0 << 20 | 0 << 16 |
+ 0 << 12 | 0 << 8 |
+ 3 << 4 | 2 << 0);
+
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
+ pix->plane_fmt[0].bytesperline);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_AXI_ID(index, frame),
+ 0);
+ }
+
+ /* Set image out flush EOF. */
+ risp_core_write(core, ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(index),
+ pix->plane_fmt[0].bytesperline <<
+ ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT);
+
+ /* Enable DMA and set burst length. */
+ risp_core_write(core, ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(index),
+ BIT(31) | 7);
+
+ return 0;
+}
+
+static u32 risp_core_pix2bus(const struct rcar_isp_core_io *io)
+{
+ switch (io->format.fmt.pix_mp.pixelformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ return MEDIA_BUS_FMT_SBGGR8_1X8;
+ case V4L2_PIX_FMT_SGBRG8:
+ return MEDIA_BUS_FMT_SGBRG8_1X8;
+ case V4L2_PIX_FMT_SGRBG8:
+ return MEDIA_BUS_FMT_SGRBG8_1X8;
+ case V4L2_PIX_FMT_SRGGB8:
+ return MEDIA_BUS_FMT_SRGGB8_1X8;
+ case V4L2_PIX_FMT_SBGGR10:
+ return MEDIA_BUS_FMT_SBGGR10_1X10;
+ case V4L2_PIX_FMT_SGBRG10:
+ return MEDIA_BUS_FMT_SGBRG10_1X10;
+ case V4L2_PIX_FMT_SGRBG10:
+ return MEDIA_BUS_FMT_SGRBG10_1X10;
+ case V4L2_PIX_FMT_SRGGB10:
+ return MEDIA_BUS_FMT_SRGGB10_1X10;
+ case V4L2_PIX_FMT_SBGGR12:
+ return MEDIA_BUS_FMT_SBGGR12_1X12;
+ case V4L2_PIX_FMT_SGBRG12:
+ return MEDIA_BUS_FMT_SGBRG12_1X12;
+ case V4L2_PIX_FMT_SGRBG12:
+ return MEDIA_BUS_FMT_SGRBG12_1X12;
+ case V4L2_PIX_FMT_SRGGB12:
+ return MEDIA_BUS_FMT_SRGGB12_1X12;
+ case V4L2_PIX_FMT_XBGR32:
+ return MEDIA_BUS_FMT_RGB888_1X24;
+ case V4L2_PIX_FMT_NV16M:
+ return MEDIA_BUS_FMT_YUYV12_1X24;
+ default:
+ return 0;
+ }
+}
+
+static void risp_core_try_next_job(struct rcar_isp_core *core)
+{
+ lockdep_assert_held(&core->lock);
+
+ struct rcar_isp_job *job = core->vspx.job;
+
+ /* If the ISP or the VSPX is not done with the job, wait. */
+ if (!job || !job->done_isp || !job->done_vspx)
+ return;
+
+ core->vspx.job = NULL;
+ kfree(job);
+
+ core->sequence++;
+
+ /* Kickoff processing of next frame (if any). */
+ risp_core_job_run(core);
+}
+
+static void risp_core_svspx_frame_end(void *data)
+{
+ struct rcar_isp_core *core = data;
+
+ guard(spinlock_irqsave)(&core->lock);
+
+ /*
+ * In tear-down the ISP may report a frame end event but we have already
+ * freed the job. It is safe to ignore the end of frame event.
+ */
+ if (!core->vspx.job)
+ return;
+
+ core->vspx.job->done_vspx = true;
+ risp_core_try_next_job(core);
+}
+
+int risp_core_start_streaming(struct rcar_isp_core *core)
+{
+ struct vsp1_vspx_frame_end vspx_fe = {
+ .vspx_frame_end = risp_core_svspx_frame_end,
+ .frame_end_data = core,
+ };
+
+ struct v4l2_mbus_framefmt inputfmt = {
+ .width = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width,
+ .height = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height,
+ .code = risp_core_pix2bus(&core->io[RISP_CORE_INPUT1]),
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+ };
+
+ struct v4l2_mbus_framefmt hvout = {
+ .width = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width,
+ .height = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height,
+ .code = risp_core_pix2bus(&core->io[RISP_CORE_OUTPUT1]),
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+ };
+
+ scoped_guard(mutex, &core->io_lock) {
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ if (!core->io[i].streaming)
+ return 0;
+ }
+
+ /*
+ * The state core->streaming is protected by core->lock, which
+ * is not held yet. It is however safe to read it here since
+ * core->io_lock is held both in risp_core_stop_streaming() and
+ * here, the only two places the variable is modified.
+ *
+ * With this small implied dependency on the two locks for write
+ * access, the interrupt handler can safely depend sole on the
+ * spinlock core->lock for read access to core->streaming.
+ *
+ * The gain is an interrupt handler which can hold the spinlock
+ * and a start/stop procedure which can reset the ISP using the
+ * reset_control_reset() API, The later which can not be called
+ * from a context that may sleep.
+ *
+ * All other locations core->streaming is read and _all_
+ * locations where it is written core->lock is held.
+ */
+ if (core->streaming)
+ return 0;
+
+ /* Reset and wait for ISP core to initialize itself. */
+ reset_control_reset(core->rstc);
+ usleep_range(2000, 4000);
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ int ret;
+
+ risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 1);
+
+ /* Configure output DMA */
+ risp_core_config_output(core, 0,
+ &core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp);
+
+ risp_cs_write(core, ISP_CS_STREAMER_VBLANK_REG, inputfmt.width * 25);
+ risp_cs_write(core, ISP_CS_STREAMER_HBLANK_REG, 64);
+
+ /* Enable ISP Streaming bridge. */
+ risp_cs_write(core, ISP_CS_STREAMER_MODE_REG,
+ ISP_CS_STREAMER_MODE_STREAMER_EN);
+
+ /* Start RPP ISP */
+ ret = rppx1_start(core->rpp, &inputfmt, &hvout, NULL);
+ if (ret)
+ return ret;
+
+ core->vspx.job = NULL;
+ core->sequence = 0;
+ core->streaming = true;
+ }
+
+ /* Start VSPX */
+ vsp1_isp_start_streaming(core->vspx.dev, &vspx_fe);
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ risp_core_job_run(core);
+ }
+ }
+
+ return 0;
+}
+
+void risp_core_stop_streaming(struct rcar_isp_core *core)
+{
+ struct rcar_isp_job *job, *tmp;
+
+ /*
+ * This function releases buffers and jobs: make sure the queues mutex
+ * is held.
+ */
+ lockdep_assert_held(&core->io_lock);
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ /* Stop is called by each vdev, only act on the first call. */
+ if (!core->streaming)
+ return;
+
+ /* Stop queueing jobs to VSPX. */
+ core->streaming = false;
+ }
+
+ /* Wait for active VSPX job to finish. */
+ for (unsigned int retry = 0; retry <= 10; retry++) {
+ if (!core->vspx.job)
+ break;
+
+ usleep_range(2000, 4000);
+ }
+
+ if (core->vspx.job)
+ dev_err(core->dev, "Failed to complete running job");
+
+ /* Free all buffers and switch of the hardware. */
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ /* Free all jobs and buffers. */
+ list_for_each_entry_safe(job, tmp, &core->risp_jobs, job_queue) {
+ vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf = job->buffers[i];
+
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ list_del(&job->job_queue);
+ kfree(job);
+ }
+
+ rppx1_stop(core->rpp);
+ risp_cs_write(core, ISP_CS_STREAMER_MODE_REG, 0);
+ risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 0);
+ }
+
+ vsp1_isp_stop_streaming(core->vspx.dev);
+}
+
+static irqreturn_t risp_core_irq(int irq, void *data)
+{
+ struct rcar_isp_core *core = data;
+ struct rcar_isp_job *job;
+ u32 status;
+
+ status = risp_core_read(core, ISP_CORE_ISPCORE_INT_STATUS);
+ if (!(status & BIT(0)))
+ return IRQ_NONE;
+
+ if (!rppx1_interrupt(core->rpp, &status))
+ return IRQ_HANDLED;
+
+ guard(spinlock_irqsave)(&core->lock);
+
+ job = core->vspx.job;
+ if (!job)
+ return IRQ_HANDLED;
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf;
+
+ buf = job->buffers[i];
+
+ switch (i) {
+ case RISP_CORE_STATS:
+ rppx1_stats_fill_isr(core->rpp, status,
+ vb2_plane_vaddr(&buf->vb.vb2_buf, 0));
+ fallthrough;
+ case RISP_CORE_OUTPUT1:
+ case RISP_CORE_INPUT1:
+ buf->vb.sequence = core->sequence;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ fallthrough;
+ case RISP_CORE_PARAMS:
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+
+ core->vspx.job->done_isp = true;
+ risp_core_try_next_job(core);
+
+ return IRQ_HANDLED;
+}
+
+static const struct v4l2_subdev_ops risp_core_subdev_ops = {
+};
+
+static int risp_core_create_subdev(struct rcar_isp_core *core)
+{
+ struct v4l2_subdev *subdev = &core->subdev;
+ int ret;
+
+ subdev->owner = THIS_MODULE;
+ subdev->dev = core->dev;
+ v4l2_subdev_init(subdev, &risp_core_subdev_ops);
+ v4l2_set_subdevdata(subdev, core->dev);
+ snprintf(subdev->name, sizeof(subdev->name), "%s %s core",
+ KBUILD_MODNAME, dev_name(core->dev));
+ subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ subdev->entity.function = MEDIA_ENT_F_VID_MUX;
+
+ core->pads[RISP_CORE_INPUT1].flags = MEDIA_PAD_FL_SINK;
+ core->pads[RISP_CORE_PARAMS].flags = MEDIA_PAD_FL_SINK;
+ core->pads[RISP_CORE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[RISP_CORE_OUTPUT1].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&subdev->entity, RISP_CORE_NUM_PADS,
+ core->pads);
+ if (ret)
+ return ret;
+
+ dev_info(core->dev, "Registered ISP core\n");
+
+ return 0;
+}
+
+int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd)
+{
+ int ret;
+
+ core->v4l2_dev.mdev = sd->v4l2_dev->mdev;
+
+ /* Register ISP Core subdevice. */
+ ret = v4l2_device_register_subdev(&core->v4l2_dev, &core->subdev);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ ret = risp_core_io_create(core->dev, core, &core->io[i], i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int risp_core_probe_resources(struct rcar_isp_core *core,
+ struct platform_device *pdev)
+{
+ struct platform_device *vspx;
+ struct device_node *of_vspx;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ if (!res)
+ return -ENODEV;
+
+ core->rppaddr = res->start;
+ core->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(core->base))
+ return PTR_ERR(core->base);
+
+ ret = platform_get_irq_byname(pdev, "core");
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, ret, risp_core_irq, IRQF_SHARED,
+ KBUILD_MODNAME, core);
+ if (ret)
+ return ret;
+
+ core->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(core->clk))
+ return -ENODEV;
+
+ core->rstc = devm_reset_control_get(&pdev->dev, "core");
+ if (IS_ERR(core->rstc))
+ return -ENODEV;
+
+ of_vspx = of_parse_phandle(pdev->dev.of_node, "renesas,vspx", 0);
+ if (!of_vspx)
+ return -ENODEV;
+
+ vspx = of_find_device_by_node(of_vspx);
+ if (!vspx)
+ return -ENODEV;
+
+ /* Attach to VSP-X */
+ core->vspx.dev = &vspx->dev;
+
+ ret = vsp1_isp_init(&vspx->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Attach ro the RPP library
+ *
+ * 1. Start and wait for the ISP to startup.
+ * 2. Attach the RPP library and talk with the RPP ISP.
+ * 3. Turn off ISP.
+ * 4. Fail if the RPP is unhappy with the hardware.
+ */
+ ret = clk_prepare_enable(core->clk);
+ if (ret)
+ return ret;
+
+ usleep_range(2000, 4000);
+
+ core->rpp = rppx1_create(core->base, &pdev->dev);
+
+ clk_disable_unprepare(core->clk);
+
+ if (!core->rpp)
+ return -ENODEV;
+
+ return 0;
+}
+
+int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
+ void __iomem *csbase, struct reset_control *csrstc)
+{
+ int ret;
+
+ core->dev = &pdev->dev;
+ core->csrstc = csrstc;
+ core->csbase = csbase;
+
+ ret = risp_core_probe_resources(core, pdev);
+ if (ret) {
+ core->base = NULL;
+ return ret;
+ }
+
+ ret = v4l2_device_register(core->dev, &core->v4l2_dev);
+ if (ret)
+ return ret;
+
+ ret = risp_core_create_subdev(core);
+ if (ret)
+ return ret;
+
+ mutex_init(&core->io_lock);
+ spin_lock_init(&core->lock);
+ INIT_LIST_HEAD(&core->risp_jobs);
+
+ return 0;
+}
+
+void risp_core_remove(struct rcar_isp_core *core)
+{
+ /* If we did not probe the ISP core, nothing to do. */
+ if (!core->base)
+ return;
+
+ dev_info(core->dev, "Remove ISP Core\n");
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++)
+ risp_core_io_destory(&core->io[i]);
+
+ mutex_destroy(&core->io_lock);
+ rppx1_destroy(core->rpp);
+}
diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
index 8fb2cc3b5650..53ce47020d17 100644
--- a/drivers/media/platform/renesas/rcar-isp/csisp.c
+++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
@@ -11,14 +11,13 @@
*/
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
-#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/reset.h>
#include <media/mipi-csi2.h>
-#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+
+#include "risp-core.h"
#define ISPINPUTSEL0_REG 0x0008
#define ISPINPUTSEL0_SEL_CSI0 BIT(31)
@@ -158,6 +157,7 @@ struct rcar_isp {
struct device *dev;
void __iomem *csbase;
struct reset_control *rstc;
+ struct rcar_isp_core core;
enum rcar_isp_input csi_input;
@@ -451,6 +451,21 @@ static int risp_parse_dt(struct rcar_isp *isp)
return ret;
}
+/* -----------------------------------------------------------------------------
+ * ISP Core connection
+ */
+
+static int risp_cs_registered(struct v4l2_subdev *sd)
+{
+ struct rcar_isp *isp = sd_to_isp(sd);
+
+ return risp_core_registered(&isp->core, sd);
+}
+
+static const struct v4l2_subdev_internal_ops risp_cs_internal_ops = {
+ .registered = risp_cs_registered,
+};
+
/* -----------------------------------------------------------------------------
* Platform Device Driver
*/
@@ -477,7 +492,7 @@ static int risp_probe_resources(struct rcar_isp *isp,
if (IS_ERR(isp->csbase))
return PTR_ERR(isp->csbase);
- isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
+ isp->rstc = devm_reset_control_get_shared(&pdev->dev, NULL);
return PTR_ERR_OR_ZERO(isp->rstc);
}
@@ -541,14 +556,31 @@ static int risp_probe(struct platform_device *pdev)
if (ret)
goto error_notifier;
- ret = v4l2_async_register_subdev(&isp->subdev);
- if (ret < 0)
+ ret = risp_core_probe(&isp->core, pdev, isp->csbase, isp->rstc);
+ switch (ret) {
+ case 0:
+ /* The device have an ISP core. */
+ isp->subdev.internal_ops = &risp_cs_internal_ops;
+ break;
+ case -ENODEV:
+ /* The device don't have an ISP core, that is OK. */
+ ret = 0;
+ break;
+ default:
+ /* Something went wrong registering the ISP core. */
goto error_subdev;
+ }
+
+ ret = v4l2_async_register_subdev(&isp->subdev);
+ if (ret < 0)
+ goto error_core;
dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
return 0;
+error_core:
+ risp_core_remove(&isp->core);
error_subdev:
v4l2_subdev_cleanup(&isp->subdev);
error_notifier:
@@ -564,6 +596,8 @@ static void risp_remove(struct platform_device *pdev)
{
struct rcar_isp *isp = platform_get_drvdata(pdev);
+ risp_core_remove(&isp->core);
+
v4l2_async_nf_unregister(&isp->notifier);
v4l2_async_nf_cleanup(&isp->notifier);
diff --git a/drivers/media/platform/renesas/rcar-isp/risp-core.h b/drivers/media/platform/renesas/rcar-isp/risp-core.h
new file mode 100644
index 000000000000..4c848551c1ef
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/risp-core.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __RCAR_ISP__
+#define __RCAR_ISP__
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <media/rppx1.h>
+#include <media/vsp1.h>
+
+struct rcar_isp_core;
+
+enum risp_core_pads {
+ RISP_CORE_INPUT1,
+ RISP_CORE_PARAMS,
+ RISP_CORE_STATS,
+ RISP_CORE_OUTPUT1,
+ RISP_CORE_NUM_PADS,
+};
+
+/**
+ * struct risp_buffer - Describe an IO buffer
+ * @vb: The VB2 buffer
+ * @list: List of buffers queued to the IO queue
+ * @vsp_buffer: Buffer mapped from VSP-X, only used for params IO
+ */
+struct risp_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ struct vsp1_isp_buffer_desc vsp_buffer;
+};
+
+/**
+ * struct rcar_isp_core_io - Information for a IO video devices
+ * @core: Backlink to the common ISP core structure
+ *
+ * @lock: Protects @vdev, @pad and @queue + open/close fops
+ * @vdev: V4L2 video device associated with this IO port
+ * @pad: Media pad for @vdev
+ * @queue: VB2 buffers queue for $@vdev
+ *
+ * @qlock: Protects @streaming and @buffers
+ * @streaming: Flag to indicate if device is streaming, or not
+ * @buffers: List of buffers queued to the device
+ *
+ * @format: The active V4L2 format
+ */
+struct rcar_isp_core_io {
+ struct rcar_isp_core *core;
+
+ struct mutex lock; /* See KDoc block. */
+ struct video_device vdev;
+ struct media_pad pad;
+ struct vb2_queue queue;
+
+ bool streaming;
+ struct list_head buffers;
+
+ struct v4l2_format format;
+};
+
+/**
+ * struct rcar_isp_job - R-Car ISP job description
+ *
+ * Both done_isp and done_vspx shall be set before the job can be considered
+ * completely done.
+ *
+ * @buffers: IO buffers that form a job
+ * @vspx_job: VSPX job description
+ * @job_queue: list handle
+ * @done_isp: Flag to indicate the ISP is done with the job
+ * @done_vspx: Flag to indicate the VSPX is done with the job
+ */
+struct rcar_isp_job {
+ struct risp_buffer *buffers[RISP_CORE_NUM_PADS];
+ struct vsp1_isp_job_desc vspx_job;
+ struct list_head job_queue;
+ bool done_isp;
+ bool done_vspx;
+};
+
+/**
+ * struct rcar_isp_vspx - R-Car ISP job description
+ *
+ * @dev: Device reference to VSPX
+ * @job: Job currently being processed by VSPX
+ */
+struct rcar_isp_vspx {
+ struct device *dev;
+ struct rcar_isp_job *job;
+};
+
+/**
+ * struct rcar_isp_core - ISP Core
+ * @dev: (OF) device
+ * @rppaddr: Hardware address of the RPP ISP (from OF)
+ * @clk: The clock for the ISP CORE
+ * @rstc: The reset for the ISP Core
+ * @csrstc: The reset for the ISP Channel Selector
+ *
+ * @base: MMIO base of the ISP CORE
+ * @csbase: MMIO base of the ISP CS
+ *
+ * @subdev: V4L2 subdevice to represent the ISP CORE
+ * @pads: Media pad for @subdev
+ *
+ * @v4l2_dev: V4L2 device
+ * @rpp: Handle to the RPP ISP connected to the ISP CORE
+ *
+ * @io_lock: Protect io[*].streaming and io[*].buffers
+ * @io: Array of IO ports to the ISP CORE
+ *
+ * @lock: Protects @vspx, @risp_jobs, @sequence and @streaming
+ * @vspx: Handle to the resources used by VSPX connected to the ISP CORE
+ * @risp_jobs: Queue of VSPX transfer jobs
+ * @sequence: V4L2 buffers sequence number
+ * @streaming: Tracks if the device is streaming
+ */
+struct rcar_isp_core {
+ struct device *dev;
+
+ u32 rppaddr;
+ struct clk *clk;
+
+ struct reset_control *rstc;
+ struct reset_control *csrstc;
+
+ void __iomem *base;
+ void __iomem *csbase;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RISP_CORE_NUM_PADS];
+
+ struct v4l2_device v4l2_dev;
+ struct rppx1 *rpp;
+
+ struct mutex io_lock; /* See KDoc block. */
+ struct rcar_isp_core_io io[RISP_CORE_NUM_PADS];
+
+ spinlock_t lock; /* See KDoc block. */
+ struct rcar_isp_vspx vspx;
+ struct list_head risp_jobs;
+ unsigned int sequence;
+ bool streaming;
+};
+
+int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
+ void __iomem *csbase, struct reset_control *csrstc);
+void risp_core_remove(struct rcar_isp_core *core);
+int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd);
+
+int risp_core_job_prepare(struct rcar_isp_core *core);
+
+int risp_core_start_streaming(struct rcar_isp_core *core);
+void risp_core_stop_streaming(struct rcar_isp_core *core);
+
+int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
+ struct rcar_isp_core_io *io, unsigned int pad);
+void risp_core_io_destory(struct rcar_isp_core_io *io);
+
+#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 03/14] media: rcar-isp: Add support for ISPCORE
2026-05-04 1:05 ` [v8 03/14] media: rcar-isp: Add support for ISPCORE Niklas Söderlund
@ 2026-05-06 15:02 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 15:02 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Niklas
On Mon, May 04, 2026 at 03:05:45AM +0200, Niklas Söderlund wrote:
> The Renesas R-Car ISP block consists of two different IP blocks, one
> CSI-2 Channel Selector (CSISP) and one traditional ISP for image
> operation (ISPCORE). The R-Car ISP driver currently supports the CSISP
> functionality as part of the video capture pipeline, this change adds
> support for the ISPCORE functionality.
>
> The ISPCORE functionality is further split in two parts, a Renesas
> specific part and a Dream Chip Real-time Pixel Processor IP part
> (RPPX1). The Renesas part deals with I/O to/from the block while the
> RPPX1 part deals with the actual ISP functions.
>
> The RPPX1 functionality is implemented in a support framework (DCT
> RPPX1) as this block can be used by different vendors or setups. This
> change deals with the Renesas part of exposing the V4L2 elements needed
> for a user-space interface to the RPPX1 and deals with the DMA to/from
> the RPP block. It also facilitates the user-space V4L2 API to allow
> configuring the RPPX1 using the DCT RPPX1 support framework.
>
> The functionality exposed are one input video device where RAW bayer
> frames can be queued for processing, one output video device where the
> debayerd image can be read as either ABGR32 or NV16M format. Further
> more a video device to queue the image processing parameters to
> configure the RPPX1 IPS as well as a video device to read statistics
> about the processed image is available.
>
> There is no change in the operation of the CSISP functionality.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> ---
> * Changes since v7
> - Fix kdoc tag.
> - Update to init ISP device pointer.
>
> * Changes since v5
> - Use NULL instead of 0 when init rcar_isp_core in probe.
>
> * Changes since v4
> - Add depend on VIDEO_RENESAS_VSP1
>
> * Changes since v3
> - Make sure VSPX is stopped before queueing next job, starting with
> v6.18-rc1 the two can get out of sync.
> - Fix a possible race with VSPX when stopping streaming.
>
> * Changes since v2
> - Rework the start procedure so the ISP is reset without from a context
> that can sleep.
> ---
> .../media/platform/renesas/rcar-isp/Kconfig | 2 +
> .../media/platform/renesas/rcar-isp/Makefile | 2 +-
> .../media/platform/renesas/rcar-isp/core-io.c | 1017 +++++++++++++++++
> .../media/platform/renesas/rcar-isp/core.c | 826 +++++++++++++
> .../media/platform/renesas/rcar-isp/csisp.c | 48 +-
> .../platform/renesas/rcar-isp/risp-core.h | 170 +++
> 6 files changed, 2057 insertions(+), 8 deletions(-)
> create mode 100644 drivers/media/platform/renesas/rcar-isp/core-io.c
> create mode 100644 drivers/media/platform/renesas/rcar-isp/core.c
> create mode 100644 drivers/media/platform/renesas/rcar-isp/risp-core.h
>
> diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig
> index 242f6a23851f..bacc15c250fe 100644
> --- a/drivers/media/platform/renesas/rcar-isp/Kconfig
> +++ b/drivers/media/platform/renesas/rcar-isp/Kconfig
> @@ -5,10 +5,12 @@ config VIDEO_RCAR_ISP
> depends on V4L_PLATFORM_DRIVERS
> depends on VIDEO_DEV && OF
> depends on ARCH_RENESAS || COMPILE_TEST
> + depends on VIDEO_RENESAS_VSP1
> select MEDIA_CONTROLLER
> select VIDEO_V4L2_SUBDEV_API
> select RESET_CONTROLLER
> select V4L2_FWNODE
> + select VIDEO_DCT_RPPX1
> help
> Support for Renesas R-Car Image Signal Processor (ISP).
> Enable this to support the Renesas R-Car Image Signal
> diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile
> index b542118c831e..c0c80303682c 100644
> --- a/drivers/media/platform/renesas/rcar-isp/Makefile
> +++ b/drivers/media/platform/renesas/rcar-isp/Makefile
> @@ -1,4 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0
> -rcar-isp-objs = csisp.o
> +rcar-isp-objs = csisp.o core.o core-io.o
>
> obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
> diff --git a/drivers/media/platform/renesas/rcar-isp/core-io.c b/drivers/media/platform/renesas/rcar-isp/core-io.c
> new file mode 100644
> index 000000000000..7fce4da41abc
> --- /dev/null
> +++ b/drivers/media/platform/renesas/rcar-isp/core-io.c
> @@ -0,0 +1,1017 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/pm_runtime.h>
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-isp.h>
> +#include <media/v4l2-mc.h>
> +
> +#include <linux/media/dreamchip/rppx1-config.h>
> +
> +#include "risp-core.h"
> +
> +#define risp_io_err(d, fmt, arg...) dev_err((d)->core->dev, fmt, ##arg)
> +
> +static struct risp_buffer *risp_io_vb2buf(struct vb2_v4l2_buffer *vb)
> +{
> + return container_of(vb, struct risp_buffer, vb);
> +}
> +
> +static int risp_io_open(struct file *file)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> + struct rcar_isp_core *core = io->core;
> + int ret;
> +
> + ret = pm_runtime_resume_and_get(core->dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = reset_control_deassert(core->csrstc);
> + if (ret)
> + goto err_csrstc;
> +
> + ret = clk_prepare_enable(core->clk);
> + if (ret)
> + goto err_clk;
How does this work ?
This handler is registered for all video devices. Everytime a video
device is opened, we go through this routine, right ?
First question first: do we need pm_runtime here ? The 'csisp' driver
which provides 'core->dev' registers a platform driver, but has not
pm_runtime handlers. What is pm_runtime_resume_and_get() supposed to
call here ?
Then, do we need to power-up at open() time or at start streaming
time ?
Wouldn't it be better to define a policy like "when all video devices
have started streaming then let's power up ?". I see you have a
similar check implemented in risp_core_start_streaming() already.
Unless I missed something obvious you're here going through the reset
deassertion and clock prepare for every open() on all video device ?
> +
> + ret = mutex_lock_interruptible(&io->lock);
Why interruptible, and you need to lock at all ? File operations (on
the same file) should be serialized already, aren't they ?
> + if (ret)
> + goto err_pm;
> +
> + file->private_data = io;
> +
> + ret = v4l2_fh_open(file);
> + if (ret)
> + goto err_unlock;
> +
> + ret = v4l2_pipeline_pm_get(&io->vdev.entity);
> + if (ret < 0)
> + goto err_open;
> +
> + mutex_unlock(&io->lock);
> +
> + return 0;
> +err_open:
> + v4l2_fh_release(file);
> +err_unlock:
> + mutex_unlock(&io->lock);
> +err_pm:
> + pm_runtime_put(core->dev);
> +err_clk:
> + clk_disable_unprepare(core->clk);
> +err_csrstc:
> + reset_control_assert(core->csrstc);
> +
> + return ret;
> +}
> +
> +static int risp_io_release(struct file *file)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> + struct rcar_isp_core *core = io->core;
> + int ret;
> +
> + mutex_lock(&io->lock);
> +
> + ret = _vb2_fop_release(file, NULL);
> +
> + v4l2_pipeline_pm_put(&io->vdev.entity);
> +
> + mutex_unlock(&io->lock);
> +
> + clk_disable_unprepare(core->clk);
> +
> + pm_runtime_put(core->dev);
> +
> + reset_control_assert(core->csrstc);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_file_operations risp_io_fops = {
> + .owner = THIS_MODULE,
> + .unlocked_ioctl = video_ioctl2,
> + .open = risp_io_open,
> + .release = risp_io_release,
> + .poll = vb2_fop_poll,
> + .mmap = vb2_fop_mmap,
> + .read = vb2_fop_read,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Common queue
> + */
> +
> +static int risp_io_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> + unsigned int *nplanes, unsigned int sizes[],
> + struct device *alloc_devs[])
> +
> +{
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> +
> + if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
> + const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
> +
> + if (*nplanes) {
> + if (*nplanes > pix->num_planes)
> + return -EINVAL;
> +
> + for (unsigned int i = 0; i < pix->num_planes; i++)
> + if (sizes[i] < pix->plane_fmt[i].sizeimage)
> + return -EINVAL;
> +
> + return 0;
> + }
> +
> + *nplanes = pix->num_planes;
> + for (unsigned int i = 0; i < pix->num_planes; i++)
> + sizes[i] = pix->plane_fmt[i].sizeimage;
> + } else {
> + if (*nplanes) {
> + if (sizes[0] < io->format.fmt.meta.buffersize)
> + return -EINVAL;
> +
> + return 0;
> + }
> +
> + *nplanes = 1;
> + sizes[0] = io->format.fmt.meta.buffersize;
> + }
> +
> + /* Initialize buffer queue */
> + INIT_LIST_HEAD(&io->buffers);
> +
> + return 0;
> +};
> +
> +static int risp_io_buffer_prepare_set(struct rcar_isp_core_io *io,
> + struct vb2_buffer *vb, unsigned int plane,
> + unsigned long size)
> +{
> + if (vb2_plane_size(vb, plane) < size) {
> + risp_io_err(io, "Buffer too small (%lu < %lu)\n",
> + vb2_plane_size(vb, plane), size);
> + return -EINVAL;
> + }
> +
> + vb2_set_plane_payload(vb, plane, size);
> +
> + return 0;
> +}
> +
> +static int risp_io_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> + int ret = 0;
> +
> + if (V4L2_TYPE_IS_MULTIPLANAR(vb->vb2_queue->type)) {
> + const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
> +
> + for (unsigned int i = 0; i < pix->num_planes; i++) {
> + ret = risp_io_buffer_prepare_set(io, vb, i,
> + pix->plane_fmt[i].sizeimage);
> + if (ret)
> + break;
> + }
return 0;
}
> + } else {
> + ret = risp_io_buffer_prepare_set(io, vb, 0,
> + io->format.fmt.meta.buffersize);
> + }
> +
> + return ret;
return risp_io_buffer_prepare_set(io, vb, 0,
io->format.fmt.meta.buffersize);
}
> +}
> +
> +static void risp_io_buffer_queue(struct vb2_buffer *vb)
> +{
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> +
> + guard(mutex)(&io->core->io_lock);
> +
> + list_add_tail(&buf->list, &io->buffers);
> +
> + if (risp_core_job_prepare(io->core))
> + risp_io_err(io, "Failed to prepare job\n");
> +}
> +
> +static void risp_io_return_buffers(struct rcar_isp_core_io *io,
> + enum vb2_buffer_state state)
> +{
> + struct risp_buffer *buf, *node;
> +
> + lockdep_assert_held(&io->core->io_lock);
> +
> + list_for_each_entry_safe(buf, node, &io->buffers, list) {
> + vb2_buffer_done(&buf->vb.vb2_buf, state);
> + list_del(&buf->list);
> + }
> +}
> +
> +static int risp_io_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> + int ret;
> +
> + scoped_guard(mutex, &io->core->io_lock) {
> + if (io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width !=
> + io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width ||
> + io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height !=
> + io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height) {
I understand you can't use link_validate(), but shouldn't this be done
by the core.c file instead of performing this for every video device,
maybe in the risp_core_start_streaming() function after we have
validated all video device have started streaming ?
> + risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
> + return -EPIPE;
> + }
> +
> + io->streaming = true;
> + }
> +
> + ret = risp_core_start_streaming(io->core);
> + if (ret) {
> + guard(mutex)(&io->core->io_lock);
> +
> + risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void risp_io_stop_streaming(struct vb2_queue *vq)
> +{
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> +
> + scoped_guard(mutex, &io->core->io_lock) {
> + io->streaming = false;
> + risp_core_stop_streaming(io->core);
> + risp_io_return_buffers(io, VB2_BUF_STATE_ERROR);
> + }
> +
> + /*
> + * Wait for buffers part of the jobs not yet processed. Note that this
> + * might complete buffers out of order.
> + */
> + vb2_wait_for_all_buffers(&io->queue);
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Common V4L2 IOCTLs
> + */
> +
> +static int risp_io_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct video_device *vdev = video_devdata(file);
> +
> + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> + strscpy(cap->card, vdev->name, sizeof(cap->card));
> +
> + return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Input Exposure
Exposure ?
> + */
> +
> +static int risp_io_input_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> + unsigned int *nplanes, unsigned int sizes[],
> + struct device *alloc_devs[])
> +
> +{
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> + struct rcar_isp_core *core = io->core;
> + struct device *bus_master;
> + int ret;
> +
> + ret = risp_io_queue_setup(vq, nbuffers, nplanes, sizes, alloc_devs);
> + if (ret)
> + return ret;
> +
> + bus_master = vsp1_isp_get_bus_master(core->vspx.dev);
> + if (IS_ERR_OR_NULL(bus_master)) {
> + risp_io_err(io, "Missing reference to bus-master device\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * Allocate buffers using the bus_master device associated with the
> + * VSPX associated to this ISP instance.
> + */
> + alloc_devs[0] = bus_master;
> +
> + return 0;
> +};
> +
> +static const struct vb2_ops risp_io_input_qops = {
> + .queue_setup = risp_io_input_queue_setup,
> + .buf_prepare = risp_io_buffer_prepare,
> + .buf_queue = risp_io_buffer_queue,
> + .start_streaming = risp_io_start_streaming,
> + .stop_streaming = risp_io_stop_streaming,
> +};
> +
> +static const struct v4l2_pix_format_mplane risp_io_input_default_format = {
> + .width = 1920,
> + .height = 1080,
> + .field = V4L2_FIELD_NONE,
> + .pixelformat = V4L2_PIX_FMT_SGRBG8,
> + .colorspace = V4L2_COLORSPACE_RAW,
.xfer_func = V4L2_XFER_FUNC_NONE,
.ycbcr_enc = V4L2_YCBCR_ENC_601,
.quantization = V4L2_QUANTIZATION_FULL_RANGE,
> + .num_planes = 1,
> + .plane_fmt = {
> + [0] = {
> + .sizeimage = 1920 * 1080,
> + .bytesperline = 1920,
> + },
> + },
> +};
> +
> +static const struct risp_io_input_format {
> + unsigned int fourcc;
> + unsigned int bpp;
> +} risp_io_input_formats[] = {
> + { .fourcc = V4L2_PIX_FMT_SBGGR8, .bpp = 1 },
> + { .fourcc = V4L2_PIX_FMT_SGBRG8, .bpp = 1 },
> + { .fourcc = V4L2_PIX_FMT_SGRBG8, .bpp = 1 },
> + { .fourcc = V4L2_PIX_FMT_SRGGB8, .bpp = 1 },
> + { .fourcc = V4L2_PIX_FMT_SBGGR10, .bpp = 2 },
> + { .fourcc = V4L2_PIX_FMT_SGBRG10, .bpp = 2 },
> + { .fourcc = V4L2_PIX_FMT_SGRBG10, .bpp = 2 },
> + { .fourcc = V4L2_PIX_FMT_SRGGB10, .bpp = 2 },
> + { .fourcc = V4L2_PIX_FMT_SBGGR12, .bpp = 2 },
> + { .fourcc = V4L2_PIX_FMT_SGBRG12, .bpp = 2 },
> + { .fourcc = V4L2_PIX_FMT_SGRBG12, .bpp = 2 },
> + { .fourcc = V4L2_PIX_FMT_SRGGB12, .bpp = 2 },
> +};
> +
> +static void risp_io_input_try_format(struct rcar_isp_core_io *io,
> + struct v4l2_pix_format_mplane *pix)
> +{
> + unsigned int bpp = 0;
> +
> + v4l_bound_align_image(&pix->width, 128, 5120, 2,
> + &pix->height, 128, 4096, 2, 0);
> +
> + for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
> + if (risp_io_input_formats[i].fourcc == pix->pixelformat) {
> + bpp = risp_io_input_formats[i].bpp;
> + break;
> + }
> + }
> +
> + if (!bpp) {
> + pix->pixelformat = risp_io_input_formats[0].fourcc;
> + bpp = risp_io_input_formats[0].bpp;
> + }
> +
> + pix->field = V4L2_FIELD_NONE;
> + pix->colorspace = V4L2_COLORSPACE_RAW;
> +
> + pix->num_planes = 1;
> + pix->plane_fmt[0].bytesperline = pix->width * bpp;
> + pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
> +}
> +
> +static int risp_io_input_enum_fmt(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + if (f->index >= ARRAY_SIZE(risp_io_input_formats))
> + return -EINVAL;
> +
> + f->pixelformat = risp_io_input_formats[f->index].fourcc;
> +
> + return 0;
> +}
> +
> +static int risp_io_input_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + return -EINVAL;
> +
> + f->fmt.pix_mp = io->format.fmt.pix_mp;
> +
> + return 0;
> +}
> +
> +static int risp_io_input_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (vb2_is_busy(&io->queue))
> + return -EBUSY;
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + return -EINVAL;
> +
> + risp_io_input_try_format(io, &f->fmt.pix_mp);
> +
> + io->format.fmt.pix_mp = f->fmt.pix_mp;
> +
> + return 0;
> +}
> +
> +static int risp_io_input_try_fmt(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + risp_io_input_try_format(io, &f->fmt.pix_mp);
> +
> + return 0;
> +}
> +
> +static int risp_io_input_enum_framesizes(struct file *file, void *fh,
> + struct v4l2_frmsizeenum *fsize)
> +{
> + bool found = false;
> +
> + if (fsize->index != 0)
> + return -EINVAL;
> +
> + for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
> + if (risp_io_input_formats[i].fourcc == fsize->pixel_format) {
> + found = true;
> + break;
> + }
> + }
> +
> + if (!found)
> + return -EINVAL;
> +
> + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
> +
> + fsize->stepwise.min_width = 128;
> + fsize->stepwise.max_width = 5120;
> + fsize->stepwise.step_width = 2;
> +
> + fsize->stepwise.min_height = 128;
> + fsize->stepwise.max_height = 4096;
> + fsize->stepwise.step_height = 2;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops risp_io_input_ioctl_ops = {
> + .vidioc_querycap = risp_io_querycap,
> +
> + .vidioc_enum_fmt_vid_out = risp_io_input_enum_fmt,
> + .vidioc_g_fmt_vid_out_mplane = risp_io_input_g_fmt,
> + .vidioc_s_fmt_vid_out_mplane = risp_io_input_s_fmt,
> + .vidioc_try_fmt_vid_out_mplane = risp_io_input_try_fmt,
> + .vidioc_enum_framesizes = risp_io_input_enum_framesizes,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Parameters
> + *
> + */
> +
> +/* Max 2048 address + value pairs in one VSPX buffer, increase if needed. */
> +#define RISP_IO_PARAMS_BUF_SIZE 16384
> +
> +static int risp_io_params_buf_init(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> + struct rcar_isp_core *core = io->core;
> + size_t size;
> + int ret;
> +
> + memset(&buf->vsp_buffer, 0, sizeof(buf->vsp_buffer));
> +
> + size = RISP_IO_PARAMS_BUF_SIZE;
> + ret = vsp1_isp_alloc_buffer(core->vspx.dev, size, &buf->vsp_buffer);
> + if (ret)
> + return -EINVAL;
> +
> + memset(buf->vsp_buffer.cpu_addr, 0, RISP_IO_PARAMS_BUF_SIZE);
> +
> + return 0;
> +}
> +
> +static void risp_io_params_buf_cleanup(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> + struct rcar_isp_core *core = io->core;
> +
> + vsp1_isp_free_buffer(core->vspx.dev, &buf->vsp_buffer);
> +}
> +
> +struct risp_conf_dma_write_desc {
> + u32 *buf;
> + u32 base;
> + unsigned int count;
> +};
> +
> +static int risp_conf_dma_prepare(void *priv, u32 offset, u32 value)
> +{
> + struct risp_conf_dma_write_desc *desc = priv;
> +
> + /* Bounds check, 8 bytes = address (4)+ value (4). */
> + if ((desc->count + 1) * 8 > RISP_IO_PARAMS_BUF_SIZE)
> + return -ENOMEM;
> +
> + (*desc->buf++) = desc->base | offset;
> + (*desc->buf++) = value;
> +
> + desc->count++;
> +
> + return 0;
> +}
> +
> +static int risp_io_params_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> + struct risp_conf_dma_write_desc desc;
> + u32 *cpu_addr;
> + int ret;
> +
> + /* Prepare params. */
> + cpu_addr = (u32 *)buf->vsp_buffer.cpu_addr;
> +
> + desc.buf = cpu_addr + 2;
> + desc.base = io->core->rppaddr;
> + desc.count = 0;
> +
> + /* Fill params body. */
> + ret = rppx1_params(io->core->rpp, vb, io->format.fmt.meta.buffersize,
> + risp_conf_dma_prepare, &desc);
> + if (ret)
> + return ret;
> +
> + /* Fill params header. */
> + cpu_addr[0] = desc.count;
> + cpu_addr[1] = 0x0;
> +
> + return 0;
> +}
> +
> +static const struct vb2_ops risp_io_params_qops = {
> + .queue_setup = risp_io_queue_setup,
> + .buf_init = risp_io_params_buf_init,
> + .buf_cleanup = risp_io_params_buf_cleanup,
> + .buf_prepare = risp_io_params_buffer_prepare,
> + .buf_queue = risp_io_buffer_queue,
> + .start_streaming = risp_io_start_streaming,
> + .stop_streaming = risp_io_stop_streaming,
> +};
> +
> +static const struct v4l2_meta_format risp_io_params_default_format = {
> + .dataformat = V4L2_META_FMT_RPP_X1_PARAMS,
> + .buffersize = v4l2_isp_buffer_size(RPPX1_PARAMS_MAX_SIZE),
> +};
> +
> +static int risp_io_params_enum_fmt(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (f->type != V4L2_BUF_TYPE_META_OUTPUT || f->index)
> + return -EINVAL;
> +
> + f->pixelformat = io->format.fmt.meta.dataformat;
> +
> + return 0;
> +}
> +
> +static int risp_io_params_g_fmt(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> + struct v4l2_meta_format *meta = &f->fmt.meta;
> +
> + if (f->type != V4L2_BUF_TYPE_META_OUTPUT)
> + return -EINVAL;
> +
> + *meta = io->format.fmt.meta;
> +
> + return 0;
> +}
> +
> +static int risp_io_params_s_fmt(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (vb2_is_busy(&io->queue))
> + return -EBUSY;
> +
> + return risp_io_params_g_fmt(file, priv, f);
> +}
> +
> +static const struct v4l2_ioctl_ops risp_io_params_ioctl_ops = {
> + .vidioc_querycap = risp_io_querycap,
> +
> + .vidioc_enum_fmt_meta_out = risp_io_params_enum_fmt,
> + .vidioc_g_fmt_meta_out = risp_io_params_g_fmt,
> + .vidioc_s_fmt_meta_out = risp_io_params_s_fmt,
> + .vidioc_try_fmt_meta_out = risp_io_params_g_fmt,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Statistics
> + */
> +
> +static const struct vb2_ops risp_io_stats_qops = {
> + .queue_setup = risp_io_queue_setup,
> + .buf_prepare = risp_io_buffer_prepare,
> + .buf_queue = risp_io_buffer_queue,
> + .start_streaming = risp_io_start_streaming,
> + .stop_streaming = risp_io_stop_streaming,
> +};
> +
> +static const struct v4l2_meta_format risp_io_stats_default_format = {
> + .dataformat = V4L2_META_FMT_RPP_X1_STATS,
> + .buffersize = v4l2_isp_buffer_size(RPPX1_STATS_MAX_SIZE),
> +};
> +
> +static int risp_io_stats_enum_fmt(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (f->type != V4L2_BUF_TYPE_META_CAPTURE || f->index)
> + return -EINVAL;
> +
> + f->pixelformat = io->format.fmt.meta.dataformat;
> +
> + return 0;
> +}
> +
> +static int risp_io_stats_g_fmt(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> + struct v4l2_meta_format *meta = &f->fmt.meta;
> +
> + if (f->type != V4L2_BUF_TYPE_META_CAPTURE)
> + return -EINVAL;
> +
> + *meta = io->format.fmt.meta;
> +
> + return 0;
> +}
> +
> +static int risp_io_stats_s_fmt(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (vb2_is_busy(&io->queue))
> + return -EBUSY;
> +
> + return risp_io_stats_g_fmt(file, priv, f);
> +}
> +
> +static const struct v4l2_ioctl_ops risp_io_stats_ioctl_ops = {
> + .vidioc_querycap = risp_io_querycap,
> +
> + .vidioc_enum_fmt_meta_cap = risp_io_stats_enum_fmt,
> + .vidioc_g_fmt_meta_cap = risp_io_stats_g_fmt,
> + .vidioc_s_fmt_meta_cap = risp_io_stats_s_fmt,
> + .vidioc_try_fmt_meta_cap = risp_io_stats_g_fmt,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Video capture
> + */
> +
> +static const struct vb2_ops risp_io_capture_qops = {
> + .queue_setup = risp_io_queue_setup,
> + .buf_prepare = risp_io_buffer_prepare,
> + .buf_queue = risp_io_buffer_queue,
> + .start_streaming = risp_io_start_streaming,
> + .stop_streaming = risp_io_stop_streaming,
> +};
> +
> +static const struct v4l2_pix_format_mplane risp_io_capture_default_format = {
> + .width = 1920,
> + .height = 1080,
> + .pixelformat = V4L2_PIX_FMT_XBGR32,
> + .field = V4L2_FIELD_NONE,
> + .colorspace = V4L2_COLORSPACE_DEFAULT,
Isn't this V4L2_COLORSPACE_SRGB ?
> + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
There shouldn't be an ycbcr encoding, I'm surprised there's no
V4L2_YCBCR_ENC_NONE..
> + .quantization = V4L2_QUANTIZATION_DEFAULT,
.quantization = V4L2_QUANTIZATION_FULL_RANGE,
.xfer_func = V4L2_XFER_FUNC_SRGB,
> + .num_planes = 1,
> + .plane_fmt = {
> + [0] = {
> + .bytesperline = ALIGN(1920 * 4, 256),
isn't 1920*4 is already aligned to 256 ?
> + .sizeimage = ALIGN(1920 * 4, 256) * 1080,
> + },
> + },
> +};
> +
> +static void risp_io_capture_try_format(struct rcar_isp_core_io *io,
> + struct v4l2_pix_format_mplane *pix)
> +{
> + v4l_bound_align_image(&pix->width, 128, 5120, 2,
> + &pix->height, 128, 4096, 2, 0);
> +
> + pix->field = V4L2_FIELD_NONE;
> + pix->colorspace = V4L2_COLORSPACE_DEFAULT;
> + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
should probably be adjusted to the match the output format ?
> + pix->quantization = V4L2_QUANTIZATION_DEFAULT;
> +
> + switch (pix->pixelformat) {
> + case V4L2_PIX_FMT_NV16M:
> + pix->num_planes = 2;
> + pix->plane_fmt[0].bytesperline = ALIGN(pix->width, 256);
> + pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
> + pix->plane_fmt[1].bytesperline = ALIGN(pix->width, 256);
> + pix->plane_fmt[1].sizeimage = pix->plane_fmt[1].bytesperline * pix->height;
> + break;
> + default:
> + pix->pixelformat = V4L2_PIX_FMT_XBGR32;
> + pix->num_planes = 1;
> + pix->plane_fmt[0].bytesperline = ALIGN(pix->width * 4, 256);
> + pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
> + break;
> + }
> +}
> +
> +static int risp_io_capture_enum_fmt(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
> +
> + switch (f->index) {
> + case 0:
> + f->pixelformat = V4L2_PIX_FMT_NV16M;
> + break;
> + case 1:
> + f->pixelformat = V4L2_PIX_FMT_XBGR32;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int risp_io_capture_g_fmt(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
> +
> + f->fmt.pix_mp = io->format.fmt.pix_mp;
> +
> + return 0;
> +}
> +
> +static int risp_io_capture_s_fmt(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + if (vb2_is_busy(&io->queue))
> + return -EBUSY;
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
> +
> + risp_io_capture_try_format(io, &f->fmt.pix_mp);
> +
> + io->format.fmt.pix_mp = f->fmt.pix_mp;
> +
> + return 0;
> +}
> +
> +static int risp_io_capture_try_fmt(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct rcar_isp_core_io *io = video_drvdata(file);
> +
> + risp_io_capture_try_format(io, &f->fmt.pix_mp);
> +
> + return 0;
> +}
> +
> +static int risp_io_capture_enum_framesizes(struct file *file, void *fh,
> + struct v4l2_frmsizeenum *fsize)
> +{
> + if (fsize->index != 0)
> + return -EINVAL;
> +
> + switch (fsize->pixel_format) {
> + case V4L2_PIX_FMT_NV16M:
> + case V4L2_PIX_FMT_XBGR32:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
> +
> + fsize->stepwise.min_width = 128;
> + fsize->stepwise.max_width = 5120;
> + fsize->stepwise.step_width = 2;
> +
> + fsize->stepwise.min_height = 128;
> + fsize->stepwise.max_height = 4096;
> + fsize->stepwise.step_height = 2;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops risp_io_capture_ioctl_ops = {
> + .vidioc_querycap = risp_io_querycap,
> +
> + .vidioc_enum_fmt_vid_cap = risp_io_capture_enum_fmt,
> + .vidioc_g_fmt_vid_cap_mplane = risp_io_capture_g_fmt,
> + .vidioc_s_fmt_vid_cap_mplane = risp_io_capture_s_fmt,
> + .vidioc_try_fmt_vid_cap_mplane = risp_io_capture_try_fmt,
> + .vidioc_enum_framesizes = risp_io_capture_enum_framesizes,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Create and remove IO video devices
> + */
> +
> +int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
> + struct rcar_isp_core_io *io, unsigned int pad)
> +{
> + struct video_device *vdev = &io->vdev;
> + struct vb2_queue *q = &io->queue;
> + int ret;
> +
> + switch (pad) {
> + case RISP_CORE_INPUT1:
> + snprintf(vdev->name, sizeof(vdev->name), "%s %s input1",
> + KBUILD_MODNAME, dev_name(dev));
> + vdev->vfl_dir = VFL_DIR_TX;
> + vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE;
> + vdev->ioctl_ops = &risp_io_input_ioctl_ops;
> +
> + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> + q->ops = &risp_io_input_qops;
> +
> + io->pad.flags = MEDIA_PAD_FL_SOURCE;
> + io->format.fmt.pix_mp = risp_io_input_default_format;
> + break;
> +
> + case RISP_CORE_PARAMS:
> + snprintf(vdev->name, sizeof(vdev->name), "%s %s params",
> + KBUILD_MODNAME, dev_name(dev));
> + vdev->vfl_dir = VFL_DIR_TX;
> + vdev->device_caps = V4L2_CAP_META_OUTPUT;
> + vdev->ioctl_ops = &risp_io_params_ioctl_ops;
> +
> + q->type = V4L2_BUF_TYPE_META_OUTPUT;
> + q->ops = &risp_io_params_qops;
> +
> + io->pad.flags = MEDIA_PAD_FL_SOURCE;
> + io->format.fmt.meta = risp_io_params_default_format;
> + break;
> +
> + case RISP_CORE_STATS:
> + snprintf(vdev->name, sizeof(vdev->name), "%s %s stats",
> + KBUILD_MODNAME, dev_name(dev));
> + vdev->vfl_dir = VFL_DIR_RX;
> + vdev->device_caps = V4L2_CAP_META_CAPTURE;
> + vdev->ioctl_ops = &risp_io_stats_ioctl_ops;
> +
> + q->type = V4L2_BUF_TYPE_META_CAPTURE;
> + q->ops = &risp_io_stats_qops;
> +
> + io->pad.flags = MEDIA_PAD_FL_SINK;
> + io->format.fmt.meta = risp_io_stats_default_format;
> + break;
> +
> + case RISP_CORE_OUTPUT1:
> + snprintf(vdev->name, sizeof(vdev->name), "%s %s output1",
> + KBUILD_MODNAME, dev_name(dev));
> + vdev->vfl_dir = VFL_DIR_RX;
> + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE;
> + vdev->ioctl_ops = &risp_io_capture_ioctl_ops;
> +
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> + q->ops = &risp_io_capture_qops;
> +
> + io->pad.flags = MEDIA_PAD_FL_SINK;
> + io->format.fmt.pix_mp = risp_io_capture_default_format;
> + break;
> + }
> +
> + io->core = core;
> +
> + mutex_init(&io->lock);
> + INIT_LIST_HEAD(&io->buffers);
> +
> + /* Create media graph pad. */
> + ret = media_entity_pads_init(&io->vdev.entity, 1, &io->pad);
> + if (ret)
> + return ret;
> +
> + /* Create queue */
> + q->io_modes = VB2_MMAP | VB2_DMABUF;
> + q->lock = &io->lock;
> + q->drv_priv = io;
> + q->mem_ops = &vb2_dma_contig_memops;
> + q->buf_struct_size = sizeof(struct risp_buffer);
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + q->dev = dev;
> +
> + ret = vb2_queue_init(q);
> + if (ret < 0) {
> + risp_io_err(io, "Failed to initialize VB2 queue\n");
> + return ret;
> + }
> +
> + /* Create video device */
> + vdev->v4l2_dev = &core->v4l2_dev;
> + vdev->queue = &io->queue;
> +
> + vdev->release = video_device_release_empty;
> + vdev->lock = &io->lock;
> + vdev->fops = &risp_io_fops;
> +
> + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
> +
> + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
Have you tried compiling the driver as module and perform a
load/unload/load ?
I'm afraid it might cause issues as this function is called in the
CS's internal_ops.registered() path, while the video device is
unregistered in risp_core_io_destory() which is in driver's remove()
call path.
Should you add an handler for the CS's .unregister call ?
I'm asking as I'm going through a similar issue with Mali, I have one
patch that address this and I wonder if this driver is affected by the
same problem.
> + if (ret) {
> + risp_io_err(io, "Failed to register video device\n");
> + return ret;
> + }
> +
> + video_set_drvdata(&io->vdev, io);
> +
> + v4l2_info(&core->v4l2_dev, "Device registered as %s\n",
Why v4l2_info() and not simply dev_dbg() ?
> + video_device_node_name(vdev));
> +
> + switch (pad) {
> + case RISP_CORE_INPUT1:
> + case RISP_CORE_PARAMS:
> + ret = media_create_pad_link(&io->vdev.entity, 0,
> + &core->subdev.entity, pad,
> + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
> + break;
> + case RISP_CORE_STATS:
> + case RISP_CORE_OUTPUT1:
> + ret = media_create_pad_link(&core->subdev.entity, pad,
> + &io->vdev.entity, 0,
> + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +void risp_core_io_destory(struct rcar_isp_core_io *io)
> +{
> + if (!video_is_registered(&io->vdev))
> + return;
> +
> + video_unregister_device(&io->vdev);
> +}
> diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c
> new file mode 100644
> index 000000000000..20dd0fb3dc71
> --- /dev/null
> +++ b/drivers/media/platform/renesas/rcar-isp/core.c
> @@ -0,0 +1,826 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/delay.h>
> +#include <linux/of_platform.h>
> +
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/vsp1.h>
> +
> +#include "risp-core.h"
> +
> +#define ISP_CS_STREAMER_MODE_REG 0x7000
> +#define ISP_CS_STREAMER_MODE_STREAMER_EN 0xf
> +
> +#define ISP_CS_STREAMER_VBLANK_REG 0x7004
> +#define ISP_CS_STREAMER_HBLANK_REG 0x7008
> +
> +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG 0x7100
> +#define ISP_CS_STREAMER_CONFIG_DMA_REG_ADDRESS_UPPER_8BIT_MASK GENMASK(31, 24)
> +#define ISP_CS_STREAMER_CONFIG_DMA_ENABLE0 BIT(0)
> +
> +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG 0x2100
> +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 BIT(31)
> +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_CONFIG_DATA_START_REG_ADDRESS_MASK GENMASK(15, 0)
> +
> +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG 0x2104
> +
> +#define ISP_CORE_ISPCORE_INT_STATUS 0x80000
> +#define ISP_CORE_ISPCORE_INT_ENABLE 0x80004
> +#define ISPCORE_DMA_IMAGE_FRAME_MODE(i, f) (0x84000 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(i, f) (0x84004 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(i, f) (0x84008 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(i, f) (0x8400c + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(i, f) (0x84010 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(i, f) (0x84014 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP2(i, f) (0x84018 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP3(i, f) (0x8401c + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(i, f) (0x84020 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(i, f) (0x84024 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP2(i, f) (0x84028 + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP3(i, f) (0x8402c + 0x1000 * (i) + 0x100 * (f))
> +#define ISPCORE_DMA_IMAGE_FRAME_AXI_ID(i, f) (0x84030 + 0x1000 * (i) + 0x100 * (f))
> +
> +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(i) (0x84400 + 0x1000 * (i))
> +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_MASK GENMASK(31, 16)
> +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT 16
> +
> +#define ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(i) (0x84800 + 0x1000 * (i))
> +
> +static void risp_cs_write(struct rcar_isp_core *core, u32 offset, u32 value)
> +{
> + iowrite32(value, core->csbase + offset);
> +}
> +
> +static u32 risp_cs_read(struct rcar_isp_core *core, u32 offset)
> +{
> + return ioread32(core->csbase + offset);
> +}
> +
> +static void risp_core_write(struct rcar_isp_core *core, u32 offset, u32 value)
> +{
> + iowrite32(value, core->base + offset);
> +}
> +
> +static u32 risp_core_read(struct rcar_isp_core *core, u32 offset)
> +{
> + return ioread32(core->base + offset);
> +}
> +
> +static void risp_core_job_run_params(struct rcar_isp_core *core,
> + struct vsp1_isp_job_desc *vspx_job,
> + struct risp_buffer *buf)
> +{
> + u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
> + bool have_config = !!params_buf[0];
> + u32 ctrl0, ctrl1, ctrl2;
> +
> + /*
> + * If we have a configuration but not asked the VSPX to programme it,
s/programme/program ?
> + * use MMIO to write the configuration. This might be needed to work
> + * around limitations of the VSPX ConfigDMA.
I would add a reference to the comment in risp_core_job_prepare() that
explains why MMIO has to be used for buffers < 16 entries.
* use MMIO to write the configuration. This might be needed to work
* around limitations of the VSPX ConfigDMA as explained in
* risp_core_job_prepare().
> + */
> + if (have_config && !vspx_job->config.pairs) {
> + for (unsigned int i = 0; i < params_buf[0]; i++)
> + risp_core_write(core, params_buf[2 + i * 2] & 0xffff,
> + params_buf[3 + i * 2]);
> +
> + /* Disable ConfigDMA. */
> + have_config = false;
> + }
> +
> + ctrl0 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG) &
> + ~ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
> + ctrl1 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG) &
> + ~(ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 | 0xffff);
> + ctrl2 = 0;
> +
> + if (have_config) {
> + ctrl0 |= ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
> + ctrl1 |= ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 |
> + (params_buf[2] & 0xffff);
> + ctrl2 = params_buf[3];
> + }
> +
> + risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG, ctrl0);
> + risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG, ctrl1);
> + risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG, ctrl2);
> +}
> +
> +static void risp_core_job_run_output(struct rcar_isp_core *core,
> + struct risp_buffer *buf)
> +{
> + const struct v4l2_format *fmt = &core->io[RISP_CORE_OUTPUT1].format;
> + dma_addr_t mem;
> + u32 reg;
> +
> + for (unsigned int frame = 0; frame < 4; frame++) {
> + reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(0, frame);
> + mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
> + risp_core_write(core, reg, mem);
> +
> + /* Only NV16 uses 2 planes. */
> + if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV16M)
> + continue;
> +
> + reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(0, frame);
> + mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
> + risp_core_write(core, reg, mem);
> + }
> +}
> +
> +static void risp_core_job_run(struct rcar_isp_core *core)
> +{
> + struct rcar_isp_job *job;
> +
> + lockdep_assert_held(&core->lock);
> +
> + /* ISP not yet started, nothing to do. */
> + if (!core->streaming)
> + return;
> +
> + /* If we have active buffers in the ISP core, nothing to do. */
> + if (core->vspx.job)
> + return;
> +
> + job = list_first_entry_or_null(&core->risp_jobs,
> + struct rcar_isp_job,
> + job_queue);
> + if (!job)
> + return;
> +
> + list_del(&job->job_queue);
> +
> + core->vspx.job = job;
> +
> + /* Program the ISP register before kicking the VSPX. */
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + struct risp_buffer *buf = job->buffers[i];
> +
> + switch (i) {
> + case RISP_CORE_PARAMS:
> + risp_core_job_run_params(core, &job->vspx_job, buf);
> + break;
> + case RISP_CORE_OUTPUT1:
> + risp_core_job_run_output(core, buf);
> + break;
> + }
> + }
> +
> + if (vsp1_isp_job_run(core->vspx.dev, &job->vspx_job)) {
> + /*
> + * Release all buffers in this job if running on the VSPX
> + * failed. Userspace should recover from this, no new jobs are
> + * scheduled.
> + */
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + struct risp_buffer *buf = job->buffers[i];
> +
> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> + }
> +
> + vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
> + core->vspx.job = NULL;
> + kfree(job);
> +
> + dev_err(core->dev, "Failed to run job");
> + }
> +}
> +
> +static int risp_core_pixfmt_to_vspx(u32 pixfmt)
> +{
> + switch (pixfmt) {
> + case V4L2_PIX_FMT_SBGGR8:
> + case V4L2_PIX_FMT_SGBRG8:
> + case V4L2_PIX_FMT_SGRBG8:
> + case V4L2_PIX_FMT_SRGGB8:
> + return V4L2_PIX_FMT_GREY;
> + case V4L2_PIX_FMT_SBGGR10:
> + case V4L2_PIX_FMT_SGBRG10:
> + case V4L2_PIX_FMT_SGRBG10:
> + case V4L2_PIX_FMT_SRGGB10:
> + return V4L2_PIX_FMT_Y10;
> + case V4L2_PIX_FMT_SBGGR12:
> + case V4L2_PIX_FMT_SGBRG12:
> + case V4L2_PIX_FMT_SGRBG12:
> + case V4L2_PIX_FMT_SRGGB12:
> + return V4L2_PIX_FMT_Y12;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +int risp_core_job_prepare(struct rcar_isp_core *core)
> +{
> + struct vsp1_isp_job_desc *vspx_job;
> + int vspx_pixfmt = -EINVAL;
> + struct rcar_isp_job *job;
> + int ret;
> +
> + lockdep_assert_held(&core->io_lock);
> +
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + if (list_empty(&core->io[i].buffers))
> + return 0;
> + }
> +
> + /* Memory is released when the job is consumed. */
> + job = kzalloc(sizeof(*job), GFP_KERNEL);
> + if (!job)
> + return -ENOMEM;
> +
> + vspx_job = &job->vspx_job;
> +
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + struct risp_buffer *buf;
> +
> + /*
> + * Extract buffer from the IO queue and save a reference in
> + * the job description. Buffers will be completed when the
> + * corresponding frame will be completed by the ISP.
> + */
> + buf = list_first_entry_or_null(&core->io[i].buffers,
> + struct risp_buffer, list);
> + if (!buf) {
This shouldn't happen, right ? we just checked above that we have
buffers and we're helding io_lock.
If we want to be defensive it doesn't hurt to keep this (maybe we
should even WARN_ON(!buf) as if this happens, something has really
gone wrong
> + ret = -EINVAL;
> + goto error_return_buffers;
> + }
> +
> + switch (i) {
> + case RISP_CORE_INPUT1: {
> + u32 isp_pixfmt = core->io[i].format.fmt.pix_mp.pixelformat;
> +
> + vspx_pixfmt = risp_core_pixfmt_to_vspx(isp_pixfmt);
> +
> + vspx_job->img.fmt = core->io[i].format.fmt.pix_mp;
> + vspx_job->img.fmt.pixelformat = vspx_pixfmt;
> + vspx_job->img.mem =
> + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf,
> + 0);
> + break;
> + }
> + case RISP_CORE_PARAMS: {
> + /*
> + * Work around undocumented behavior of the ConfigDMA
> + * interface by using MMIO if 16 or less pairs are to
> + * be programmed.
> + *
> + * Programing 15 or less pairs corrupts the image data
> + * following the config buffer, programing exactly 16
> + * pairs freeze the whole VSPX.
> + */
> + u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
> +
> + if (params_buf[0] <= 16) {
> + vspx_job->config.pairs = 0;
> + } else {
> + vspx_job->config.pairs = params_buf[0];
> + vspx_job->config.mem = buf->vsp_buffer.dma_addr;
> + }
> + break;
> + }
> + }
> +
> + list_del(&buf->list);
> + job->buffers[i] = buf;
> + }
> +
> + if (vspx_pixfmt < 0) {
> + ret = -EINVAL;
> + goto error_return_buffers;
> + }
> +
> + ret = vsp1_isp_job_prepare(core->vspx.dev, vspx_job);
> + if (ret)
> + goto error_return_buffers;
> +
> + scoped_guard(spinlock_irqsave, &core->lock) {
> + list_add_tail(&job->job_queue, &core->risp_jobs);
> + risp_core_job_run(core);
> + }
> +
> + return 0;
> +
> +error_return_buffers:
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + if (!job->buffers[i])
> + continue;
> +
> + vb2_buffer_done(&job->buffers[i]->vb.vb2_buf,
> + VB2_BUF_STATE_ERROR);
> + }
> + kfree(job);
> + return ret;
> +}
> +
> +static int risp_core_config_output(struct rcar_isp_core *core,
> + unsigned int index,
> + const struct v4l2_pix_format_mplane *pix)
> +{
> + /* For all frame capture slots. */
> + for (unsigned int frame = 0; frame < 4; frame++) {
> + switch (pix->pixelformat) {
> + case V4L2_PIX_FMT_NV16M:
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
> + 1);
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
> + 0 << 24 | 0 << 16 | 4 << 8 | 16 << 0);
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
> + 0 << 24 | 0 << 16 | 7 << 8 | 7 << 0);
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
> + 0 << 28 | 0 << 24 |
> + 0 << 20 | 0 << 16 |
> + 3 << 12 | 0 << 8 |
> + 3 << 4 | 0 << 0);
> +
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
> + pix->plane_fmt[0].bytesperline);
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(index, frame),
> + pix->plane_fmt[0].bytesperline);
> + break;
> + case V4L2_PIX_FMT_XBGR32:
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
> + 0);
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
> + 0 << 24 | 0 << 16 | 0 << 8 | 0 << 0);
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
> + 0 << 24 | 0 << 16 | 0 << 8 | 23 << 0);
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
> + 0 << 28 | 0 << 24 |
> + 0 << 20 | 0 << 16 |
> + 0 << 12 | 0 << 8 |
> + 3 << 4 | 2 << 0);
> +
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
> + pix->plane_fmt[0].bytesperline);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + risp_core_write(core,
> + ISPCORE_DMA_IMAGE_FRAME_AXI_ID(index, frame),
> + 0);
> + }
> +
> + /* Set image out flush EOF. */
> + risp_core_write(core, ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(index),
> + pix->plane_fmt[0].bytesperline <<
> + ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT);
> +
> + /* Enable DMA and set burst length. */
> + risp_core_write(core, ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(index),
> + BIT(31) | 7);
> +
> + return 0;
> +}
> +
> +static u32 risp_core_pix2bus(const struct rcar_isp_core_io *io)
> +{
> + switch (io->format.fmt.pix_mp.pixelformat) {
> + case V4L2_PIX_FMT_SBGGR8:
> + return MEDIA_BUS_FMT_SBGGR8_1X8;
> + case V4L2_PIX_FMT_SGBRG8:
> + return MEDIA_BUS_FMT_SGBRG8_1X8;
> + case V4L2_PIX_FMT_SGRBG8:
> + return MEDIA_BUS_FMT_SGRBG8_1X8;
> + case V4L2_PIX_FMT_SRGGB8:
> + return MEDIA_BUS_FMT_SRGGB8_1X8;
> + case V4L2_PIX_FMT_SBGGR10:
> + return MEDIA_BUS_FMT_SBGGR10_1X10;
> + case V4L2_PIX_FMT_SGBRG10:
> + return MEDIA_BUS_FMT_SGBRG10_1X10;
> + case V4L2_PIX_FMT_SGRBG10:
> + return MEDIA_BUS_FMT_SGRBG10_1X10;
> + case V4L2_PIX_FMT_SRGGB10:
> + return MEDIA_BUS_FMT_SRGGB10_1X10;
> + case V4L2_PIX_FMT_SBGGR12:
> + return MEDIA_BUS_FMT_SBGGR12_1X12;
> + case V4L2_PIX_FMT_SGBRG12:
> + return MEDIA_BUS_FMT_SGBRG12_1X12;
> + case V4L2_PIX_FMT_SGRBG12:
> + return MEDIA_BUS_FMT_SGRBG12_1X12;
> + case V4L2_PIX_FMT_SRGGB12:
> + return MEDIA_BUS_FMT_SRGGB12_1X12;
> + case V4L2_PIX_FMT_XBGR32:
> + return MEDIA_BUS_FMT_RGB888_1X24;
> + case V4L2_PIX_FMT_NV16M:
> + return MEDIA_BUS_FMT_YUYV12_1X24;
> + default:
> + return 0;
> + }
> +}
> +
> +static void risp_core_try_next_job(struct rcar_isp_core *core)
> +{
> + lockdep_assert_held(&core->lock);
> +
> + struct rcar_isp_job *job = core->vspx.job;
> +
> + /* If the ISP or the VSPX is not done with the job, wait. */
> + if (!job || !job->done_isp || !job->done_vspx)
> + return;
> +
> + core->vspx.job = NULL;
> + kfree(job);
> +
> + core->sequence++;
> +
> + /* Kickoff processing of next frame (if any). */
> + risp_core_job_run(core);
> +}
> +
> +static void risp_core_svspx_frame_end(void *data)
Should this be called risp_core_vspsx ...
> +{
> + struct rcar_isp_core *core = data;
> +
> + guard(spinlock_irqsave)(&core->lock);
> +
> + /*
> + * In tear-down the ISP may report a frame end event but we have already
> + * freed the job. It is safe to ignore the end of frame event.
> + */
> + if (!core->vspx.job)
> + return;
> +
> + core->vspx.job->done_vspx = true;
> + risp_core_try_next_job(core);
> +}
> +
> +int risp_core_start_streaming(struct rcar_isp_core *core)
> +{
> + struct vsp1_vspx_frame_end vspx_fe = {
> + .vspx_frame_end = risp_core_svspx_frame_end,
> + .frame_end_data = core,
> + };
> +
> + struct v4l2_mbus_framefmt inputfmt = {
> + .width = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width,
> + .height = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height,
> + .code = risp_core_pix2bus(&core->io[RISP_CORE_INPUT1]),
> + .field = V4L2_FIELD_NONE,
> + .colorspace = V4L2_COLORSPACE_RAW,
> + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
> + .quantization = V4L2_QUANTIZATION_DEFAULT,
> + .xfer_func = V4L2_XFER_FUNC_DEFAULT,
Please adjust the colorspace related fields according to the format.
I -think- using _DEFAULT is discouraged ?
> + };
> +
> + struct v4l2_mbus_framefmt hvout = {
> + .width = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width,
> + .height = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height,
> + .code = risp_core_pix2bus(&core->io[RISP_CORE_OUTPUT1]),
> + .field = V4L2_FIELD_NONE,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
> + .quantization = V4L2_QUANTIZATION_DEFAULT,
> + .xfer_func = V4L2_XFER_FUNC_DEFAULT,
> + };
> +
> + scoped_guard(mutex, &core->io_lock) {
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + if (!core->io[i].streaming)
> + return 0;
> + }
> +
> + /*
> + * The state core->streaming is protected by core->lock, which
> + * is not held yet. It is however safe to read it here since
> + * core->io_lock is held both in risp_core_stop_streaming() and
> + * here, the only two places the variable is modified.
> + *
> + * With this small implied dependency on the two locks for write
> + * access, the interrupt handler can safely depend sole on the
> + * spinlock core->lock for read access to core->streaming.
> + *
> + * The gain is an interrupt handler which can hold the spinlock
> + * and a start/stop procedure which can reset the ISP using the
> + * reset_control_reset() API, The later which can not be called
> + * from a context that may sleep.
> + *
> + * All other locations core->streaming is read and _all_
> + * locations where it is written core->lock is held.
> + */
> + if (core->streaming)
> + return 0;
> +
> + /* Reset and wait for ISP core to initialize itself. */
> + reset_control_reset(core->rstc);
> + usleep_range(2000, 4000);
> +
> + scoped_guard(spinlock_irqsave, &core->lock) {
> + int ret;
> +
> + risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 1);
> +
> + /* Configure output DMA */
> + risp_core_config_output(core, 0,
> + &core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp);
> +
> + risp_cs_write(core, ISP_CS_STREAMER_VBLANK_REG, inputfmt.width * 25);
> + risp_cs_write(core, ISP_CS_STREAMER_HBLANK_REG, 64);
> +
> + /* Enable ISP Streaming bridge. */
> + risp_cs_write(core, ISP_CS_STREAMER_MODE_REG,
> + ISP_CS_STREAMER_MODE_STREAMER_EN);
> +
> + /* Start RPP ISP */
> + ret = rppx1_start(core->rpp, &inputfmt, &hvout, NULL);
> + if (ret)
> + return ret;
> +
> + core->vspx.job = NULL;
> + core->sequence = 0;
> + core->streaming = true;
> + }
> +
> + /* Start VSPX */
> + vsp1_isp_start_streaming(core->vspx.dev, &vspx_fe);
> +
> + scoped_guard(spinlock_irqsave, &core->lock) {
> + risp_core_job_run(core);
> + }
> + }
> +
> + return 0;
> +}
> +
> +void risp_core_stop_streaming(struct rcar_isp_core *core)
> +{
> + struct rcar_isp_job *job, *tmp;
> +
> + /*
> + * This function releases buffers and jobs: make sure the queues mutex
> + * is held.
> + */
> + lockdep_assert_held(&core->io_lock);
> +
> + scoped_guard(spinlock_irqsave, &core->lock) {
> + /* Stop is called by each vdev, only act on the first call. */
> + if (!core->streaming)
> + return;
> +
> + /* Stop queueing jobs to VSPX. */
> + core->streaming = false;
> + }
> +
> + /* Wait for active VSPX job to finish. */
> + for (unsigned int retry = 0; retry <= 10; retry++) {
> + if (!core->vspx.job)
> + break;
> +
> + usleep_range(2000, 4000);
> + }
> +
> + if (core->vspx.job)
> + dev_err(core->dev, "Failed to complete running job");
> +
> + /* Free all buffers and switch of the hardware. */
"switch off"
> + scoped_guard(spinlock_irqsave, &core->lock) {
> + /* Free all jobs and buffers. */
> + list_for_each_entry_safe(job, tmp, &core->risp_jobs, job_queue) {
> + vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
> +
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + struct risp_buffer *buf = job->buffers[i];
> +
> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> + }
> +
> + list_del(&job->job_queue);
> + kfree(job);
> + }
> +
> + rppx1_stop(core->rpp);
> + risp_cs_write(core, ISP_CS_STREAMER_MODE_REG, 0);
> + risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 0);
> + }
> +
> + vsp1_isp_stop_streaming(core->vspx.dev);
I'm not really going to review again the locking and vspx interfacing
as we spent quite some time in order to make it LOCKDEP-safe and
reliable. Speaking of which, please add:
[ VSPX interfacing + locking sanitizing ]
Co-developed-by: Jacopo Mondi <jacopo.mondi+renesas@ideasonboard.com>
> +}
> +
> +static irqreturn_t risp_core_irq(int irq, void *data)
> +{
> + struct rcar_isp_core *core = data;
> + struct rcar_isp_job *job;
> + u32 status;
> +
> + status = risp_core_read(core, ISP_CORE_ISPCORE_INT_STATUS);
> + if (!(status & BIT(0)))
> + return IRQ_NONE;
> +
> + if (!rppx1_interrupt(core->rpp, &status))
> + return IRQ_HANDLED;
> +
> + guard(spinlock_irqsave)(&core->lock);
> +
> + job = core->vspx.job;
> + if (!job)
> + return IRQ_HANDLED;
> +
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + struct risp_buffer *buf;
> +
> + buf = job->buffers[i];
> +
> + switch (i) {
> + case RISP_CORE_STATS:
> + rppx1_stats_fill_isr(core->rpp, status,
> + vb2_plane_vaddr(&buf->vb.vb2_buf, 0));
> + fallthrough;
> + case RISP_CORE_OUTPUT1:
> + case RISP_CORE_INPUT1:
> + buf->vb.sequence = core->sequence;
> + buf->vb.vb2_buf.timestamp = ktime_get_ns();
> + fallthrough;
> + case RISP_CORE_PARAMS:
> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
> + break;
> + }
> + }
> +
> + core->vspx.job->done_isp = true;
> + risp_core_try_next_job(core);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const struct v4l2_subdev_ops risp_core_subdev_ops = {
> +};
> +
> +static int risp_core_create_subdev(struct rcar_isp_core *core)
> +{
> + struct v4l2_subdev *subdev = &core->subdev;
> + int ret;
> +
> + subdev->owner = THIS_MODULE;
> + subdev->dev = core->dev;
> + v4l2_subdev_init(subdev, &risp_core_subdev_ops);
> + v4l2_set_subdevdata(subdev, core->dev);
> + snprintf(subdev->name, sizeof(subdev->name), "%s %s core",
> + KBUILD_MODNAME, dev_name(core->dev));
> + subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> + subdev->entity.function = MEDIA_ENT_F_VID_MUX;
> +
> + core->pads[RISP_CORE_INPUT1].flags = MEDIA_PAD_FL_SINK;
> + core->pads[RISP_CORE_PARAMS].flags = MEDIA_PAD_FL_SINK;
> + core->pads[RISP_CORE_STATS].flags = MEDIA_PAD_FL_SOURCE;
> + core->pads[RISP_CORE_OUTPUT1].flags = MEDIA_PAD_FL_SOURCE;
> +
> + ret = media_entity_pads_init(&subdev->entity, RISP_CORE_NUM_PADS,
> + core->pads);
> + if (ret)
> + return ret;
> +
> + dev_info(core->dev, "Registered ISP core\n");
I would demote it to _dbg at most. And you've not registered yet if
I'm not mistaken
> +
> + return 0;
> +}
> +
> +int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd)
> +{
> + int ret;
> +
> + core->v4l2_dev.mdev = sd->v4l2_dev->mdev;
> +
> + /* Register ISP Core subdevice. */
> + ret = v4l2_device_register_subdev(&core->v4l2_dev, &core->subdev);
> + if (ret)
> + return ret;
> +
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> + ret = risp_core_io_create(core->dev, core, &core->io[i], i);
> + if (ret)
> + return ret;
Should the subdev be unregistered and the already created video device
destroyed ?
> + }
> +
> + return 0;
> +}
> +
> +static int risp_core_probe_resources(struct rcar_isp_core *core,
> + struct platform_device *pdev)
> +{
> + struct platform_device *vspx;
> + struct device_node *of_vspx;
> + struct resource *res;
> + int ret;
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
> + if (!res)
> + return -ENODEV;
> +
> + core->rppaddr = res->start;
> + core->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(core->base))
> + return PTR_ERR(core->base);
> +
> + ret = platform_get_irq_byname(pdev, "core");
> + if (ret < 0)
> + return -ENODEV;
> +
> + ret = devm_request_irq(&pdev->dev, ret, risp_core_irq, IRQF_SHARED,
> + KBUILD_MODNAME, core);
> + if (ret)
> + return ret;
> +
> + core->clk = devm_clk_get(&pdev->dev, "core");
> + if (IS_ERR(core->clk))
> + return -ENODEV;
> +
> + core->rstc = devm_reset_control_get(&pdev->dev, "core");
> + if (IS_ERR(core->rstc))
> + return -ENODEV;
> +
> + of_vspx = of_parse_phandle(pdev->dev.of_node, "renesas,vspx", 0);
> + if (!of_vspx)
> + return -ENODEV;
> +
> + vspx = of_find_device_by_node(of_vspx);
> + if (!vspx)
> + return -ENODEV;
> +
> + /* Attach to VSP-X */
> + core->vspx.dev = &vspx->dev;
> +
> + ret = vsp1_isp_init(&vspx->dev);
> + if (ret < 0)
> + return ret;
> +
> + /* Attach ro the RPP library
s/ro/to
> + *
> + * 1. Start and wait for the ISP to startup.
> + * 2. Attach the RPP library and talk with the RPP ISP.
> + * 3. Turn off ISP.
> + * 4. Fail if the RPP is unhappy with the hardware.
> + */
> + ret = clk_prepare_enable(core->clk);
> + if (ret)
> + return ret;
> +
> + usleep_range(2000, 4000);
> +
> + core->rpp = rppx1_create(core->base, &pdev->dev);
> +
> + clk_disable_unprepare(core->clk);
> +
> + if (!core->rpp)
> + return -ENODEV;
> +
> + return 0;
> +}
> +
> +int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
> + void __iomem *csbase, struct reset_control *csrstc)
> +{
> + int ret;
> +
> + core->dev = &pdev->dev;
> + core->csrstc = csrstc;
> + core->csbase = csbase;
> +
> + ret = risp_core_probe_resources(core, pdev);
> + if (ret) {
> + core->base = NULL;
> + return ret;
> + }
> +
> + ret = v4l2_device_register(core->dev, &core->v4l2_dev);
> + if (ret)
> + return ret;
> +
> + ret = risp_core_create_subdev(core);
> + if (ret)
> + return ret;
> +
> + mutex_init(&core->io_lock);
> + spin_lock_init(&core->lock);
> + INIT_LIST_HEAD(&core->risp_jobs);
> +
> + return 0;
> +}
> +
> +void risp_core_remove(struct rcar_isp_core *core)
> +{
> + /* If we did not probe the ISP core, nothing to do. */
> + if (!core->base)
> + return;
> +
> + dev_info(core->dev, "Remove ISP Core\n");
> +
> + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++)
> + risp_core_io_destory(&core->io[i]);
> +
> + mutex_destroy(&core->io_lock);
> + rppx1_destroy(core->rpp);
> +}
> diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
> index 8fb2cc3b5650..53ce47020d17 100644
> --- a/drivers/media/platform/renesas/rcar-isp/csisp.c
> +++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
> @@ -11,14 +11,13 @@
> */
>
> #include <linux/module.h>
> -#include <linux/mutex.h>
> #include <linux/of.h>
> -#include <linux/platform_device.h>
> #include <linux/pm_runtime.h>
> -#include <linux/reset.h>
>
> #include <media/mipi-csi2.h>
> -#include <media/v4l2-subdev.h>
> +#include <media/v4l2-async.h>
> +
> +#include "risp-core.h"
>
> #define ISPINPUTSEL0_REG 0x0008
> #define ISPINPUTSEL0_SEL_CSI0 BIT(31)
> @@ -158,6 +157,7 @@ struct rcar_isp {
> struct device *dev;
> void __iomem *csbase;
> struct reset_control *rstc;
> + struct rcar_isp_core core;
>
> enum rcar_isp_input csi_input;
>
> @@ -451,6 +451,21 @@ static int risp_parse_dt(struct rcar_isp *isp)
> return ret;
> }
>
> +/* -----------------------------------------------------------------------------
> + * ISP Core connection
> + */
> +
> +static int risp_cs_registered(struct v4l2_subdev *sd)
> +{
> + struct rcar_isp *isp = sd_to_isp(sd);
> +
> + return risp_core_registered(&isp->core, sd);
> +}
> +
> +static const struct v4l2_subdev_internal_ops risp_cs_internal_ops = {
> + .registered = risp_cs_registered,
Back on the module/load unload thing I hinted previously, this is
called when the vin's notifier matches on the cs subdev. Does
unloading and then reloading the VIN module cause issues ?
> +};
> +
> /* -----------------------------------------------------------------------------
> * Platform Device Driver
> */
> @@ -477,7 +492,7 @@ static int risp_probe_resources(struct rcar_isp *isp,
> if (IS_ERR(isp->csbase))
> return PTR_ERR(isp->csbase);
>
> - isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
> + isp->rstc = devm_reset_control_get_shared(&pdev->dev, NULL);
>
> return PTR_ERR_OR_ZERO(isp->rstc);
> }
> @@ -541,14 +556,31 @@ static int risp_probe(struct platform_device *pdev)
> if (ret)
> goto error_notifier;
>
> - ret = v4l2_async_register_subdev(&isp->subdev);
> - if (ret < 0)
> + ret = risp_core_probe(&isp->core, pdev, isp->csbase, isp->rstc);
> + switch (ret) {
> + case 0:
> + /* The device have an ISP core. */
> + isp->subdev.internal_ops = &risp_cs_internal_ops;
> + break;
> + case -ENODEV:
> + /* The device don't have an ISP core, that is OK. */
> + ret = 0;
> + break;
> + default:
> + /* Something went wrong registering the ISP core. */
> goto error_subdev;
> + }
> +
> + ret = v4l2_async_register_subdev(&isp->subdev);
> + if (ret < 0)
> + goto error_core;
>
> dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
>
> return 0;
>
> +error_core:
> + risp_core_remove(&isp->core);
> error_subdev:
> v4l2_subdev_cleanup(&isp->subdev);
> error_notifier:
> @@ -564,6 +596,8 @@ static void risp_remove(struct platform_device *pdev)
> {
> struct rcar_isp *isp = platform_get_drvdata(pdev);
>
> + risp_core_remove(&isp->core);
> +
> v4l2_async_nf_unregister(&isp->notifier);
> v4l2_async_nf_cleanup(&isp->notifier);
>
> diff --git a/drivers/media/platform/renesas/rcar-isp/risp-core.h b/drivers/media/platform/renesas/rcar-isp/risp-core.h
> new file mode 100644
> index 000000000000..4c848551c1ef
> --- /dev/null
> +++ b/drivers/media/platform/renesas/rcar-isp/risp-core.h
> @@ -0,0 +1,170 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +
> +#ifndef __RCAR_ISP__
> +#define __RCAR_ISP__
> +
> +#include <linux/clk.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
#include <linux/spinlock.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include <media/rppx1.h>
> +#include <media/vsp1.h>
> +
> +struct rcar_isp_core;
> +
> +enum risp_core_pads {
> + RISP_CORE_INPUT1,
> + RISP_CORE_PARAMS,
> + RISP_CORE_STATS,
> + RISP_CORE_OUTPUT1,
> + RISP_CORE_NUM_PADS,
> +};
> +
> +/**
> + * struct risp_buffer - Describe an IO buffer
> + * @vb: The VB2 buffer
> + * @list: List of buffers queued to the IO queue
> + * @vsp_buffer: Buffer mapped from VSP-X, only used for params IO
> + */
> +struct risp_buffer {
> + struct vb2_v4l2_buffer vb;
> + struct list_head list;
> + struct vsp1_isp_buffer_desc vsp_buffer;
> +};
> +
> +/**
> + * struct rcar_isp_core_io - Information for a IO video devices
> + * @core: Backlink to the common ISP core structure
> + *
> + * @lock: Protects @vdev, @pad and @queue + open/close fops
> + * @vdev: V4L2 video device associated with this IO port
> + * @pad: Media pad for @vdev
> + * @queue: VB2 buffers queue for $@vdev
> + *
> + * @qlock: Protects @streaming and @buffers
> + * @streaming: Flag to indicate if device is streaming, or not
> + * @buffers: List of buffers queued to the device
> + *
> + * @format: The active V4L2 format
> + */
> +struct rcar_isp_core_io {
> + struct rcar_isp_core *core;
> +
> + struct mutex lock; /* See KDoc block. */
> + struct video_device vdev;
> + struct media_pad pad;
> + struct vb2_queue queue;
> +
> + bool streaming;
> + struct list_head buffers;
> +
> + struct v4l2_format format;
> +};
> +
> +/**
> + * struct rcar_isp_job - R-Car ISP job description
> + *
> + * Both done_isp and done_vspx shall be set before the job can be considered
> + * completely done.
> + *
> + * @buffers: IO buffers that form a job
> + * @vspx_job: VSPX job description
> + * @job_queue: list handle
> + * @done_isp: Flag to indicate the ISP is done with the job
> + * @done_vspx: Flag to indicate the VSPX is done with the job
> + */
> +struct rcar_isp_job {
> + struct risp_buffer *buffers[RISP_CORE_NUM_PADS];
> + struct vsp1_isp_job_desc vspx_job;
> + struct list_head job_queue;
> + bool done_isp;
> + bool done_vspx;
> +};
> +
> +/**
> + * struct rcar_isp_vspx - R-Car ISP job description
> + *
> + * @dev: Device reference to VSPX
> + * @job: Job currently being processed by VSPX
> + */
> +struct rcar_isp_vspx {
> + struct device *dev;
> + struct rcar_isp_job *job;
> +};
> +
> +/**
> + * struct rcar_isp_core - ISP Core
> + * @dev: (OF) device
> + * @rppaddr: Hardware address of the RPP ISP (from OF)
> + * @clk: The clock for the ISP CORE
> + * @rstc: The reset for the ISP Core
> + * @csrstc: The reset for the ISP Channel Selector
> + *
> + * @base: MMIO base of the ISP CORE
> + * @csbase: MMIO base of the ISP CS
> + *
> + * @subdev: V4L2 subdevice to represent the ISP CORE
> + * @pads: Media pad for @subdev
> + *
> + * @v4l2_dev: V4L2 device
> + * @rpp: Handle to the RPP ISP connected to the ISP CORE
> + *
> + * @io_lock: Protect io[*].streaming and io[*].buffers
> + * @io: Array of IO ports to the ISP CORE
> + *
> + * @lock: Protects @vspx, @risp_jobs, @sequence and @streaming
> + * @vspx: Handle to the resources used by VSPX connected to the ISP CORE
> + * @risp_jobs: Queue of VSPX transfer jobs
> + * @sequence: V4L2 buffers sequence number
> + * @streaming: Tracks if the device is streaming
> + */
> +struct rcar_isp_core {
> + struct device *dev;
> +
> + u32 rppaddr;
> + struct clk *clk;
> +
> + struct reset_control *rstc;
> + struct reset_control *csrstc;
> +
> + void __iomem *base;
> + void __iomem *csbase;
> +
> + struct v4l2_subdev subdev;
> + struct media_pad pads[RISP_CORE_NUM_PADS];
> +
> + struct v4l2_device v4l2_dev;
> + struct rppx1 *rpp;
> +
> + struct mutex io_lock; /* See KDoc block. */
> + struct rcar_isp_core_io io[RISP_CORE_NUM_PADS];
> +
> + spinlock_t lock; /* See KDoc block. */
> + struct rcar_isp_vspx vspx;
> + struct list_head risp_jobs;
> + unsigned int sequence;
> + bool streaming;
> +};
> +
> +int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
> + void __iomem *csbase, struct reset_control *csrstc);
> +void risp_core_remove(struct rcar_isp_core *core);
> +int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd);
> +
> +int risp_core_job_prepare(struct rcar_isp_core *core);
> +
> +int risp_core_start_streaming(struct rcar_isp_core *core);
> +void risp_core_stop_streaming(struct rcar_isp_core *core);
> +
> +int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
> + struct rcar_isp_core_io *io, unsigned int pad);
> +void risp_core_io_destory(struct rcar_isp_core_io *io);
> +
> +#endif
> --
Overall, this driver is robust and has been thoroughly tested in the
last 2 years, so I would be happy to see it merged soon.
Of all the comments I had, only two things need to be clarified:
- The IO power routines
- Module loading/unloading
everything else is minor and I hope doesn't cause any rework.
Thanks
j
> 2.54.0
>
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [v8 04/14] media: rppx1: wbmeas: Add support for white balance measurement
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (2 preceding siblings ...)
2026-05-04 1:05 ` [v8 03/14] media: rcar-isp: Add support for ISPCORE Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 13:58 ` Antoine Bouyer
2026-05-04 1:05 ` [v8 05/14] media: rppx1: awbg: Add support for white balance gain settings Niklas Söderlund
` (10 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the white balance measurement
configuration and consuming the resulting statistics. It uses the RPPX1
framework for parameters and its writer abstraction to allow the user to
control how, and when, configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 2 +
.../platform/dreamchip/rppx1/rpp_params.c | 4 +
.../platform/dreamchip/rppx1/rpp_stats.c | 18 +++
.../platform/dreamchip/rppx1/rppx1_wbmeas.c | 103 ++++++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 101 ++++++++++++++++-
5 files changed, 226 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 742904973e35..85fb23174e57 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -85,10 +85,12 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
union rppx1_params_block {
struct v4l2_isp_params_block_header header;
+ struct rppx1_wbmeas_params wbmeas;
};
union rppx1_stats_block {
struct v4l2_isp_params_block_header header;
+ struct rppx1_wbmeas_stats wbmeas;
};
struct rpp_module_ops {
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index a5feb18f3bd5..1262350d2190 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -16,6 +16,7 @@
static const struct v4l2_isp_block_type_info
rppx1_ext_params_blocks_info[] = {
+ RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
};
int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
@@ -50,6 +51,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
block_offset += block->header.size;
switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST:
+ module = &rpp->post.wbmeas;
+ break;
default:
module = NULL;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
index 8f43e56ba361..4c38e657e5f0 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -14,6 +14,11 @@
.size = sizeof(struct rppx1_ ## block ## _stats), \
}
+static const struct v4l2_isp_block_type_info
+rppx1_stats_blocks_info[] = {
+ RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
+};
+
#define rppx1_init_stats_block(rpp, buf, type) \
((union rppx1_stats_block *) \
v4l2_isp_stats_init_block((rpp)->dev, (buf), \
@@ -23,5 +28,18 @@
void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
{
+ struct v4l2_isp_buffer *stats = buf;
+ union rppx1_stats_block *block;
+
+ v4l2_isp_stats_init_buffer(stats);
+
+ if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) {
+ block = rppx1_init_stats_block(rpp, stats,
+ RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST);
+ if (!block)
+ return;
+
+ rpp_module_call(&rpp->post.wbmeas, fill_stats, block);
+ }
}
EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
index 3d197d914d07..53263bc96280 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
@@ -56,6 +56,109 @@ static int rppx1_wbmeas_probe(struct rpp_module *mod)
return 0;
}
+static int
+rppx1_wbmeas_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_wbmeas_params *cfg = &block->wbmeas;
+ u32 awb_meas_props;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + AWB_MEAS_PROP_REG, 0);
+ return 0;
+ }
+
+ /* Program measurement window. */
+ write(priv, mod->base + AWB_MEAS_H_OFFS_REG, cfg->wnd.h_offs);
+ write(priv, mod->base + AWB_MEAS_V_OFFS_REG, cfg->wnd.v_offs);
+ write(priv, mod->base + AWB_MEAS_H_SIZE_REG, cfg->wnd.h_size);
+ write(priv, mod->base + AWB_MEAS_V_SIZE_REG, cfg->wnd.v_size);
+
+ /* Set number of frames to sample. */
+ write(priv, mod->base + AWB_MEAS_FRAMES_REG, cfg->frames);
+
+ if (cfg->mode == RPPX1_WBMEAS_MODE_YCBCR) {
+ write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+ cfg->ref_cb_max_b);
+ write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+ cfg->ref_cr_max_r);
+ write(priv, mod->base + AWB_MEAS_MAX_Y_REG, cfg->max_y);
+ write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+ cfg->min_y_max_g);
+ write(priv, mod->base + AWB_MEAS_MAX_CSUM_REG, cfg->max_csum);
+ write(priv, mod->base + AWB_MEAS_MIN_C_REG, cfg->min_c);
+
+ /*
+ * Program the color conversion matrix coefficients and the
+ * per-color channel offsets.
+ */
+ for (unsigned int i = 0; i < 3; i++) {
+ for (unsigned int j = 0; j < 3; j++) {
+ unsigned int index = i * 3 + j;
+
+ write(priv,
+ mod->base + AWB_MEAS_CCOR_COEFF_REG(index),
+ cfg->ccor_coeff[i][j]);
+ }
+ }
+
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG,
+ cfg->ccor_offs[0]);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG,
+ cfg->ccor_offs[1]);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG,
+ cfg->ccor_offs[2]);
+
+ awb_meas_props = cfg->ymax_cmp ? AWB_MEAS_PROP_YMAX : 0;
+ } else {
+ write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+ cfg->ref_cb_max_b);
+ write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+ cfg->ref_cr_max_r);
+ write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+ cfg->min_y_max_g);
+
+ /* Bypass color conversion matrix and color offsets. */
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00000000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00000000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00000000);
+
+ awb_meas_props = AWB_MEAS_PROP_MEAS_MODE_RGB;
+ }
+
+ write(priv, mod->base + AWB_MEAS_PROP_REG,
+ awb_meas_props | AWB_MEAS_PROP_AWB_MODE_ON);
+
+ return 0;
+}
+
+static int rppx1_wbmeas_fill_stats(struct rpp_module *mod,
+ union rppx1_stats_block *block)
+{
+ struct rppx1_wbmeas_stats *stats = &block->wbmeas;
+
+ /* Return measurements at native hardware precision. */
+ stats->cnt = rpp_module_read(mod, AWB_MEAS_WHITE_CNT_REG);
+ stats->mean_y_or_g = rpp_module_read(mod, AWB_MEAS_MEAN_Y_G_REG);
+ stats->mean_cb_or_b = rpp_module_read(mod, AWB_MEAS_MEAN_CB_B_REG);
+ stats->mean_cr_or_r = rpp_module_read(mod, AWB_MEAS_MEAN_CR_R_REG);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_wbmeas_ops = {
.probe = rppx1_wbmeas_probe,
+ .fill_params = rppx1_wbmeas_fill_params,
+ .fill_stats = rppx1_wbmeas_fill_stats
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 26627be6f483..1d76a85164c8 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -40,13 +40,81 @@ struct rppx1_window {
* wider-than-8-bit results.
*/
+/**
+ * enum rppx1_params_block_type - RPP-X1 extensible params block types
+ *
+ * NOTE: Only append to the enumeration as the numbers are uAPI.
+ *
+ * @RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: AWB Measurement Configuration
+ */
+enum rppx1_params_block_type {
+ RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
+};
+
+/**
+ * enum rppx1_wbmeas_mode - AWB measurement mode
+ *
+ * @RPPX1_WBMEAS_MODE_YCBCR: YCbCr measurement mode
+ * @RPPX1_WBMEAS_MODE_RGB: RGB measurement mode
+ */
+enum rppx1_wbmeas_mode {
+ RPPX1_WBMEAS_MODE_YCBCR,
+ RPPX1_WBMEAS_MODE_RGB,
+};
+
+/**
+ * struct rppx1_wbmeas_params - AWB measurement configuration
+ *
+ * The Auto-White Balance measurement module is available on the MAIN_POST pipe.
+ * It supports two measurement modes, selected by the @mode field. The
+ * measurement window is programmed through the @wnd field.
+ *
+ * To support measurement in YCbCr mode a color conversion matrix with
+ * programmable offset is available in the @ccor_coeff and @ccor_offs fields.
+ * The color conversion matrix coefficients are represented as 16 bits signed
+ * Q4.12 numbers ranging from -8 to +7.99. The per-color channel offsets are
+ * represented as 25 bits 2's complement integer numbers ranging from -16777216
+ * to +16777215.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST)
+ * @mode: measurement mode (from enum rppx1_wbmeas_mode)
+ * @ymax_cmp: enable Y_MAX compare using @max_y
+ * @wnd: measurement window
+ * @frames: number of frames for mean value calculation (0 = 1 frame)
+ * @ref_cr_max_r: reference Cr or max red value in RGB mode, 24 bits
+ * @ref_cb_max_b: reference Cb or max blue value in RGB mode, 24 bits
+ * @min_y_max_g: luminance minimum value or max green value in RGB mode, 24 bits
+ * @max_y: luminance maximum value, only valid if @mode is set to YCbCr and
+ * @ymax_cmp is set to enabled, 24 bits
+ * @max_csum: chrominance sum maximum value, 24 bits
+ * @min_c: chrominance minimum value, 24 bits
+ * @ccor_coeff: coefficients for color conversion matrix, signed 16 bits Q4.6
+ * @ccor_offs: R-G-B color conversion coefficients, signed 25 bits 2's complement
+ */
+struct rppx1_wbmeas_params {
+ struct v4l2_isp_params_block_header header;
+ __u8 mode;
+ __u8 ymax_cmp;
+ struct rppx1_window wnd;
+ __u8 frames;
+ __u32 ref_cr_max_r;
+ __u32 ref_cb_max_b;
+ __u32 min_y_max_g;
+ __u32 max_y;
+ __u32 max_csum;
+ __u32 min_c;
+ __u16 ccor_coeff[3][3];
+ __u32 ccor_offs[3];
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
* Some types are reported twice as the same block might be instantiated in
* multiple pipes.
*/
-#define RPPX1_PARAMS_MAX_SIZE 0
+#define RPPX1_PARAMS_MAX_SIZE \
+ (sizeof(struct rppx1_wbmeas_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
@@ -55,12 +123,41 @@ struct rppx1_window {
* wider-than-8-bit results.
*/
+/**
+ * enum rppx1_stats_block_type - RPP-X1 extensible stats block types
+ *
+ * NOTE: Only append to the enumeration as the numbers are uAPI.
+ *
+ * @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement
+ */
+enum rppx1_stats_block_type {
+ RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
+};
+
+/**
+ * struct rppx1_wbmeas_stats - AWB statistics
+ *
+ * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_WBMEAS)
+ * @cnt: Number of pixels matched
+ * @mean_y_or_g: mean Y (or G in RGB mode) value, 24-bit
+ * @mean_cb_or_b: mean Cb (or B in RGB mode) value, 24-bit
+ * @mean_cr_or_r: mean Cr (or R in RGB mode) value, 24-bit
+ */
+struct rppx1_wbmeas_stats {
+ struct v4l2_isp_block_header header;
+ __u32 cnt;
+ __u32 mean_y_or_g;
+ __u32 mean_cb_or_b;
+ __u32 mean_cr_or_r;
+};
+
/**
* RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
*
* Some types are reported twice as the same block might be instantiated in
* multiple pipes.
*/
-#define RPPX1_STATS_MAX_SIZE 0
+#define RPPX1_STATS_MAX_SIZE \
+ (sizeof(struct rppx1_wbmeas_stats))
#endif /* __UAPI_RPP_X1_CONFIG_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 04/14] media: rppx1: wbmeas: Add support for white balance measurement
2026-05-04 1:05 ` [v8 04/14] media: rppx1: wbmeas: Add support for white balance measurement Niklas Söderlund
@ 2026-05-06 13:58 ` Antoine Bouyer
2026-05-06 14:22 ` Jacopo Mondi
0 siblings, 1 reply; 28+ messages in thread
From: Antoine Bouyer @ 2026-05-06 13:58 UTC (permalink / raw)
To: Niklas Söderlund, Jai Luthra, Mauro Carvalho Chehab,
Kuninori Morimoto, Jacopo Mondi, Laurent Pinchart, linux-media,
linux-renesas-soc, linux-kernel
On 5/4/26 3:05 AM, Niklas Söderlund wrote:
> Extend the RPPX1 driver to allow setting the white balance measurement
> configuration and consuming the resulting statistics. It uses the RPPX1
> framework for parameters and its writer abstraction to allow the user to
> control how, and when, configuration is applied to the RPPX1.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> ---
> .../platform/dreamchip/rppx1/rpp_module.h | 2 +
> .../platform/dreamchip/rppx1/rpp_params.c | 4 +
> .../platform/dreamchip/rppx1/rpp_stats.c | 18 +++
> .../platform/dreamchip/rppx1/rppx1_wbmeas.c | 103 ++++++++++++++++++
> .../uapi/linux/media/dreamchip/rppx1-config.h | 101 ++++++++++++++++-
> 5 files changed, 226 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> index 742904973e35..85fb23174e57 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> @@ -85,10 +85,12 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
>
> union rppx1_params_block {
> struct v4l2_isp_params_block_header header;
> + struct rppx1_wbmeas_params wbmeas;
> };
>
> union rppx1_stats_block {
> struct v4l2_isp_params_block_header header;
> + struct rppx1_wbmeas_stats wbmeas;
> };
>
> struct rpp_module_ops {
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> index a5feb18f3bd5..1262350d2190 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> @@ -16,6 +16,7 @@
>
> static const struct v4l2_isp_block_type_info
> rppx1_ext_params_blocks_info[] = {
> + RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
> };
>
> int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> @@ -50,6 +51,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> block_offset += block->header.size;
>
> switch (block->header.type) {
> + case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST:
> + module = &rpp->post.wbmeas;
> + break;
> default:
> module = NULL;
> break;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> index 8f43e56ba361..4c38e657e5f0 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> @@ -14,6 +14,11 @@
> .size = sizeof(struct rppx1_ ## block ## _stats), \
> }
>
> +static const struct v4l2_isp_block_type_info
> +rppx1_stats_blocks_info[] = {
> + RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
> +};
> +
> #define rppx1_init_stats_block(rpp, buf, type) \
> ((union rppx1_stats_block *) \
> v4l2_isp_stats_init_block((rpp)->dev, (buf), \
> @@ -23,5 +28,18 @@
>
> void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
> {
> + struct v4l2_isp_buffer *stats = buf;
> + union rppx1_stats_block *block;
> +
> + v4l2_isp_stats_init_buffer(stats);
> +
> + if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) {
> + block = rppx1_init_stats_block(rpp, stats,
> + RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST);
> + if (!block)
Hi Niklas
I assume you need to use IS_ERR() here, instead of null pointer check,
since v4l2_isp_stats_init_block() does return an error from this series [1].
Same applies to other stats blocks.
BR
Antoine
[1]
https://lore.kernel.org/linux-media/20260505-extensible-stats-v1-6-e16f326b8dad@ideasonboard.com/
> + return;
> +
> + rpp_module_call(&rpp->post.wbmeas, fill_stats, block);
> + }
> }
> EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> index 3d197d914d07..53263bc96280 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> @@ -56,6 +56,109 @@ static int rppx1_wbmeas_probe(struct rpp_module *mod)
> return 0;
> }
>
> +static int
> +rppx1_wbmeas_fill_params(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv)
> +{
> + const struct rppx1_wbmeas_params *cfg = &block->wbmeas;
> + u32 awb_meas_props;
> +
> + /* If the modules is disabled, simply bypass it. */
> + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
> + write(priv, mod->base + AWB_MEAS_PROP_REG, 0);
> + return 0;
> + }
> +
> + /* Program measurement window. */
> + write(priv, mod->base + AWB_MEAS_H_OFFS_REG, cfg->wnd.h_offs);
> + write(priv, mod->base + AWB_MEAS_V_OFFS_REG, cfg->wnd.v_offs);
> + write(priv, mod->base + AWB_MEAS_H_SIZE_REG, cfg->wnd.h_size);
> + write(priv, mod->base + AWB_MEAS_V_SIZE_REG, cfg->wnd.v_size);
> +
> + /* Set number of frames to sample. */
> + write(priv, mod->base + AWB_MEAS_FRAMES_REG, cfg->frames);
> +
> + if (cfg->mode == RPPX1_WBMEAS_MODE_YCBCR) {
> + write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
> + cfg->ref_cb_max_b);
> + write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
> + cfg->ref_cr_max_r);
> + write(priv, mod->base + AWB_MEAS_MAX_Y_REG, cfg->max_y);
> + write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
> + cfg->min_y_max_g);
> + write(priv, mod->base + AWB_MEAS_MAX_CSUM_REG, cfg->max_csum);
> + write(priv, mod->base + AWB_MEAS_MIN_C_REG, cfg->min_c);
> +
> + /*
> + * Program the color conversion matrix coefficients and the
> + * per-color channel offsets.
> + */
> + for (unsigned int i = 0; i < 3; i++) {
> + for (unsigned int j = 0; j < 3; j++) {
> + unsigned int index = i * 3 + j;
> +
> + write(priv,
> + mod->base + AWB_MEAS_CCOR_COEFF_REG(index),
> + cfg->ccor_coeff[i][j]);
> + }
> + }
> +
> + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG,
> + cfg->ccor_offs[0]);
> + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG,
> + cfg->ccor_offs[1]);
> + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG,
> + cfg->ccor_offs[2]);
> +
> + awb_meas_props = cfg->ymax_cmp ? AWB_MEAS_PROP_YMAX : 0;
> + } else {
> + write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
> + cfg->ref_cb_max_b);
> + write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
> + cfg->ref_cr_max_r);
> + write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
> + cfg->min_y_max_g);
> +
> + /* Bypass color conversion matrix and color offsets. */
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x1000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x0000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0x0000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x1000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0x0000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0x0000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0x0000);
> + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x1000);
> + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00000000);
> + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00000000);
> + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00000000);
> +
> + awb_meas_props = AWB_MEAS_PROP_MEAS_MODE_RGB;
> + }
> +
> + write(priv, mod->base + AWB_MEAS_PROP_REG,
> + awb_meas_props | AWB_MEAS_PROP_AWB_MODE_ON);
> +
> + return 0;
> +}
> +
> +static int rppx1_wbmeas_fill_stats(struct rpp_module *mod,
> + union rppx1_stats_block *block)
> +{
> + struct rppx1_wbmeas_stats *stats = &block->wbmeas;
> +
> + /* Return measurements at native hardware precision. */
> + stats->cnt = rpp_module_read(mod, AWB_MEAS_WHITE_CNT_REG);
> + stats->mean_y_or_g = rpp_module_read(mod, AWB_MEAS_MEAN_Y_G_REG);
> + stats->mean_cb_or_b = rpp_module_read(mod, AWB_MEAS_MEAN_CB_B_REG);
> + stats->mean_cr_or_r = rpp_module_read(mod, AWB_MEAS_MEAN_CR_R_REG);
> +
> + return 0;
> +}
> +
> const struct rpp_module_ops rppx1_wbmeas_ops = {
> .probe = rppx1_wbmeas_probe,
> + .fill_params = rppx1_wbmeas_fill_params,
> + .fill_stats = rppx1_wbmeas_fill_stats
> };
> diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
> index 26627be6f483..1d76a85164c8 100644
> --- a/include/uapi/linux/media/dreamchip/rppx1-config.h
> +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
> @@ -40,13 +40,81 @@ struct rppx1_window {
> * wider-than-8-bit results.
> */
>
> +/**
> + * enum rppx1_params_block_type - RPP-X1 extensible params block types
> + *
> + * NOTE: Only append to the enumeration as the numbers are uAPI.
> + *
> + * @RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: AWB Measurement Configuration
> + */
> +enum rppx1_params_block_type {
> + RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
> +};
> +
> +/**
> + * enum rppx1_wbmeas_mode - AWB measurement mode
> + *
> + * @RPPX1_WBMEAS_MODE_YCBCR: YCbCr measurement mode
> + * @RPPX1_WBMEAS_MODE_RGB: RGB measurement mode
> + */
> +enum rppx1_wbmeas_mode {
> + RPPX1_WBMEAS_MODE_YCBCR,
> + RPPX1_WBMEAS_MODE_RGB,
> +};
> +
> +/**
> + * struct rppx1_wbmeas_params - AWB measurement configuration
> + *
> + * The Auto-White Balance measurement module is available on the MAIN_POST pipe.
> + * It supports two measurement modes, selected by the @mode field. The
> + * measurement window is programmed through the @wnd field.
> + *
> + * To support measurement in YCbCr mode a color conversion matrix with
> + * programmable offset is available in the @ccor_coeff and @ccor_offs fields.
> + * The color conversion matrix coefficients are represented as 16 bits signed
> + * Q4.12 numbers ranging from -8 to +7.99. The per-color channel offsets are
> + * represented as 25 bits 2's complement integer numbers ranging from -16777216
> + * to +16777215.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST)
> + * @mode: measurement mode (from enum rppx1_wbmeas_mode)
> + * @ymax_cmp: enable Y_MAX compare using @max_y
> + * @wnd: measurement window
> + * @frames: number of frames for mean value calculation (0 = 1 frame)
> + * @ref_cr_max_r: reference Cr or max red value in RGB mode, 24 bits
> + * @ref_cb_max_b: reference Cb or max blue value in RGB mode, 24 bits
> + * @min_y_max_g: luminance minimum value or max green value in RGB mode, 24 bits
> + * @max_y: luminance maximum value, only valid if @mode is set to YCbCr and
> + * @ymax_cmp is set to enabled, 24 bits
> + * @max_csum: chrominance sum maximum value, 24 bits
> + * @min_c: chrominance minimum value, 24 bits
> + * @ccor_coeff: coefficients for color conversion matrix, signed 16 bits Q4.6
> + * @ccor_offs: R-G-B color conversion coefficients, signed 25 bits 2's complement
> + */
> +struct rppx1_wbmeas_params {
> + struct v4l2_isp_params_block_header header;
> + __u8 mode;
> + __u8 ymax_cmp;
> + struct rppx1_window wnd;
> + __u8 frames;
> + __u32 ref_cr_max_r;
> + __u32 ref_cb_max_b;
> + __u32 min_y_max_g;
> + __u32 max_y;
> + __u32 max_csum;
> + __u32 min_c;
> + __u16 ccor_coeff[3][3];
> + __u32 ccor_offs[3];
> +};
> +
> /**
> * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> *
> * Some types are reported twice as the same block might be instantiated in
> * multiple pipes.
> */
> -#define RPPX1_PARAMS_MAX_SIZE 0
> +#define RPPX1_PARAMS_MAX_SIZE \
> + (sizeof(struct rppx1_wbmeas_params))
>
> /* ---------------------------------------------------------------------------
> * Statistics Structures
> @@ -55,12 +123,41 @@ struct rppx1_window {
> * wider-than-8-bit results.
> */
>
> +/**
> + * enum rppx1_stats_block_type - RPP-X1 extensible stats block types
> + *
> + * NOTE: Only append to the enumeration as the numbers are uAPI.
> + *
> + * @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement
> + */
> +enum rppx1_stats_block_type {
> + RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
> +};
> +
> +/**
> + * struct rppx1_wbmeas_stats - AWB statistics
> + *
> + * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_WBMEAS)
> + * @cnt: Number of pixels matched
> + * @mean_y_or_g: mean Y (or G in RGB mode) value, 24-bit
> + * @mean_cb_or_b: mean Cb (or B in RGB mode) value, 24-bit
> + * @mean_cr_or_r: mean Cr (or R in RGB mode) value, 24-bit
> + */
> +struct rppx1_wbmeas_stats {
> + struct v4l2_isp_block_header header;
> + __u32 cnt;
> + __u32 mean_y_or_g;
> + __u32 mean_cb_or_b;
> + __u32 mean_cr_or_r;
> +};
> +
> /**
> * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
> *
> * Some types are reported twice as the same block might be instantiated in
> * multiple pipes.
> */
> -#define RPPX1_STATS_MAX_SIZE 0
> +#define RPPX1_STATS_MAX_SIZE \
> + (sizeof(struct rppx1_wbmeas_stats))
>
> #endif /* __UAPI_RPP_X1_CONFIG_H */
> --
> 2.54.0
>
>
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [v8 04/14] media: rppx1: wbmeas: Add support for white balance measurement
2026-05-06 13:58 ` Antoine Bouyer
@ 2026-05-06 14:22 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 14:22 UTC (permalink / raw)
To: Antoine Bouyer
Cc: Niklas Söderlund, Jai Luthra, Mauro Carvalho Chehab,
Kuninori Morimoto, Jacopo Mondi, Laurent Pinchart, linux-media,
linux-renesas-soc, linux-kernel
Hi Antoine
On Wed, May 06, 2026 at 03:58:30PM +0200, Antoine Bouyer wrote:
> On 5/4/26 3:05 AM, Niklas Söderlund wrote:
> > Extend the RPPX1 driver to allow setting the white balance measurement
> > configuration and consuming the resulting statistics. It uses the RPPX1
> > framework for parameters and its writer abstraction to allow the user to
> > control how, and when, configuration is applied to the RPPX1.
> >
> > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> > Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> > Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> > ---
> > .../platform/dreamchip/rppx1/rpp_module.h | 2 +
> > .../platform/dreamchip/rppx1/rpp_params.c | 4 +
> > .../platform/dreamchip/rppx1/rpp_stats.c | 18 +++
> > .../platform/dreamchip/rppx1/rppx1_wbmeas.c | 103 ++++++++++++++++++
> > .../uapi/linux/media/dreamchip/rppx1-config.h | 101 ++++++++++++++++-
> > 5 files changed, 226 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> > index 742904973e35..85fb23174e57 100644
> > --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> > +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> > @@ -85,10 +85,12 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
> >
> > union rppx1_params_block {
> > struct v4l2_isp_params_block_header header;
> > + struct rppx1_wbmeas_params wbmeas;
> > };
> >
> > union rppx1_stats_block {
> > struct v4l2_isp_params_block_header header;
> > + struct rppx1_wbmeas_stats wbmeas;
> > };
> >
> > struct rpp_module_ops {
> > diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> > index a5feb18f3bd5..1262350d2190 100644
> > --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> > +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> > @@ -16,6 +16,7 @@
> >
> > static const struct v4l2_isp_block_type_info
> > rppx1_ext_params_blocks_info[] = {
> > + RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
> > };
> >
> > int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> > @@ -50,6 +51,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> > block_offset += block->header.size;
> >
> > switch (block->header.type) {
> > + case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST:
> > + module = &rpp->post.wbmeas;
> > + break;
> > default:
> > module = NULL;
> > break;
> > diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> > index 8f43e56ba361..4c38e657e5f0 100644
> > --- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> > +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> > @@ -14,6 +14,11 @@
> > .size = sizeof(struct rppx1_ ## block ## _stats), \
> > }
> >
> > +static const struct v4l2_isp_block_type_info
> > +rppx1_stats_blocks_info[] = {
> > + RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
> > +};
> > +
> > #define rppx1_init_stats_block(rpp, buf, type) \
> > ((union rppx1_stats_block *) \
> > v4l2_isp_stats_init_block((rpp)->dev, (buf), \
> > @@ -23,5 +28,18 @@
> >
> > void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
> > {
> > + struct v4l2_isp_buffer *stats = buf;
> > + union rppx1_stats_block *block;
> > +
> > + v4l2_isp_stats_init_buffer(stats);
> > +
> > + if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) {
> > + block = rppx1_init_stats_block(rpp, stats,
> > + RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST);
> > + if (!block)
>
> Hi Niklas
>
> I assume you need to use IS_ERR() here, instead of null pointer check, since
> v4l2_isp_stats_init_block() does return an error from this series [1].
>
> Same applies to other stats blocks.
Duh, thanks for catching! This is defintely my bad and not Niklas' has
I didn't update these callers when I changed the return value of
v4l2_isp_stats_init_block() ...
>
> BR
> Antoine
>
> [1] https://lore.kernel.org/linux-media/20260505-extensible-stats-v1-6-e16f326b8dad@ideasonboard.com/
>
> > + return;
> > +
> > + rpp_module_call(&rpp->post.wbmeas, fill_stats, block);
> > + }
> > }
> > EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
> > diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> > index 3d197d914d07..53263bc96280 100644
> > --- a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> > +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
> > @@ -56,6 +56,109 @@ static int rppx1_wbmeas_probe(struct rpp_module *mod)
> > return 0;
> > }
> >
> > +static int
> > +rppx1_wbmeas_fill_params(struct rpp_module *mod,
> > + const union rppx1_params_block *block,
> > + rppx1_reg_write write, void *priv)
> > +{
> > + const struct rppx1_wbmeas_params *cfg = &block->wbmeas;
> > + u32 awb_meas_props;
> > +
> > + /* If the modules is disabled, simply bypass it. */
> > + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
> > + write(priv, mod->base + AWB_MEAS_PROP_REG, 0);
> > + return 0;
> > + }
> > +
> > + /* Program measurement window. */
> > + write(priv, mod->base + AWB_MEAS_H_OFFS_REG, cfg->wnd.h_offs);
> > + write(priv, mod->base + AWB_MEAS_V_OFFS_REG, cfg->wnd.v_offs);
> > + write(priv, mod->base + AWB_MEAS_H_SIZE_REG, cfg->wnd.h_size);
> > + write(priv, mod->base + AWB_MEAS_V_SIZE_REG, cfg->wnd.v_size);
> > +
> > + /* Set number of frames to sample. */
> > + write(priv, mod->base + AWB_MEAS_FRAMES_REG, cfg->frames);
> > +
> > + if (cfg->mode == RPPX1_WBMEAS_MODE_YCBCR) {
> > + write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
> > + cfg->ref_cb_max_b);
> > + write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
> > + cfg->ref_cr_max_r);
> > + write(priv, mod->base + AWB_MEAS_MAX_Y_REG, cfg->max_y);
> > + write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
> > + cfg->min_y_max_g);
> > + write(priv, mod->base + AWB_MEAS_MAX_CSUM_REG, cfg->max_csum);
> > + write(priv, mod->base + AWB_MEAS_MIN_C_REG, cfg->min_c);
> > +
> > + /*
> > + * Program the color conversion matrix coefficients and the
> > + * per-color channel offsets.
> > + */
> > + for (unsigned int i = 0; i < 3; i++) {
> > + for (unsigned int j = 0; j < 3; j++) {
> > + unsigned int index = i * 3 + j;
> > +
> > + write(priv,
> > + mod->base + AWB_MEAS_CCOR_COEFF_REG(index),
> > + cfg->ccor_coeff[i][j]);
> > + }
> > + }
> > +
> > + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG,
> > + cfg->ccor_offs[0]);
> > + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG,
> > + cfg->ccor_offs[1]);
> > + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG,
> > + cfg->ccor_offs[2]);
> > +
> > + awb_meas_props = cfg->ymax_cmp ? AWB_MEAS_PROP_YMAX : 0;
> > + } else {
> > + write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
> > + cfg->ref_cb_max_b);
> > + write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
> > + cfg->ref_cr_max_r);
> > + write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
> > + cfg->min_y_max_g);
> > +
> > + /* Bypass color conversion matrix and color offsets. */
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x1000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x0000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0x0000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x1000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0x0000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0x0000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0x0000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x1000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00000000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00000000);
> > + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00000000);
> > +
> > + awb_meas_props = AWB_MEAS_PROP_MEAS_MODE_RGB;
> > + }
> > +
> > + write(priv, mod->base + AWB_MEAS_PROP_REG,
> > + awb_meas_props | AWB_MEAS_PROP_AWB_MODE_ON);
> > +
> > + return 0;
> > +}
> > +
> > +static int rppx1_wbmeas_fill_stats(struct rpp_module *mod,
> > + union rppx1_stats_block *block)
> > +{
> > + struct rppx1_wbmeas_stats *stats = &block->wbmeas;
> > +
> > + /* Return measurements at native hardware precision. */
> > + stats->cnt = rpp_module_read(mod, AWB_MEAS_WHITE_CNT_REG);
> > + stats->mean_y_or_g = rpp_module_read(mod, AWB_MEAS_MEAN_Y_G_REG);
> > + stats->mean_cb_or_b = rpp_module_read(mod, AWB_MEAS_MEAN_CB_B_REG);
> > + stats->mean_cr_or_r = rpp_module_read(mod, AWB_MEAS_MEAN_CR_R_REG);
> > +
> > + return 0;
> > +}
> > +
> > const struct rpp_module_ops rppx1_wbmeas_ops = {
> > .probe = rppx1_wbmeas_probe,
> > + .fill_params = rppx1_wbmeas_fill_params,
> > + .fill_stats = rppx1_wbmeas_fill_stats
> > };
> > diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
> > index 26627be6f483..1d76a85164c8 100644
> > --- a/include/uapi/linux/media/dreamchip/rppx1-config.h
> > +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
> > @@ -40,13 +40,81 @@ struct rppx1_window {
> > * wider-than-8-bit results.
> > */
> >
> > +/**
> > + * enum rppx1_params_block_type - RPP-X1 extensible params block types
> > + *
> > + * NOTE: Only append to the enumeration as the numbers are uAPI.
> > + *
> > + * @RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: AWB Measurement Configuration
> > + */
> > +enum rppx1_params_block_type {
> > + RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
> > +};
> > +
> > +/**
> > + * enum rppx1_wbmeas_mode - AWB measurement mode
> > + *
> > + * @RPPX1_WBMEAS_MODE_YCBCR: YCbCr measurement mode
> > + * @RPPX1_WBMEAS_MODE_RGB: RGB measurement mode
> > + */
> > +enum rppx1_wbmeas_mode {
> > + RPPX1_WBMEAS_MODE_YCBCR,
> > + RPPX1_WBMEAS_MODE_RGB,
> > +};
> > +
> > +/**
> > + * struct rppx1_wbmeas_params - AWB measurement configuration
> > + *
> > + * The Auto-White Balance measurement module is available on the MAIN_POST pipe.
> > + * It supports two measurement modes, selected by the @mode field. The
> > + * measurement window is programmed through the @wnd field.
> > + *
> > + * To support measurement in YCbCr mode a color conversion matrix with
> > + * programmable offset is available in the @ccor_coeff and @ccor_offs fields.
> > + * The color conversion matrix coefficients are represented as 16 bits signed
> > + * Q4.12 numbers ranging from -8 to +7.99. The per-color channel offsets are
> > + * represented as 25 bits 2's complement integer numbers ranging from -16777216
> > + * to +16777215.
> > + *
> > + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST)
> > + * @mode: measurement mode (from enum rppx1_wbmeas_mode)
> > + * @ymax_cmp: enable Y_MAX compare using @max_y
> > + * @wnd: measurement window
> > + * @frames: number of frames for mean value calculation (0 = 1 frame)
> > + * @ref_cr_max_r: reference Cr or max red value in RGB mode, 24 bits
> > + * @ref_cb_max_b: reference Cb or max blue value in RGB mode, 24 bits
> > + * @min_y_max_g: luminance minimum value or max green value in RGB mode, 24 bits
> > + * @max_y: luminance maximum value, only valid if @mode is set to YCbCr and
> > + * @ymax_cmp is set to enabled, 24 bits
> > + * @max_csum: chrominance sum maximum value, 24 bits
> > + * @min_c: chrominance minimum value, 24 bits
> > + * @ccor_coeff: coefficients for color conversion matrix, signed 16 bits Q4.6
> > + * @ccor_offs: R-G-B color conversion coefficients, signed 25 bits 2's complement
> > + */
> > +struct rppx1_wbmeas_params {
> > + struct v4l2_isp_params_block_header header;
> > + __u8 mode;
> > + __u8 ymax_cmp;
> > + struct rppx1_window wnd;
> > + __u8 frames;
> > + __u32 ref_cr_max_r;
> > + __u32 ref_cb_max_b;
> > + __u32 min_y_max_g;
> > + __u32 max_y;
> > + __u32 max_csum;
> > + __u32 min_c;
> > + __u16 ccor_coeff[3][3];
> > + __u32 ccor_offs[3];
> > +};
> > +
> > /**
> > * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> > *
> > * Some types are reported twice as the same block might be instantiated in
> > * multiple pipes.
> > */
> > -#define RPPX1_PARAMS_MAX_SIZE 0
> > +#define RPPX1_PARAMS_MAX_SIZE \
> > + (sizeof(struct rppx1_wbmeas_params))
> >
> > /* ---------------------------------------------------------------------------
> > * Statistics Structures
> > @@ -55,12 +123,41 @@ struct rppx1_window {
> > * wider-than-8-bit results.
> > */
> >
> > +/**
> > + * enum rppx1_stats_block_type - RPP-X1 extensible stats block types
> > + *
> > + * NOTE: Only append to the enumeration as the numbers are uAPI.
> > + *
> > + * @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement
> > + */
> > +enum rppx1_stats_block_type {
> > + RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
> > +};
> > +
> > +/**
> > + * struct rppx1_wbmeas_stats - AWB statistics
> > + *
> > + * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_WBMEAS)
> > + * @cnt: Number of pixels matched
> > + * @mean_y_or_g: mean Y (or G in RGB mode) value, 24-bit
> > + * @mean_cb_or_b: mean Cb (or B in RGB mode) value, 24-bit
> > + * @mean_cr_or_r: mean Cr (or R in RGB mode) value, 24-bit
> > + */
> > +struct rppx1_wbmeas_stats {
> > + struct v4l2_isp_block_header header;
> > + __u32 cnt;
> > + __u32 mean_y_or_g;
> > + __u32 mean_cb_or_b;
> > + __u32 mean_cr_or_r;
> > +};
> > +
> > /**
> > * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
> > *
> > * Some types are reported twice as the same block might be instantiated in
> > * multiple pipes.
> > */
> > -#define RPPX1_STATS_MAX_SIZE 0
> > +#define RPPX1_STATS_MAX_SIZE \
> > + (sizeof(struct rppx1_wbmeas_stats))
> >
> > #endif /* __UAPI_RPP_X1_CONFIG_H */
> > --
> > 2.54.0
> >
> >
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [v8 05/14] media: rppx1: awbg: Add support for white balance gain settings
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (3 preceding siblings ...)
2026-05-04 1:05 ` [v8 04/14] media: rppx1: wbmeas: Add support for white balance measurement Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-04 1:05 ` [v8 06/14] media: rppx1: exm: Add support for exposure measurement Niklas Söderlund
` (9 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the white balance gain
configuration parameters. It uses the RPPX1 framework for parameters and
its writer abstraction to allow the user to control how, and when,
configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 1 +
.../platform/dreamchip/rppx1/rpp_params.c | 5 +++
.../platform/dreamchip/rppx1/rppx1_awbg.c | 32 +++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 40 ++++++++++++++++++-
4 files changed, 77 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 85fb23174e57..76adfe95777d 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -85,6 +85,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
union rppx1_params_block {
struct v4l2_isp_params_block_header header;
+ struct rppx1_awbg_params awbg;
struct rppx1_wbmeas_params wbmeas;
};
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 1262350d2190..0ac9bf1f9a65 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -16,6 +16,8 @@
static const struct v4l2_isp_block_type_info
rppx1_ext_params_blocks_info[] = {
+ RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
};
@@ -51,6 +53,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
block_offset += block->header.size;
switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
+ module = &rpp->pre1.awbg;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST:
module = &rpp->post.wbmeas;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
index e20bc369ca8c..dcccb1a2e28f 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
@@ -25,6 +25,38 @@ static int rppx1_awbg_probe(struct rpp_module *mod)
return 0;
}
+static int
+rppx1_awbg_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_awbg_params *cfg = &block->awbg;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + AWB_ENABLE_REG, 0);
+ return 0;
+ }
+
+ /*
+ * RPP gains are 18-bit with 12 bit fractional part and 0x1000 = 1.0,
+ * giving a possible range of 0.0 to 64.0. NOTE: RPP documentation is
+ * contradictory this is the register definition, the function
+ * description states 0x400 = 1.0 AND 18-bit with 12 fractional bits,
+ * which is not possible...
+ */
+
+ write(priv, mod->base + AWB_GAIN_GR_REG, cfg->gain_green_r);
+ write(priv, mod->base + AWB_GAIN_GB_REG, cfg->gain_green_b);
+ write(priv, mod->base + AWB_GAIN_R_REG, cfg->gain_red);
+ write(priv, mod->base + AWB_GAIN_B_REG, cfg->gain_blue);
+
+ write(priv, mod->base + AWB_ENABLE_REG, AWB_ENABLE_AWB_GAIN_EN);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_awbg_ops = {
.probe = rppx1_awbg_probe,
+ .fill_params = rppx1_awbg_fill_params,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 1d76a85164c8..e96308b4e308 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -46,9 +46,15 @@ struct rppx1_window {
* NOTE: Only append to the enumeration as the numbers are uAPI.
*
* @RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: AWB Measurement Configuration
+ * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: PRE1 pipe White Balance Gains
+ * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2: PRE2 White Balance Gains
+ * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST: MAIN_POST White Balance Gains
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
+ RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1,
+ RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2,
+ RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST,
};
/**
@@ -107,6 +113,35 @@ struct rppx1_wbmeas_params {
__u32 ccor_offs[3];
};
+/**
+ * struct rppx1_awbg_params - WB gain configuration
+ *
+ * The RPP-X1 White Balance Gain module is available in the PRE1 and PRE2
+ * pre-fusion pipes and in the MAIN_POST post-fusion pipe. Userspace selects
+ * which pipe to operate by setting the @header.type field to
+ * RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1, RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2
+ * or RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST.
+ *
+ * The White Balance module allows to specify per-color channel gains, expressed
+ * as unsigned fixed-point values as 18 bits unsigned integers in Q6.12 format
+ * with a maximum of 63.999.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1 or
+ * type = RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2 or
+ * type = RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST)
+ * @gain_red: gain for red component, 18-bit (unsigned Q6.12)
+ * @gain_green_r: gain for green component in red lines, 18-bit (unsigned Q6.12)
+ * @gain_blue: gain for blue component, 18-bit (unsigned Q6.12)
+ * @gain_green_b: gain for green component in blue lines, 18-bit (unsigned Q6.12)
+ */
+struct rppx1_awbg_params {
+ struct v4l2_isp_params_block_header header;
+ __u32 gain_red;
+ __u32 gain_green_r;
+ __u32 gain_blue;
+ __u32 gain_green_b;
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -114,7 +149,10 @@ struct rppx1_wbmeas_params {
* multiple pipes.
*/
#define RPPX1_PARAMS_MAX_SIZE \
- (sizeof(struct rppx1_wbmeas_params))
+ (sizeof(struct rppx1_wbmeas_params) + \
+ sizeof(struct rppx1_awbg_params) + \
+ sizeof(struct rppx1_awbg_params) + \
+ sizeof(struct rppx1_awbg_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [v8 06/14] media: rppx1: exm: Add support for exposure measurement
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (4 preceding siblings ...)
2026-05-04 1:05 ` [v8 05/14] media: rppx1: awbg: Add support for white balance gain settings Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-04 1:05 ` [v8 07/14] media: rppx1: hist: Add support histogram measurement Niklas Söderlund
` (8 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the exposure measurement
configuration and consuming the resulting statistics. It uses the RPPX1
framework for parameters and its writer abstraction to allow the user to
control how, and when, configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 2 +
.../platform/dreamchip/rppx1/rpp_params.c | 5 +
.../platform/dreamchip/rppx1/rpp_stats.c | 10 ++
.../platform/dreamchip/rppx1/rppx1_exm.c | 78 ++++++++++-
.../uapi/linux/media/dreamchip/rppx1-config.h | 121 +++++++++++++++++-
5 files changed, 213 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 76adfe95777d..2dc9e54027e1 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -86,11 +86,13 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
union rppx1_params_block {
struct v4l2_isp_params_block_header header;
struct rppx1_awbg_params awbg;
+ struct rppx1_exm_params exm;
struct rppx1_wbmeas_params wbmeas;
};
union rppx1_stats_block {
struct v4l2_isp_params_block_header header;
+ struct rppx1_exm_stats exm;
struct rppx1_wbmeas_stats wbmeas;
};
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 0ac9bf1f9a65..23094e8ce3a7 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -18,6 +18,8 @@ static const struct v4l2_isp_block_type_info
rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm),
+ RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm),
RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
};
@@ -56,6 +58,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
module = &rpp->pre1.awbg;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1:
+ module = &rpp->pre1.exm;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST:
module = &rpp->post.wbmeas;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
index 4c38e657e5f0..0fddf50f7222 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -16,6 +16,7 @@
static const struct v4l2_isp_block_type_info
rppx1_stats_blocks_info[] = {
+ RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm),
RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
};
@@ -33,6 +34,15 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
v4l2_isp_stats_init_buffer(stats);
+ if (isc & RPPX1_IRQ_ID_PRE1_EXM) {
+ block = rppx1_init_stats_block(rpp, stats,
+ RPPX1_STATS_BLOCK_TYPE_EXM_PRE1);
+ if (!block)
+ return;
+
+ rpp_module_call(&rpp->pre1.exm, fill_stats, block);
+ }
+
if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) {
block = rppx1_init_stats_block(rpp, stats,
RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
index 0c40300e13ad..1f2e740a3bd0 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
@@ -10,10 +10,14 @@
#define EXM_START_REG 0x0004
#define EXM_CTRL_REG 0x0008
+#define EXM_CTRL_EXM_AUTOSTOP BIT(1) /* HW doc says not supported. */
#define EXM_CTRL_EXM_UPDATE_ENABLE BIT(0)
#define EXM_MODE_REG 0x000c
+
#define EXM_CHANNEL_SEL_REG 0x0010
+#define EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0)
+
#define EXM_LAST_MEAS_LINE_REG 0x0014
#define EXM_COEFF_R_REG 0x0018
#define EXM_COEFF_G_GR_REG 0x001c
@@ -26,7 +30,6 @@
#define EXM_FORCED_UPD_START_LINE_REG 0x0038
#define EXM_VSTART_STATUS_REG 0x003c
-#define EXM_MEAN_REG_NUM 25
#define EXM_MEAN_REG(n) (0x0040 + (4 * (n)))
static int rppx1_exm_probe(struct rpp_module *mod)
@@ -46,6 +49,79 @@ static int rppx1_exm_probe(struct rpp_module *mod)
return 0;
}
+static int
+rppx1_exm_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_exm_params *cfg = &block->exm;
+ u32 h_offs, v_offs, h_size, v_size;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + EXM_MODE_REG, 0);
+ return 0;
+ }
+
+ switch (cfg->mode) {
+ case RPPX1_EXP_MEASURING_MODE_RGB:
+ case RPPX1_EXP_MEASURING_MODE_BAYER:
+ write(priv, mod->base + EXM_MODE_REG, cfg->mode);
+ break;
+ default:
+ write(priv, mod->base + EXM_MODE_REG, 0);
+ return 0;
+ }
+
+ write(priv, mod->base + EXM_COEFF_R_REG, cfg->coeff_r);
+ write(priv, mod->base + EXM_COEFF_G_GR_REG, cfg->coeff_g_gr);
+ write(priv, mod->base + EXM_COEFF_GB_REG, cfg->coeff_gb);
+ write(priv, mod->base + EXM_COEFF_B_REG, cfg->coeff_b);
+
+ /* Select sample point */
+ write(priv, mod->base + EXM_CHANNEL_SEL_REG,
+ cfg->channel_sel & EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK);
+
+ /*
+ * Adjust and set measurement window to hardware limitations,
+ * - Offsets must be even.
+ * - Width and height must be divisible by 10.
+ */
+ h_offs = cfg->wnd.h_offs & 0x1ffe;
+ v_offs = cfg->wnd.v_offs & 0x1ffe;
+ h_size = (cfg->wnd.h_size - 1) - ((cfg->wnd.h_size - 1) % 10);
+ v_size = (cfg->wnd.v_size - 1) - ((cfg->wnd.v_size - 1) % 10);
+
+ write(priv, mod->base + EXM_H_OFFS_REG, h_offs);
+ write(priv, mod->base + EXM_V_OFFS_REG, v_offs);
+ write(priv, mod->base + EXM_H_SIZE_REG, h_size / 5);
+ write(priv, mod->base + EXM_V_SIZE_REG, v_size / 5);
+
+ /*
+ * Set last measurement line for ready interrupt. Ignore the value
+ * from the parameters as it is only useful for fast-channel switching.
+ */
+ write(priv, mod->base + EXM_LAST_MEAS_LINE_REG, v_offs + v_size + 1);
+
+ write(priv, mod->base + EXM_START_REG, 1);
+
+ return 0;
+}
+
+static int rppx1_exm_fill_stats(struct rpp_module *mod,
+ union rppx1_stats_block *block)
+{
+ struct rppx1_exm_stats *stats = &block->exm;
+
+ /* Return measurements at native hardware precision. */
+ for (unsigned int i = 0; i < RPPX1_EXM_NUM_WIN; i++)
+ stats->exp_mean[i] = rpp_module_read(mod, EXM_MEAN_REG(i));
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_exm_ops = {
.probe = rppx1_exm_probe,
+ .fill_params = rppx1_exm_fill_params,
+ .fill_stats = rppx1_exm_fill_stats,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index e96308b4e308..18e718d43f80 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -33,6 +33,36 @@ struct rppx1_window {
__u16 v_size;
};
+/**
+ * enum rppx1_meas_chan - Measurement point for the Histogram and EXM Modules
+ *
+ * Measurement points for the RPP-X1 Histogram measurement module and Exposure
+ * measurement module.
+ *
+ * All tap points are available for the PRE1/PRE2 pipes. Only
+ * RPPX1_MEAS_CHAN_SEL4 and RPPX1_MEAS_CHAN_SEL7 are available for the
+ * MAIN_POST pipe.
+ *
+ * @RPPX1_MEAS_CHAN_SEL0: after input acquisition
+ * @RPPX1_MEAS_CHAN_SEL1: after black level subtraction
+ * @RPPX1_MEAS_CHAN_SEL2: after sensor gamma linearization
+ * @RPPX1_MEAS_CHAN_SEL3: after lens shading correction
+ * @RPPX1_MEAS_CHAN_SEL4: after auto white balance gains
+ * @RPPX1_MEAS_CHAN_SEL5: after defect pixel correction
+ * @RPPX1_MEAS_CHAN_SEL6: after denoise pre-filter
+ * @RPPX1_MEAS_CHAN_SEL7: after demosaicing
+ */
+enum rppx1_meas_chan {
+ RPPX1_MEAS_CHAN_SEL0,
+ RPPX1_MEAS_CHAN_SEL1,
+ RPPX1_MEAS_CHAN_SEL2,
+ RPPX1_MEAS_CHAN_SEL3,
+ RPPX1_MEAS_CHAN_SEL4,
+ RPPX1_MEAS_CHAN_SEL5,
+ RPPX1_MEAS_CHAN_SEL6,
+ RPPX1_MEAS_CHAN_SEL7,
+};
+
/* ---------------------------------------------------------------------------
* Parameter Structures
*
@@ -49,12 +79,16 @@ struct rppx1_window {
* @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: PRE1 pipe White Balance Gains
* @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2: PRE2 White Balance Gains
* @RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST: MAIN_POST White Balance Gains
+ * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1: PRE1 pipe Exposure Measurement
+ * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2: PRE2 pipe Exposure Measurement
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1,
RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2,
RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST,
+ RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1,
+ RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2,
};
/**
@@ -142,6 +176,64 @@ struct rppx1_awbg_params {
__u32 gain_green_b;
};
+/**
+ * enum rppx1_exm_mode - Exposure measurement mode
+ *
+ * Exaposure measurement mode selection (RGB/Bayer).
+ *
+ * @RPPX1_EXP_MEASURING_MODE_DISABLED: no measurement
+ * @RPPX1_EXP_MEASURING_MODE_RGB: Y/R/G/B measurement
+ * @RPPX1_EXP_MEASURING_MODE_BAYER: Bayer RGB measurement
+ */
+enum rppx1_exm_mode {
+ RPPX1_EXP_MEASURING_MODE_DISABLED,
+ RPPX1_EXP_MEASURING_MODE_RGB,
+ RPPX1_EXP_MEASURING_MODE_BAYER,
+};
+
+/**
+ * struct rppx1_exm_params - Exposure measurement configuration
+ *
+ * The RPP-X1 Exposure measurement unit is available on the PRE1 and PRE2
+ * pre-fusion pipes. Userspace selects which pipe to operate by setting
+ * the @header.type field to RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1 or
+ * RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2.
+ *
+ * Exposure measurement is performed in the RGB or Bayer domain, according to
+ * the setting of the @mode field. The exposure measurement tap point is
+ * selected according to the value of @channel_sel.
+ *
+ * The exposure measurement is performed on an input window specified in @wnd.
+ * To each color component a programmable weight coefficient is associated.
+ * Coefficients are represented as unsigned 8 bits integer values in Q1.7 format
+ * ranging from 0 to 1.992.
+ *
+ * The @last_line fields controls when the exposure measurement completes. It
+ * is usually programmed to the value of (@wnd.v_offs + @wnd.v_size + 1).
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1 or
+ * type = RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2)
+ * @mode: exposure measure mode (from enum rppx1_exm_mode)
+ * @channel_sel: exposure measurement point (see enum rppx1_meas_chan)
+ * @last_line: line number for which the exposure measurement completes
+ * @wnd: measurement window coordinates
+ * @coeff_r: coefficient for the red Bayer sample or red color channel, Q1.7
+ * @coeff_g_gr: coefficient for the green/red Bayer sample or green color channel, Q1.7
+ * @coeff_b: coefficient for the blue Bayer sample or blue color channel, Q1.7
+ * @coeff_gb: coefficient for the green/blue Bayer sample, unused in RGB mode, Q1.7
+ */
+struct rppx1_exm_params {
+ struct v4l2_isp_params_block_header header;
+ __u32 mode;
+ __u8 channel_sel;
+ __u32 last_line;
+ struct rppx1_window wnd;
+ __u8 coeff_r;
+ __u8 coeff_g_gr;
+ __u8 coeff_b;
+ __u8 coeff_gb;
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -152,7 +244,9 @@ struct rppx1_awbg_params {
(sizeof(struct rppx1_wbmeas_params) + \
sizeof(struct rppx1_awbg_params) + \
sizeof(struct rppx1_awbg_params) + \
- sizeof(struct rppx1_awbg_params))
+ sizeof(struct rppx1_awbg_params) + \
+ sizeof(struct rppx1_exm_params) + \
+ sizeof(struct rppx1_exm_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
@@ -167,9 +261,13 @@ struct rppx1_awbg_params {
* NOTE: Only append to the enumeration as the numbers are uAPI.
*
* @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement
+ * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE1: pre-fusion pipe1 exposure measurement
+ * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE2: pre-fusion pipe2 exposure measurement
*/
enum rppx1_stats_block_type {
RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
+ RPPX1_STATS_BLOCK_TYPE_EXM_PRE1,
+ RPPX1_STATS_BLOCK_TYPE_EXM_PRE2,
};
/**
@@ -189,6 +287,23 @@ struct rppx1_wbmeas_stats {
__u32 mean_cr_or_r;
};
+/* Exposure Measurement */
+#define RPPX1_EXM_NUM_WIN 25
+
+/**
+ * struct rppx1_exm_stats - Exposure measurement
+ *
+ * RPP-X1 exposure measurement calculates the mean value on 25 programmable
+ * windows on the input picture.
+ *
+ * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_EXM_PRE1)
+ * @exp_mean: mean luminance values per block, up to 20-bit
+ */
+struct rppx1_exm_stats {
+ struct v4l2_isp_block_header header;
+ __u32 exp_mean[RPPX1_EXM_NUM_WIN];
+};
+
/**
* RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
*
@@ -196,6 +311,8 @@ struct rppx1_wbmeas_stats {
* multiple pipes.
*/
#define RPPX1_STATS_MAX_SIZE \
- (sizeof(struct rppx1_wbmeas_stats))
+ (sizeof(struct rppx1_wbmeas_stats) + \
+ sizeof(struct rppx1_exm_stats) + \
+ sizeof(struct rppx1_exm_stats))
#endif /* __UAPI_RPP_X1_CONFIG_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [v8 07/14] media: rppx1: hist: Add support histogram measurement
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (5 preceding siblings ...)
2026-05-04 1:05 ` [v8 06/14] media: rppx1: exm: Add support for exposure measurement Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 15:51 ` Jacopo Mondi
2026-05-04 1:05 ` [v8 08/14] media: rppx1: bls: Add support for black level compensation Niklas Söderlund
` (7 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the histogram measurement
configuration and consuming the resulting statistics. It uses the RPPX1
framework for parameters and its writer abstraction to allow the user to
control how, and when, configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 2 +
.../platform/dreamchip/rppx1/rpp_params.c | 6 +
.../platform/dreamchip/rppx1/rpp_stats.c | 10 ++
.../platform/dreamchip/rppx1/rppx1_hist.c | 115 +++++++++++++++++-
.../uapi/linux/media/dreamchip/rppx1-config.h | 115 +++++++++++++++++-
5 files changed, 245 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 2dc9e54027e1..5725243d0119 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -86,12 +86,14 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
union rppx1_params_block {
struct v4l2_isp_params_block_header header;
struct rppx1_awbg_params awbg;
+ struct rppx1_hist_params hist;
struct rppx1_exm_params exm;
struct rppx1_wbmeas_params wbmeas;
};
union rppx1_stats_block {
struct v4l2_isp_params_block_header header;
+ struct rppx1_hist_stats hist;
struct rppx1_exm_stats exm;
struct rppx1_wbmeas_stats wbmeas;
};
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 23094e8ce3a7..6472bec6fba3 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -18,6 +18,9 @@ static const struct v4l2_isp_block_type_info
rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
+ RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist),
+ RPPX1_PARAMS_BLOCK_INFO(HIST_POST, hist),
RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm),
RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm),
RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
@@ -58,6 +61,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
module = &rpp->pre1.awbg;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_HIST_POST:
+ module = &rpp->post.hist;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1:
module = &rpp->pre1.exm;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
index 0fddf50f7222..b265e858cfd1 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -16,6 +16,7 @@
static const struct v4l2_isp_block_type_info
rppx1_stats_blocks_info[] = {
+ RPPX1_STATS_BLOCK_INFO(HIST_POST, hist),
RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm),
RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
};
@@ -34,6 +35,15 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
v4l2_isp_stats_init_buffer(stats);
+ if (isc & RPPX1_IRQ_ID_POST_HIST_MEAS) {
+ block = rppx1_init_stats_block(rpp, stats,
+ RPPX1_STATS_BLOCK_TYPE_HIST_POST);
+ if (!block)
+ return;
+
+ rpp_module_call(&rpp->post.hist, fill_stats, block);
+ }
+
if (isc & RPPX1_IRQ_ID_PRE1_EXM) {
block = rppx1_init_stats_block(rpp, stats,
RPPX1_STATS_BLOCK_TYPE_EXM_PRE1);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
index cab498ece5a8..99044dc6c115 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
@@ -25,6 +25,9 @@
#define HIST_LAST_MEAS_LINE_REG 0x0010
#define HIST_SUBSAMPLING_REG 0x0014
+#define HIST_SUBSAMPLING_V_STEPSIZE(x) (((x) & 0x7f) << 24)
+#define HIST_SUBSAMPLING_H_STEP_INC(x) (((x) & 0x1ffff))
+
#define HIST_COEFF_R_REG 0x0018
#define HIST_COEFF_G_REG 0x001c
#define HIST_COEFF_B_REG 0x0020
@@ -48,7 +51,6 @@
#define HIST_FORCED_UPDATE_REG 0x0058
#define HIST_VSTART_STATUS_REG 0x005c
-#define HIST_BIN_REG_NUM 32
#define HIST_BIN_REG(n) (0x0060 + (4 * (n)))
static int rppx1_hist_probe(struct rpp_module *mod)
@@ -71,6 +73,117 @@ static int rppx1_hist_probe(struct rpp_module *mod)
return 0;
}
+#define RPPX1_HIST_WEIGHT(v0, v1, v2, v3) \
+ (((v0) & 0x1f) | (((v1) & 0x1f) << 8) | \
+ (((v2) & 0x1f) << 16) | \
+ (((v3) & 0x1f) << 24))
+
+static int rppx1_hist_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_hist_params *cfg = &block->hist;
+ u32 h_offs, v_offs, h_size, v_size;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + HIST_MODE_REG,
+ HIST_MODE_HIST_MODE_DISABLE);
+ return 0;
+ }
+
+ /* Select sample point */
+ write(priv, mod->base + HIST_CHANNEL_SEL_REG,
+ cfg->channel_sel & HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK);
+
+ /*
+ * Configure the input subsampling.
+ *
+ * v_stepsize controls which lines are processed. In Bayer mode the
+ * effective value is double to account for the 2x2 macro-pixel size.
+ *
+ * h_step_inc is the horizontal pixel increment counter. The subsampling
+ * counter is incremented by h_step_inc. When the result of the
+ * increment overflows 2^16 a sampling is performed. In Bayer mode the
+ * subsampling counter is only incremented for color channels selected
+ * by hist_mode.
+ */
+ write(priv, mod->base + HIST_SUBSAMPLING_REG,
+ HIST_SUBSAMPLING_V_STEPSIZE(cfg->v_stepsize) |
+ HIST_SUBSAMPLING_H_STEP_INC(cfg->h_step_inc));
+
+ /*
+ * Adjust and set measurement window to hardware limitations,
+ * - Offsets must be even.
+ * - Width and height must be divisible by 10.
+ */
+ h_offs = cfg->wnd.h_offs & 0x1ffe;
+ v_offs = cfg->wnd.v_offs & 0x1ffe;
+ h_size = cfg->wnd.h_size - cfg->wnd.h_size % 10;
+ v_size = cfg->wnd.v_size - cfg->wnd.v_size % 10;
+
+ write(priv, mod->base + HIST_H_OFFS_REG, h_offs);
+ write(priv, mod->base + HIST_V_OFFS_REG, v_offs);
+ write(priv, mod->base + HIST_H_SIZE_REG, h_size / 5);
+ write(priv, mod->base + HIST_V_SIZE_REG, v_size / 5);
+
+ /*
+ * Set last measurement line for ready interrupt. Ignore the value
+ * from the parameters as it is only useful for fast-channel switching.
+ */
+ write(priv, mod->base + HIST_LAST_MEAS_LINE_REG, v_offs + v_size + 1);
+
+ /* Set measurement window weights. */
+ write(priv, mod->base + HIST_WEIGHT_00TO30_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[0], cfg->weights[1],
+ cfg->weights[2], cfg->weights[3]));
+ write(priv, mod->base + HIST_WEIGHT_40TO21_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[4], cfg->weights[5],
+ cfg->weights[6], cfg->weights[7]));
+ write(priv, mod->base + HIST_WEIGHT_31TO12_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[8], cfg->weights[9],
+ cfg->weights[10], cfg->weights[11]));
+ write(priv, mod->base + HIST_WEIGHT_22TO03_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[12], cfg->weights[13],
+ cfg->weights[14], cfg->weights[15]));
+ write(priv, mod->base + HIST_WEIGHT_13TO43_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[16], cfg->weights[17],
+ cfg->weights[18], cfg->weights[19]));
+ write(priv, mod->base + HIST_WEIGHT_04TO34_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[20], cfg->weights[21],
+ cfg->weights[22], cfg->weights[23]));
+ write(priv, mod->base + HIST_WEIGHT_44_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[24], 0, 0, 0));
+
+ write(priv, mod->base + HIST_MODE_REG, cfg->mode);
+ write(priv, mod->base + HIST_COEFF_R_REG, cfg->coeff[0]);
+ write(priv, mod->base + HIST_COEFF_G_REG, cfg->coeff[1]);
+ write(priv, mod->base + HIST_COEFF_B_REG, cfg->coeff[2]);
+
+ u32 sample_reg = FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK,
+ cfg->sample_shift) |
+ FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK,
+ cfg->sample_offs);
+ write(priv, mod->base + HIST_SAMPLE_RANGE_REG, sample_reg);
+
+ write(priv, mod->base + HIST_FORCED_UPDATE_REG, 1);
+
+ return 0;
+}
+
+static int rppx1_hist_fill_stats(struct rpp_module *mod,
+ union rppx1_stats_block *block)
+{
+ struct rppx1_hist_stats *stats = &block->hist;
+
+ for (unsigned int i = 0; i < RPPX1_HIST_NUM_BINS; i++)
+ stats->hist_bins[i] = rpp_module_read(mod, HIST_BIN_REG(i)) & 0xfffff;
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_hist_ops = {
.probe = rppx1_hist_probe,
+ .fill_params = rppx1_hist_fill_params,
+ .fill_stats = rppx1_hist_fill_stats,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 18e718d43f80..909a10935772 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -81,6 +81,9 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST: MAIN_POST White Balance Gains
* @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1: PRE1 pipe Exposure Measurement
* @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2: PRE2 pipe Exposure Measurement
+ * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1: PRE1 pipe Histogram Measurement
+ * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2: PRE2 pipe Histogram Measurement
+ * @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -89,6 +92,9 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST,
RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1,
RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2,
+ RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
+ RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2,
+ RPPX1_PARAMS_BLOCK_TYPE_HIST_POST,
};
/**
@@ -234,6 +240,85 @@ struct rppx1_exm_params {
__u8 coeff_gb;
};
+/* Histogram */
+#define RPPX1_HIST_WEIGHT_GRIDS_SIZE 25
+
+/**
+ * enum rppx1_hist_mode - Histogram measurement mode
+ *
+ * Histogram measurement mode. Select which channel or combination of channels
+ * the histogram measurement is performed on.
+ *
+ * @RPPX1_HIST_MODE_DISABLE: histogram disabled
+ * @RPPX1_HIST_MODE_RGB_COMBINED: combined RGB histogram
+ * @RPPX1_HIST_MODE_R_HISTOGRAM: red channel histogram
+ * @RPPX1_HIST_MODE_GR_HISTOGRAM: green/red channel histogram
+ * @RPPX1_HIST_MODE_B_HISTOGRAM: blue channel histogram
+ * @RPPX1_HIST_MODE_GB_HISTOGRAM: green/blue histogram
+ */
+enum rppx1_hist_mode {
+ RPPX1_HIST_MODE_DISABLE,
+ RPPX1_HIST_MODE_RGB_COMBINED,
+ RPPX1_HIST_MODE_R_HISTOGRAM,
+ RPPX1_HIST_MODE_GR_HISTOGRAM,
+ RPPX1_HIST_MODE_B_HISTOGRAM,
+ RPPX1_HIST_MODE_GB_HISTOGRAM,
+};
+
+/**
+ * struct rppx1_hist_params - Histogram measurement configuration
+ *
+ * The RPP-X1 Histogram measurement unit is available on the PRE1, PRE2 and
+ * MAIN_POST pipes. Userspace selects which pipe to operate by setting the
+ * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
+ * RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2 or
+ * RPPX1_PARAMS_BLOCK_TYPE_HIST_POST.
+ *
+ * The histogram measurement point is selected using the @channel field while
+ * histogram measurement mode is selected using the @mode field.
+ *
+ * Histogram measurement is performed by programming subsampling factors using
+ * the @v_stepsize and @h_step_inc fields and by weighted windowing, by
+ * programming the size of the measurement window @wnd with @weights associated
+ * to each cell of the 5x5 measurement grid. Weights are represented as 5 bits
+ * integer values ranging from 0 to 16.
+ *
+ * The @last_line fields controls when the histogram measurement completes. It
+ * is usually programmed to the value of (@wnd.v_offs + @wnd.v_size - 1).
+ *
+ * Histogram values are calculated by applying a per-color channel coefficient
+ * represented as an 8 bits unsigned Q1.7 integer value. The @sample_offs and
+ * @sample_shift fields allow to reduce the color dynamic range on which
+ * histogram data are produced.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
+ * type = RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2 or
+ * type = RPPX1_PARAMS_BLOCK_TYPE_HIST_POST)
+ * @mode: histogram measurement mode (from enum rppx1_hist_mode)
+ * @channel_sel: histogram measurement point (see enum rppx1_meas_chan)
+ * @wnd: measurement window coordinates
+ * @weights: weighting factors for each sub-window (5x5 grid)
+ * @last_line: line number for which the histogram measurement completes
+ * @v_stepsize: vertical subsampling divider, 7 bits
+ * @h_step_inc: horizontal subsampling step counter, 17 bits
+ * @coeff: R-G-B coefficients, 8 bits unsigned Q1.7
+ * @sample_offs: sample offset, 24 bits
+ * @sample_shift: sample shift, 4 bits
+ */
+struct rppx1_hist_params {
+ struct v4l2_isp_params_block_header header;
+ __u8 mode;
+ __u8 channel_sel;
+ struct rppx1_window wnd;
+ __u8 weights[RPPX1_HIST_WEIGHT_GRIDS_SIZE];
+ __u32 last_line;
+ __u32 v_stepsize;
+ __u32 h_step_inc;
+ __u8 coeff[3];
+ __u32 sample_offs;
+ __u8 sample_shift;
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -246,7 +331,10 @@ struct rppx1_exm_params {
sizeof(struct rppx1_awbg_params) + \
sizeof(struct rppx1_awbg_params) + \
sizeof(struct rppx1_exm_params) + \
- sizeof(struct rppx1_exm_params))
+ sizeof(struct rppx1_exm_params) + \
+ sizeof(struct rppx1_hist_params) + \
+ sizeof(struct rppx1_hist_params) + \
+ sizeof(struct rppx1_hist_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
@@ -263,11 +351,17 @@ struct rppx1_exm_params {
* @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement
* @RPPX1_STATS_BLOCK_TYPE_EXM_PRE1: pre-fusion pipe1 exposure measurement
* @RPPX1_STATS_BLOCK_TYPE_EXM_PRE2: pre-fusion pipe2 exposure measurement
+ * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE1: pre-fusion pipe1 histogram
+ * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE2: pre-fusion pipe2 histogram
+ * @RPPX1_STATS_BLOCK_TYPE_HIST_POST: post-fusion histogram
*/
enum rppx1_stats_block_type {
RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
RPPX1_STATS_BLOCK_TYPE_EXM_PRE1,
RPPX1_STATS_BLOCK_TYPE_EXM_PRE2,
+ RPPX1_STATS_BLOCK_TYPE_HIST_PRE1,
+ RPPX1_STATS_BLOCK_TYPE_HIST_PRE2,
+ RPPX1_STATS_BLOCK_TYPE_HIST_POST,
};
/**
@@ -304,6 +398,20 @@ struct rppx1_exm_stats {
__u32 exp_mean[RPPX1_EXM_NUM_WIN];
};
+/* Histogram */
+#define RPPX1_HIST_NUM_BINS 32
+
+/**
+ * struct rppx1_hist_stats - Histogram statistics
+ *
+ * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_HIST_POST)
+ * @hist_bins: accumulation histogram results in unsigned 20-bit Q16.4 format
+ */
+struct rppx1_hist_stats {
+ struct v4l2_isp_block_header header;
+ __u32 hist_bins[RPPX1_HIST_NUM_BINS];
+};
+
/**
* RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
*
@@ -313,6 +421,9 @@ struct rppx1_exm_stats {
#define RPPX1_STATS_MAX_SIZE \
(sizeof(struct rppx1_wbmeas_stats) + \
sizeof(struct rppx1_exm_stats) + \
- sizeof(struct rppx1_exm_stats))
+ sizeof(struct rppx1_exm_stats) + \
+ sizeof(struct rppx1_hist_stats) + \
+ sizeof(struct rppx1_hist_stats) + \
+ sizeof(struct rppx1_hist_stats))
#endif /* __UAPI_RPP_X1_CONFIG_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 07/14] media: rppx1: hist: Add support histogram measurement
2026-05-04 1:05 ` [v8 07/14] media: rppx1: hist: Add support histogram measurement Niklas Söderlund
@ 2026-05-06 15:51 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 15:51 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Niklas
On Mon, May 04, 2026 at 03:05:49AM +0200, Niklas Söderlund wrote:
> Extend the RPPX1 driver to allow setting the histogram measurement
> configuration and consuming the resulting statistics. It uses the RPPX1
> framework for parameters and its writer abstraction to allow the user to
> control how, and when, configuration is applied to the RPPX1.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> ---
> .../platform/dreamchip/rppx1/rpp_module.h | 2 +
> .../platform/dreamchip/rppx1/rpp_params.c | 6 +
> .../platform/dreamchip/rppx1/rpp_stats.c | 10 ++
> .../platform/dreamchip/rppx1/rppx1_hist.c | 115 +++++++++++++++++-
> .../uapi/linux/media/dreamchip/rppx1-config.h | 115 +++++++++++++++++-
> 5 files changed, 245 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> index 2dc9e54027e1..5725243d0119 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> @@ -86,12 +86,14 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
> union rppx1_params_block {
> struct v4l2_isp_params_block_header header;
> struct rppx1_awbg_params awbg;
> + struct rppx1_hist_params hist;
> struct rppx1_exm_params exm;
> struct rppx1_wbmeas_params wbmeas;
> };
>
> union rppx1_stats_block {
> struct v4l2_isp_params_block_header header;
> + struct rppx1_hist_stats hist;
> struct rppx1_exm_stats exm;
> struct rppx1_wbmeas_stats wbmeas;
> };
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> index 23094e8ce3a7..6472bec6fba3 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> @@ -18,6 +18,9 @@ static const struct v4l2_isp_block_type_info
> rppx1_ext_params_blocks_info[] = {
> RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
> RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
> + RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
> + RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist),
> + RPPX1_PARAMS_BLOCK_INFO(HIST_POST, hist),
> RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm),
> RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm),
> RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
> @@ -58,6 +61,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
> module = &rpp->pre1.awbg;
> break;
> + case RPPX1_PARAMS_BLOCK_TYPE_HIST_POST:
> + module = &rpp->post.hist;
> + break;
> case RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1:
> module = &rpp->pre1.exm;
> break;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> index 0fddf50f7222..b265e858cfd1 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> @@ -16,6 +16,7 @@
>
> static const struct v4l2_isp_block_type_info
> rppx1_stats_blocks_info[] = {
> + RPPX1_STATS_BLOCK_INFO(HIST_POST, hist),
> RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm),
> RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
> };
> @@ -34,6 +35,15 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
>
> v4l2_isp_stats_init_buffer(stats);
>
> + if (isc & RPPX1_IRQ_ID_POST_HIST_MEAS) {
> + block = rppx1_init_stats_block(rpp, stats,
> + RPPX1_STATS_BLOCK_TYPE_HIST_POST);
> + if (!block)
> + return;
> +
> + rpp_module_call(&rpp->post.hist, fill_stats, block);
> + }
> +
> if (isc & RPPX1_IRQ_ID_PRE1_EXM) {
> block = rppx1_init_stats_block(rpp, stats,
> RPPX1_STATS_BLOCK_TYPE_EXM_PRE1);
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
> index cab498ece5a8..99044dc6c115 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
> @@ -25,6 +25,9 @@
>
> #define HIST_LAST_MEAS_LINE_REG 0x0010
> #define HIST_SUBSAMPLING_REG 0x0014
> +#define HIST_SUBSAMPLING_V_STEPSIZE(x) (((x) & 0x7f) << 24)
> +#define HIST_SUBSAMPLING_H_STEP_INC(x) (((x) & 0x1ffff))
> +
> #define HIST_COEFF_R_REG 0x0018
> #define HIST_COEFF_G_REG 0x001c
> #define HIST_COEFF_B_REG 0x0020
> @@ -48,7 +51,6 @@
> #define HIST_FORCED_UPDATE_REG 0x0058
> #define HIST_VSTART_STATUS_REG 0x005c
>
> -#define HIST_BIN_REG_NUM 32
> #define HIST_BIN_REG(n) (0x0060 + (4 * (n)))
>
> static int rppx1_hist_probe(struct rpp_module *mod)
> @@ -71,6 +73,117 @@ static int rppx1_hist_probe(struct rpp_module *mod)
> return 0;
> }
>
> +#define RPPX1_HIST_WEIGHT(v0, v1, v2, v3) \
> + (((v0) & 0x1f) | (((v1) & 0x1f) << 8) | \
> + (((v2) & 0x1f) << 16) | \
> + (((v3) & 0x1f) << 24))
> +
> +static int rppx1_hist_fill_params(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv)
> +{
> + const struct rppx1_hist_params *cfg = &block->hist;
> + u32 h_offs, v_offs, h_size, v_size;
> +
> + /* If the modules is disabled, simply bypass it. */
> + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
> + write(priv, mod->base + HIST_MODE_REG,
> + HIST_MODE_HIST_MODE_DISABLE);
> + return 0;
> + }
> +
> + /* Select sample point */
> + write(priv, mod->base + HIST_CHANNEL_SEL_REG,
> + cfg->channel_sel & HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK);
> +
> + /*
> + * Configure the input subsampling.
> + *
> + * v_stepsize controls which lines are processed. In Bayer mode the
> + * effective value is double to account for the 2x2 macro-pixel size.
> + *
> + * h_step_inc is the horizontal pixel increment counter. The subsampling
> + * counter is incremented by h_step_inc. When the result of the
> + * increment overflows 2^16 a sampling is performed. In Bayer mode the
> + * subsampling counter is only incremented for color channels selected
> + * by hist_mode.
> + */
> + write(priv, mod->base + HIST_SUBSAMPLING_REG,
> + HIST_SUBSAMPLING_V_STEPSIZE(cfg->v_stepsize) |
> + HIST_SUBSAMPLING_H_STEP_INC(cfg->h_step_inc));
> +
> + /*
> + * Adjust and set measurement window to hardware limitations,
> + * - Offsets must be even.
> + * - Width and height must be divisible by 10.
> + */
> + h_offs = cfg->wnd.h_offs & 0x1ffe;
> + v_offs = cfg->wnd.v_offs & 0x1ffe;
> + h_size = cfg->wnd.h_size - cfg->wnd.h_size % 10;
> + v_size = cfg->wnd.v_size - cfg->wnd.v_size % 10;
I didn't modify this, but I might have missed where the divisible by
10 comes from (even and divisile by 5 maybe ?)
> +
> + write(priv, mod->base + HIST_H_OFFS_REG, h_offs);
> + write(priv, mod->base + HIST_V_OFFS_REG, v_offs);
> + write(priv, mod->base + HIST_H_SIZE_REG, h_size / 5);
> + write(priv, mod->base + HIST_V_SIZE_REG, v_size / 5);
We discussed with Jai if it was better to ask userspace to provide the
size of one window, or provide instead the size of the whole area to
cover and divide in the driver. I think your opinion is clear from the
above /5 but I just want to re-check.
> +
> + /*
> + * Set last measurement line for ready interrupt. Ignore the value
> + * from the parameters as it is only useful for fast-channel switching.
> + */
> + write(priv, mod->base + HIST_LAST_MEAS_LINE_REG, v_offs + v_size + 1);
> +
> + /* Set measurement window weights. */
> + write(priv, mod->base + HIST_WEIGHT_00TO30_REG,
> + RPPX1_HIST_WEIGHT(cfg->weights[0], cfg->weights[1],
> + cfg->weights[2], cfg->weights[3]));
> + write(priv, mod->base + HIST_WEIGHT_40TO21_REG,
> + RPPX1_HIST_WEIGHT(cfg->weights[4], cfg->weights[5],
> + cfg->weights[6], cfg->weights[7]));
> + write(priv, mod->base + HIST_WEIGHT_31TO12_REG,
> + RPPX1_HIST_WEIGHT(cfg->weights[8], cfg->weights[9],
> + cfg->weights[10], cfg->weights[11]));
> + write(priv, mod->base + HIST_WEIGHT_22TO03_REG,
> + RPPX1_HIST_WEIGHT(cfg->weights[12], cfg->weights[13],
> + cfg->weights[14], cfg->weights[15]));
> + write(priv, mod->base + HIST_WEIGHT_13TO43_REG,
> + RPPX1_HIST_WEIGHT(cfg->weights[16], cfg->weights[17],
> + cfg->weights[18], cfg->weights[19]));
> + write(priv, mod->base + HIST_WEIGHT_04TO34_REG,
> + RPPX1_HIST_WEIGHT(cfg->weights[20], cfg->weights[21],
> + cfg->weights[22], cfg->weights[23]));
> + write(priv, mod->base + HIST_WEIGHT_44_REG,
> + RPPX1_HIST_WEIGHT(cfg->weights[24], 0, 0, 0));
> +
> + write(priv, mod->base + HIST_MODE_REG, cfg->mode);
> + write(priv, mod->base + HIST_COEFF_R_REG, cfg->coeff[0]);
> + write(priv, mod->base + HIST_COEFF_G_REG, cfg->coeff[1]);
> + write(priv, mod->base + HIST_COEFF_B_REG, cfg->coeff[2]);
> +
> + u32 sample_reg = FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK,
> + cfg->sample_shift) |
> + FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK,
> + cfg->sample_offs);
> + write(priv, mod->base + HIST_SAMPLE_RANGE_REG, sample_reg);
> +
> + write(priv, mod->base + HIST_FORCED_UPDATE_REG, 1);
> +
> + return 0;
> +}
> +
> +static int rppx1_hist_fill_stats(struct rpp_module *mod,
> + union rppx1_stats_block *block)
> +{
> + struct rppx1_hist_stats *stats = &block->hist;
> +
> + for (unsigned int i = 0; i < RPPX1_HIST_NUM_BINS; i++)
> + stats->hist_bins[i] = rpp_module_read(mod, HIST_BIN_REG(i)) & 0xfffff;
> +
> + return 0;
> +}
> +
> const struct rpp_module_ops rppx1_hist_ops = {
> .probe = rppx1_hist_probe,
> + .fill_params = rppx1_hist_fill_params,
> + .fill_stats = rppx1_hist_fill_stats,
> };
> diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
> index 18e718d43f80..909a10935772 100644
> --- a/include/uapi/linux/media/dreamchip/rppx1-config.h
> +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
> @@ -81,6 +81,9 @@ enum rppx1_meas_chan {
> * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST: MAIN_POST White Balance Gains
> * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1: PRE1 pipe Exposure Measurement
> * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2: PRE2 pipe Exposure Measurement
> + * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1: PRE1 pipe Histogram Measurement
> + * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2: PRE2 pipe Histogram Measurement
> + * @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement
> */
> enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
> @@ -89,6 +92,9 @@ enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST,
> RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1,
> RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2,
> + RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
> + RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2,
> + RPPX1_PARAMS_BLOCK_TYPE_HIST_POST,
> };
>
> /**
> @@ -234,6 +240,85 @@ struct rppx1_exm_params {
> __u8 coeff_gb;
> };
>
> +/* Histogram */
> +#define RPPX1_HIST_WEIGHT_GRIDS_SIZE 25
> +
> +/**
> + * enum rppx1_hist_mode - Histogram measurement mode
> + *
> + * Histogram measurement mode. Select which channel or combination of channels
> + * the histogram measurement is performed on.
> + *
> + * @RPPX1_HIST_MODE_DISABLE: histogram disabled
> + * @RPPX1_HIST_MODE_RGB_COMBINED: combined RGB histogram
> + * @RPPX1_HIST_MODE_R_HISTOGRAM: red channel histogram
> + * @RPPX1_HIST_MODE_GR_HISTOGRAM: green/red channel histogram
> + * @RPPX1_HIST_MODE_B_HISTOGRAM: blue channel histogram
> + * @RPPX1_HIST_MODE_GB_HISTOGRAM: green/blue histogram
> + */
> +enum rppx1_hist_mode {
> + RPPX1_HIST_MODE_DISABLE,
> + RPPX1_HIST_MODE_RGB_COMBINED,
> + RPPX1_HIST_MODE_R_HISTOGRAM,
> + RPPX1_HIST_MODE_GR_HISTOGRAM,
> + RPPX1_HIST_MODE_B_HISTOGRAM,
> + RPPX1_HIST_MODE_GB_HISTOGRAM,
> +};
> +
> +/**
> + * struct rppx1_hist_params - Histogram measurement configuration
> + *
> + * The RPP-X1 Histogram measurement unit is available on the PRE1, PRE2 and
> + * MAIN_POST pipes. Userspace selects which pipe to operate by setting the
> + * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
> + * RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2 or
> + * RPPX1_PARAMS_BLOCK_TYPE_HIST_POST.
> + *
> + * The histogram measurement point is selected using the @channel field while
> + * histogram measurement mode is selected using the @mode field.
> + *
> + * Histogram measurement is performed by programming subsampling factors using
> + * the @v_stepsize and @h_step_inc fields and by weighted windowing, by
> + * programming the size of the measurement window @wnd with @weights associated
> + * to each cell of the 5x5 measurement grid. Weights are represented as 5 bits
> + * integer values ranging from 0 to 16.
> + *
> + * The @last_line fields controls when the histogram measurement completes. It
> + * is usually programmed to the value of (@wnd.v_offs + @wnd.v_size - 1).
> + *
> + * Histogram values are calculated by applying a per-color channel coefficient
> + * represented as an 8 bits unsigned Q1.7 integer value. The @sample_offs and
> + * @sample_shift fields allow to reduce the color dynamic range on which
> + * histogram data are produced.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
> + * type = RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2 or
> + * type = RPPX1_PARAMS_BLOCK_TYPE_HIST_POST)
> + * @mode: histogram measurement mode (from enum rppx1_hist_mode)
> + * @channel_sel: histogram measurement point (see enum rppx1_meas_chan)
> + * @wnd: measurement window coordinates
> + * @weights: weighting factors for each sub-window (5x5 grid)
> + * @last_line: line number for which the histogram measurement completes
> + * @v_stepsize: vertical subsampling divider, 7 bits
> + * @h_step_inc: horizontal subsampling step counter, 17 bits
> + * @coeff: R-G-B coefficients, 8 bits unsigned Q1.7
> + * @sample_offs: sample offset, 24 bits
> + * @sample_shift: sample shift, 4 bits
> + */
> +struct rppx1_hist_params {
> + struct v4l2_isp_params_block_header header;
> + __u8 mode;
> + __u8 channel_sel;
> + struct rppx1_window wnd;
> + __u8 weights[RPPX1_HIST_WEIGHT_GRIDS_SIZE];
> + __u32 last_line;
> + __u32 v_stepsize;
> + __u32 h_step_inc;
> + __u8 coeff[3];
> + __u32 sample_offs;
> + __u8 sample_shift;
> +};
> +
> /**
> * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> *
> @@ -246,7 +331,10 @@ struct rppx1_exm_params {
> sizeof(struct rppx1_awbg_params) + \
> sizeof(struct rppx1_awbg_params) + \
> sizeof(struct rppx1_exm_params) + \
> - sizeof(struct rppx1_exm_params))
> + sizeof(struct rppx1_exm_params) + \
> + sizeof(struct rppx1_hist_params) + \
> + sizeof(struct rppx1_hist_params) + \
> + sizeof(struct rppx1_hist_params))
>
> /* ---------------------------------------------------------------------------
> * Statistics Structures
> @@ -263,11 +351,17 @@ struct rppx1_exm_params {
> * @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement
> * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE1: pre-fusion pipe1 exposure measurement
> * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE2: pre-fusion pipe2 exposure measurement
> + * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE1: pre-fusion pipe1 histogram
> + * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE2: pre-fusion pipe2 histogram
> + * @RPPX1_STATS_BLOCK_TYPE_HIST_POST: post-fusion histogram
> */
> enum rppx1_stats_block_type {
> RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
> RPPX1_STATS_BLOCK_TYPE_EXM_PRE1,
> RPPX1_STATS_BLOCK_TYPE_EXM_PRE2,
> + RPPX1_STATS_BLOCK_TYPE_HIST_PRE1,
> + RPPX1_STATS_BLOCK_TYPE_HIST_PRE2,
> + RPPX1_STATS_BLOCK_TYPE_HIST_POST,
> };
>
> /**
> @@ -304,6 +398,20 @@ struct rppx1_exm_stats {
> __u32 exp_mean[RPPX1_EXM_NUM_WIN];
> };
>
> +/* Histogram */
> +#define RPPX1_HIST_NUM_BINS 32
> +
> +/**
> + * struct rppx1_hist_stats - Histogram statistics
> + *
> + * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_HIST_POST)
> + * @hist_bins: accumulation histogram results in unsigned 20-bit Q16.4 format
> + */
> +struct rppx1_hist_stats {
> + struct v4l2_isp_block_header header;
> + __u32 hist_bins[RPPX1_HIST_NUM_BINS];
> +};
> +
> /**
> * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
> *
> @@ -313,6 +421,9 @@ struct rppx1_exm_stats {
> #define RPPX1_STATS_MAX_SIZE \
> (sizeof(struct rppx1_wbmeas_stats) + \
> sizeof(struct rppx1_exm_stats) + \
> - sizeof(struct rppx1_exm_stats))
> + sizeof(struct rppx1_exm_stats) + \
> + sizeof(struct rppx1_hist_stats) + \
> + sizeof(struct rppx1_hist_stats) + \
> + sizeof(struct rppx1_hist_stats))
>
> #endif /* __UAPI_RPP_X1_CONFIG_H */
> --
> 2.54.0
>
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [v8 08/14] media: rppx1: bls: Add support for black level compensation
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (6 preceding siblings ...)
2026-05-04 1:05 ` [v8 07/14] media: rppx1: hist: Add support histogram measurement Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 15:25 ` Jacopo Mondi
2026-05-04 1:05 ` [v8 09/14] media: rppx1: ccor: Add support for color correction matrix Niklas Söderlund
` (6 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the black level measurement
and gain configuration and consuming the resulting statistics. It uses
the RPPX1 framework for parameters and its writer abstraction to allow
the user to control how, and when, configuration is applied to the
RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 2 +
.../platform/dreamchip/rppx1/rpp_params.c | 5 +
.../platform/dreamchip/rppx1/rpp_stats.c | 8 ++
.../platform/dreamchip/rppx1/rppx1_bls.c | 121 ++++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 131 +++++++++++++++++-
5 files changed, 265 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 5725243d0119..9c761448717b 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -85,6 +85,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
union rppx1_params_block {
struct v4l2_isp_params_block_header header;
+ struct rppx1_bls_params bls;
struct rppx1_awbg_params awbg;
struct rppx1_hist_params hist;
struct rppx1_exm_params exm;
@@ -93,6 +94,7 @@ union rppx1_params_block {
union rppx1_stats_block {
struct v4l2_isp_params_block_header header;
+ struct rppx1_bls_stats bls;
struct rppx1_hist_stats hist;
struct rppx1_exm_stats exm;
struct rppx1_wbmeas_stats wbmeas;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 6472bec6fba3..7b006c68381b 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -16,6 +16,8 @@
static const struct v4l2_isp_block_type_info
rppx1_ext_params_blocks_info[] = {
+ RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls),
+ RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
@@ -58,6 +60,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
block_offset += block->header.size;
switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
+ module = &rpp->pre1.bls;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
module = &rpp->pre1.awbg;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
index b265e858cfd1..c2c0fc109e90 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -16,6 +16,7 @@
static const struct v4l2_isp_block_type_info
rppx1_stats_blocks_info[] = {
+ RPPX1_STATS_BLOCK_INFO(BLS_PRE1, bls),
RPPX1_STATS_BLOCK_INFO(HIST_POST, hist),
RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm),
RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
@@ -51,6 +52,13 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
return;
rpp_module_call(&rpp->pre1.exm, fill_stats, block);
+
+ block = rppx1_init_stats_block(rpp, stats,
+ RPPX1_STATS_BLOCK_TYPE_BLS_PRE1);
+ if (!block)
+ return;
+
+ rpp_module_call(&rpp->pre1.bls, fill_stats, block);
}
if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) {
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
index de7008befd8e..a3cb03fd97d0 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
@@ -5,6 +5,7 @@
*/
#include "rpp_module.h"
+#include "rppx1.h"
#define BLS_VERSION_REG 0x0000
@@ -31,6 +32,8 @@
#define BLS_B_MEASURED_REG 0x0040
#define BLS_C_MEASURED_REG 0x0044
#define BLS_D_MEASURED_REG 0x0048
+#define BLS_PRE1_FIXED_MASK GENMASK(24, 0)
+#define BLS_PRE2_FIXED_MASK GENMASK(12, 0)
static int rppx1_bls_probe(struct rpp_module *mod)
{
@@ -54,6 +57,124 @@ static int rppx1_bls_probe(struct rpp_module *mod)
return 0;
}
+static void
+rppx1_bls_swap_regs(struct rpp_module *mod, const u32 input[4], u32 output[4])
+{
+ static const unsigned int swap[4][4] = {
+ [RPP_RGGB] = { 0, 1, 2, 3 },
+ [RPP_GRBG] = { 1, 0, 3, 2 },
+ [RPP_GBRG] = { 2, 3, 0, 1 },
+ [RPP_BGGR] = { 3, 2, 1, 0 },
+ };
+
+ /* Swap to pattern used in our path, PRE1 or PRE2. */
+ struct rpp_module *acq = mod == &mod->rpp->pre1.bls ?
+ &mod->rpp->pre1.acq : &mod->rpp->pre2.bls;
+ enum rpp_raw_pattern pattern = acq->info.acq.raw_pattern;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ output[i] = input[swap[pattern][i]];
+}
+
+static int
+rppx1_bls_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_bls_params *cfg = &block->bls;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + BLS_CTRL_REG, 0);
+ return 0;
+ }
+
+ u32 ctrl = BLS_CTRL_BLS_EN;
+
+ if (cfg->mode == RPPX1_BLS_MODE_FIXED) {
+ static const u32 regs[] = {
+ BLS_A_FIXED_REG,
+ BLS_B_FIXED_REG,
+ BLS_C_FIXED_REG,
+ BLS_D_FIXED_REG,
+ };
+ u32 swapped[4];
+
+ rppx1_bls_swap_regs(mod, regs, swapped);
+
+ /*
+ * The PRE1 pipe fixed values are 24-bits + 1 sign bit, while
+ * the PRE2 pipe values are 12-bits + 1 sign bit.
+ */
+ u32 mask;
+
+ switch (cfg->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
+ mask = BLS_PRE1_FIXED_MASK;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2:
+ mask = BLS_PRE2_FIXED_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ write(priv, mod->base + swapped[0], cfg->fixed.a & mask);
+ write(priv, mod->base + swapped[1], cfg->fixed.b & mask);
+ write(priv, mod->base + swapped[2], cfg->fixed.c & mask);
+ write(priv, mod->base + swapped[3], cfg->fixed.d & mask);
+ } else {
+ write(priv, mod->base + BLS_SAMPLES_REG, cfg->samples);
+
+ if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN1) {
+ write(priv, mod->base + BLS_H1_START_REG, cfg->window1.h_offs);
+ write(priv, mod->base + BLS_H1_STOP_REG, cfg->window1.h_size);
+ write(priv, mod->base + BLS_V1_START_REG, cfg->window1.v_offs);
+ write(priv, mod->base + BLS_V1_STOP_REG, cfg->window1.v_size);
+ ctrl |= BLS_CTRL_BLS_WIN1;
+ }
+
+ if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN2) {
+ write(priv, mod->base + BLS_H2_START_REG, cfg->window2.h_offs);
+ write(priv, mod->base + BLS_H2_STOP_REG, cfg->window2.h_size);
+ write(priv, mod->base + BLS_V2_START_REG, cfg->window2.v_offs);
+ write(priv, mod->base + BLS_V2_STOP_REG, cfg->window2.v_size);
+ ctrl |= BLS_CTRL_BLS_WIN2;
+ }
+
+ ctrl |= BLS_CTRL_BLS_MODE_MEASURED;
+ }
+
+ write(priv, mod->base + BLS_CTRL_REG, ctrl);
+
+ return 0;
+}
+
+static int rppx1_bls_fill_stats(struct rpp_module *mod,
+ union rppx1_stats_block *block)
+{
+ struct rppx1_bls_stats *stats = &block->bls;
+
+ static const u32 regs[] = {
+ BLS_A_MEASURED_REG,
+ BLS_B_MEASURED_REG,
+ BLS_C_MEASURED_REG,
+ BLS_D_MEASURED_REG,
+ };
+ u32 swapped[4];
+
+ rppx1_bls_swap_regs(mod, regs, swapped);
+
+ stats->meas_r = rpp_module_read(mod, swapped[0]);
+ stats->meas_gr = rpp_module_read(mod, swapped[1]);
+ stats->meas_gb = rpp_module_read(mod, swapped[2]);
+ stats->meas_b = rpp_module_read(mod, swapped[3]);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_bls_ops = {
.probe = rppx1_bls_probe,
+ .fill_params = rppx1_bls_fill_params,
+ .fill_stats = rppx1_bls_fill_stats,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 909a10935772..b181ef08d093 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -84,6 +84,8 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1: PRE1 pipe Histogram Measurement
* @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2: PRE2 pipe Histogram Measurement
* @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement
+ * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction
+ * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -95,6 +97,8 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2,
RPPX1_PARAMS_BLOCK_TYPE_HIST_POST,
+ RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1,
+ RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2,
};
/**
@@ -319,6 +323,103 @@ struct rppx1_hist_params {
__u8 sample_shift;
};
+/**
+ * struct rppx1_bls_fixed - BLS fixed subtraction values
+ *
+ * Fixed black level values subtracted from sensor data per Bayer channel.
+ * Negative values result in addition.
+ *
+ * The PRE1 pipe BLS module operates on a 24-bits input data and fixed black
+ * levels are stored as a signed 2's complement representation ranging from
+ * -2^24 to 2^24-1.
+ *
+ * The PRE2 pipe BLS module operates on a 12-bits input data and fixed black
+ * levels are stored as a signed 2's complement representation ranging from
+ * -2^12 to 2^12-1.
+ *
+ * Userspace is expected to provide fixed black level values with a bit-depth
+ * matching the one of pipe in use.
+ *
+ * These subtraction values are matched with the sensor native Bayer components
+ * ordering according to the cropping configuration on the input port.
+ *
+ * @a: subtraction value for channel A
+ * @b: subtraction value for channel B
+ * @c: subtraction value for channel C
+ * @d: subtraction value for channel D
+ */
+struct rppx1_bls_fixed {
+ __u32 a;
+ __u32 b;
+ __u32 c;
+ __u32 d;
+};
+
+/**
+ * enum rppx1_bls_mode - BLS subtraction mode
+ *
+ * Select if subtracted black level come from fixed or measured values.
+ *
+ * @RPPX1_BLS_MODE_FIXED: subtract fixed values
+ * @RPPX1_BLS_MODE_MEAS: subtract measured values
+ */
+enum rppx1_bls_mode {
+ RPPX1_BLS_MODE_FIXED,
+ RPPX1_BLS_MODE_MEAS,
+};
+
+/**
+ * enum rppx1_bls_win_en: BLS measurement configuration
+ *
+ * Select the measurement window to use for measured black level values.
+ *
+ * @RPPX1_BLS_WIN_EN_OFF: disable measurement
+ * @RPPX1_BLS_WIN_EN_WIN1: Enable measurement from window 1
+ * @RPPX1_BLS_WIN_EN_WIN2: enable measurement from window 2
+ * @RPPX1_BLS_WIN_EN_WIN12: enable measurement from window 1 and window 2
+ */
+enum rppx1_bls_win_en {
+ RPPX1_BLS_WIN_EN_OFF,
+ RPPX1_BLS_WIN_EN_WIN1,
+ RPPX1_BLS_WIN_EN_WIN2,
+ RPPX1_BLS_WIN_EN_WIN12,
+};
+
+/**
+ * struct rppx1_bls_params - RPP-X1 Black Level Subtraction Module
+ *
+ * The RPP-X1 Black Level Subtraction module is available on the PRE1 and PRE2
+ * pre-fusion pipes. Userspace selects which pipe to operate by setting the
+ * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1 or
+ * RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2.
+ *
+ * The BLS module operates on fixed or measured data according to the setting of
+ * the @mode field. When RPPX1_BLS_MODE_FIXED is used userspace shall provide
+ * the per-channel black levels in @fixed. When RPPX1_BLS_MODE_MEAS is used
+ * userspace shall configure the measurement windows @window1 and optionally
+ * @window2 to select the optically black pixels region in the input frame. The
+ * @samples fields controls how many measure samples are used for averaging the
+ * measured black levels.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1 or
+ * type == RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2)
+ * @mode: BLS subtraction mode (see enum rppx1_bls_mode)
+ * @en_windows: BLS measurement mode (see rppx1_bls_win_en)
+ * @samples: log2 of the number of measured pixels per Bayer position
+ * @window1: BLS measurement window 1 (14 bits)
+ * @window2: BLS measurement window 2 (14 bits)
+ * @fixed: fixed subtraction values (see enum rppx1_bls_fixed)
+ */
+struct rppx1_bls_params {
+ struct v4l2_isp_params_block_header header;
+ __u8 mode;
+ __u8 en_windows;
+ __u8 samples;
+ struct rppx1_window window1;
+ struct rppx1_window window2;
+ struct rppx1_bls_fixed fixed;
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -334,7 +435,9 @@ struct rppx1_hist_params {
sizeof(struct rppx1_exm_params) + \
sizeof(struct rppx1_hist_params) + \
sizeof(struct rppx1_hist_params) + \
- sizeof(struct rppx1_hist_params))
+ sizeof(struct rppx1_hist_params) + \
+ sizeof(struct rppx1_bls_params) + \
+ sizeof(struct rppx1_bls_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
@@ -354,6 +457,8 @@ struct rppx1_hist_params {
* @RPPX1_STATS_BLOCK_TYPE_HIST_PRE1: pre-fusion pipe1 histogram
* @RPPX1_STATS_BLOCK_TYPE_HIST_PRE2: pre-fusion pipe2 histogram
* @RPPX1_STATS_BLOCK_TYPE_HIST_POST: post-fusion histogram
+ * @RPPX1_STATS_BLOCK_TYPE_BLS_PRE1: pre-fusion pipe1 black level subtraction
+ * @RPPX1_STATS_BLOCK_TYPE_BLS_PRE2: pre-fusion pipe2 black level subtraction
*/
enum rppx1_stats_block_type {
RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
@@ -362,6 +467,8 @@ enum rppx1_stats_block_type {
RPPX1_STATS_BLOCK_TYPE_HIST_PRE1,
RPPX1_STATS_BLOCK_TYPE_HIST_PRE2,
RPPX1_STATS_BLOCK_TYPE_HIST_POST,
+ RPPX1_STATS_BLOCK_TYPE_BLS_PRE1,
+ RPPX1_STATS_BLOCK_TYPE_BLS_PRE2,
};
/**
@@ -412,6 +519,24 @@ struct rppx1_hist_stats {
__u32 hist_bins[RPPX1_HIST_NUM_BINS];
};
+/**
+ * struct rppx1_bls_stats - Black level subtraction measurements
+ *
+ * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_BLS_PRE1 or
+ * RPPX1_STATS_BLOCK_TYPE_BLS_PRE2)
+ * @meas_r: mean measured value for Bayer pattern R
+ * @meas_gr: mean measured value for Bayer pattern Gr
+ * @meas_gb: mean measured value for Bayer pattern Gb
+ * @meas_b: mean measured value for Bayer pattern B
+ */
+struct rppx1_bls_stats {
+ struct v4l2_isp_block_header header;
+ __u32 meas_r;
+ __u32 meas_gr;
+ __u32 meas_gb;
+ __u32 meas_b;
+};
+
/**
* RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
*
@@ -424,6 +549,8 @@ struct rppx1_hist_stats {
sizeof(struct rppx1_exm_stats) + \
sizeof(struct rppx1_hist_stats) + \
sizeof(struct rppx1_hist_stats) + \
- sizeof(struct rppx1_hist_stats))
+ sizeof(struct rppx1_hist_stats) + \
+ sizeof(struct rppx1_bls_stats) + \
+ sizeof(struct rppx1_bls_stats))
#endif /* __UAPI_RPP_X1_CONFIG_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 08/14] media: rppx1: bls: Add support for black level compensation
2026-05-04 1:05 ` [v8 08/14] media: rppx1: bls: Add support for black level compensation Niklas Söderlund
@ 2026-05-06 15:25 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 15:25 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Niklas
On Mon, May 04, 2026 at 03:05:50AM +0200, Niklas Söderlund wrote:
> Extend the RPPX1 driver to allow setting the black level measurement
> and gain configuration and consuming the resulting statistics. It uses
> the RPPX1 framework for parameters and its writer abstraction to allow
> the user to control how, and when, configuration is applied to the
> RPPX1.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> ---
> .../platform/dreamchip/rppx1/rpp_module.h | 2 +
> .../platform/dreamchip/rppx1/rpp_params.c | 5 +
> .../platform/dreamchip/rppx1/rpp_stats.c | 8 ++
> .../platform/dreamchip/rppx1/rppx1_bls.c | 121 ++++++++++++++++
> .../uapi/linux/media/dreamchip/rppx1-config.h | 131 +++++++++++++++++-
> 5 files changed, 265 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> index 5725243d0119..9c761448717b 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> @@ -85,6 +85,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
>
> union rppx1_params_block {
> struct v4l2_isp_params_block_header header;
> + struct rppx1_bls_params bls;
> struct rppx1_awbg_params awbg;
> struct rppx1_hist_params hist;
> struct rppx1_exm_params exm;
> @@ -93,6 +94,7 @@ union rppx1_params_block {
>
> union rppx1_stats_block {
> struct v4l2_isp_params_block_header header;
> + struct rppx1_bls_stats bls;
> struct rppx1_hist_stats hist;
> struct rppx1_exm_stats exm;
> struct rppx1_wbmeas_stats wbmeas;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> index 6472bec6fba3..7b006c68381b 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> @@ -16,6 +16,8 @@
>
> static const struct v4l2_isp_block_type_info
> rppx1_ext_params_blocks_info[] = {
> + RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls),
> + RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
> RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
> RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
> RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
> @@ -58,6 +60,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> block_offset += block->header.size;
>
> switch (block->header.type) {
> + case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
> + module = &rpp->pre1.bls;
> + break;
> case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
> module = &rpp->pre1.awbg;
> break;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> index b265e858cfd1..c2c0fc109e90 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
> @@ -16,6 +16,7 @@
>
> static const struct v4l2_isp_block_type_info
> rppx1_stats_blocks_info[] = {
> + RPPX1_STATS_BLOCK_INFO(BLS_PRE1, bls),
> RPPX1_STATS_BLOCK_INFO(HIST_POST, hist),
> RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm),
> RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
> @@ -51,6 +52,13 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
> return;
>
> rpp_module_call(&rpp->pre1.exm, fill_stats, block);
> +
> + block = rppx1_init_stats_block(rpp, stats,
> + RPPX1_STATS_BLOCK_TYPE_BLS_PRE1);
> + if (!block)
> + return;
> +
> + rpp_module_call(&rpp->pre1.bls, fill_stats, block);
> }
>
> if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) {
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
> index de7008befd8e..a3cb03fd97d0 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
> @@ -5,6 +5,7 @@
> */
>
> #include "rpp_module.h"
> +#include "rppx1.h"
>
> #define BLS_VERSION_REG 0x0000
>
> @@ -31,6 +32,8 @@
> #define BLS_B_MEASURED_REG 0x0040
> #define BLS_C_MEASURED_REG 0x0044
> #define BLS_D_MEASURED_REG 0x0048
> +#define BLS_PRE1_FIXED_MASK GENMASK(24, 0)
> +#define BLS_PRE2_FIXED_MASK GENMASK(12, 0)
>
> static int rppx1_bls_probe(struct rpp_module *mod)
> {
> @@ -54,6 +57,124 @@ static int rppx1_bls_probe(struct rpp_module *mod)
> return 0;
> }
>
> +static void
> +rppx1_bls_swap_regs(struct rpp_module *mod, const u32 input[4], u32 output[4])
> +{
> + static const unsigned int swap[4][4] = {
> + [RPP_RGGB] = { 0, 1, 2, 3 },
> + [RPP_GRBG] = { 1, 0, 3, 2 },
> + [RPP_GBRG] = { 2, 3, 0, 1 },
> + [RPP_BGGR] = { 3, 2, 1, 0 },
> + };
> +
> + /* Swap to pattern used in our path, PRE1 or PRE2. */
> + struct rpp_module *acq = mod == &mod->rpp->pre1.bls ?
> + &mod->rpp->pre1.acq : &mod->rpp->pre2.bls;
> + enum rpp_raw_pattern pattern = acq->info.acq.raw_pattern;
> +
> + for (unsigned int i = 0; i < 4; ++i)
> + output[i] = input[swap[pattern][i]];
> +}
> +
> +static int
> +rppx1_bls_fill_params(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv)
> +{
> + const struct rppx1_bls_params *cfg = &block->bls;
> +
> + /* If the modules is disabled, simply bypass it. */
> + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
> + write(priv, mod->base + BLS_CTRL_REG, 0);
> + return 0;
> + }
> +
> + u32 ctrl = BLS_CTRL_BLS_EN;
> +
> + if (cfg->mode == RPPX1_BLS_MODE_FIXED) {
> + static const u32 regs[] = {
> + BLS_A_FIXED_REG,
> + BLS_B_FIXED_REG,
> + BLS_C_FIXED_REG,
> + BLS_D_FIXED_REG,
> + };
> + u32 swapped[4];
> +
> + rppx1_bls_swap_regs(mod, regs, swapped);
> +
> + /*
> + * The PRE1 pipe fixed values are 24-bits + 1 sign bit, while
> + * the PRE2 pipe values are 12-bits + 1 sign bit.
> + */
> + u32 mask;
> +
> + switch (cfg->header.type) {
> + case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
> + mask = BLS_PRE1_FIXED_MASK;
> + break;
> + case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2:
> + mask = BLS_PRE2_FIXED_MASK;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + write(priv, mod->base + swapped[0], cfg->fixed.a & mask);
> + write(priv, mod->base + swapped[1], cfg->fixed.b & mask);
> + write(priv, mod->base + swapped[2], cfg->fixed.c & mask);
> + write(priv, mod->base + swapped[3], cfg->fixed.d & mask);
> + } else {
> + write(priv, mod->base + BLS_SAMPLES_REG, cfg->samples);
> +
> + if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN1) {
> + write(priv, mod->base + BLS_H1_START_REG, cfg->window1.h_offs);
> + write(priv, mod->base + BLS_H1_STOP_REG, cfg->window1.h_size);
> + write(priv, mod->base + BLS_V1_START_REG, cfg->window1.v_offs);
> + write(priv, mod->base + BLS_V1_STOP_REG, cfg->window1.v_size);
> + ctrl |= BLS_CTRL_BLS_WIN1;
> + }
> +
> + if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN2) {
> + write(priv, mod->base + BLS_H2_START_REG, cfg->window2.h_offs);
> + write(priv, mod->base + BLS_H2_STOP_REG, cfg->window2.h_size);
> + write(priv, mod->base + BLS_V2_START_REG, cfg->window2.v_offs);
> + write(priv, mod->base + BLS_V2_STOP_REG, cfg->window2.v_size);
> + ctrl |= BLS_CTRL_BLS_WIN2;
> + }
> +
> + ctrl |= BLS_CTRL_BLS_MODE_MEASURED;
> + }
> +
> + write(priv, mod->base + BLS_CTRL_REG, ctrl);
> +
> + return 0;
> +}
> +
> +static int rppx1_bls_fill_stats(struct rpp_module *mod,
> + union rppx1_stats_block *block)
> +{
I had left out bls_stats for two reasons:
- they need a properly configured sensor to produce optically black
pixels
- I don't think they should be reported in stats for each frame, after
all their measurement should be fixed ?
All in all this seems like a feature to be used for calibration. I'm
not saying we should prevent it from being used, but maybe reporting
it through stats is not the best way ?
> + struct rppx1_bls_stats *stats = &block->bls;
> +
> + static const u32 regs[] = {
> + BLS_A_MEASURED_REG,
> + BLS_B_MEASURED_REG,
> + BLS_C_MEASURED_REG,
> + BLS_D_MEASURED_REG,
> + };
> + u32 swapped[4];
> +
> + rppx1_bls_swap_regs(mod, regs, swapped);
> +
> + stats->meas_r = rpp_module_read(mod, swapped[0]);
> + stats->meas_gr = rpp_module_read(mod, swapped[1]);
> + stats->meas_gb = rpp_module_read(mod, swapped[2]);
> + stats->meas_b = rpp_module_read(mod, swapped[3]);
For params I used 'a' 'b' 'c' and 'd' to match the registers. True
that with rppx1_bls_swap_regs() the a,b,c and d channels gets mapped
to the color ones. Should we use color channels for params as well ?
> +
> + return 0;
> +}
> +
> const struct rpp_module_ops rppx1_bls_ops = {
> .probe = rppx1_bls_probe,
> + .fill_params = rppx1_bls_fill_params,
> + .fill_stats = rppx1_bls_fill_stats,
> };
> diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
> index 909a10935772..b181ef08d093 100644
> --- a/include/uapi/linux/media/dreamchip/rppx1-config.h
> +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
> @@ -84,6 +84,8 @@ enum rppx1_meas_chan {
> * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1: PRE1 pipe Histogram Measurement
> * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2: PRE2 pipe Histogram Measurement
> * @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement
> + * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction
> + * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction
> */
> enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
> @@ -95,6 +97,8 @@ enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1,
> RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2,
> RPPX1_PARAMS_BLOCK_TYPE_HIST_POST,
> + RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1,
> + RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2,
> };
>
> /**
> @@ -319,6 +323,103 @@ struct rppx1_hist_params {
> __u8 sample_shift;
> };
>
> +/**
> + * struct rppx1_bls_fixed - BLS fixed subtraction values
> + *
> + * Fixed black level values subtracted from sensor data per Bayer channel.
> + * Negative values result in addition.
> + *
> + * The PRE1 pipe BLS module operates on a 24-bits input data and fixed black
> + * levels are stored as a signed 2's complement representation ranging from
> + * -2^24 to 2^24-1.
> + *
> + * The PRE2 pipe BLS module operates on a 12-bits input data and fixed black
> + * levels are stored as a signed 2's complement representation ranging from
> + * -2^12 to 2^12-1.
> + *
> + * Userspace is expected to provide fixed black level values with a bit-depth
> + * matching the one of pipe in use.
> + *
> + * These subtraction values are matched with the sensor native Bayer components
> + * ordering according to the cropping configuration on the input port.
> + *
> + * @a: subtraction value for channel A
> + * @b: subtraction value for channel B
> + * @c: subtraction value for channel C
> + * @d: subtraction value for channel D
> + */
> +struct rppx1_bls_fixed {
> + __u32 a;
> + __u32 b;
> + __u32 c;
> + __u32 d;
> +};
> +
> +/**
> + * enum rppx1_bls_mode - BLS subtraction mode
> + *
> + * Select if subtracted black level come from fixed or measured values.
> + *
> + * @RPPX1_BLS_MODE_FIXED: subtract fixed values
> + * @RPPX1_BLS_MODE_MEAS: subtract measured values
> + */
> +enum rppx1_bls_mode {
> + RPPX1_BLS_MODE_FIXED,
> + RPPX1_BLS_MODE_MEAS,
> +};
> +
> +/**
> + * enum rppx1_bls_win_en: BLS measurement configuration
> + *
> + * Select the measurement window to use for measured black level values.
> + *
> + * @RPPX1_BLS_WIN_EN_OFF: disable measurement
> + * @RPPX1_BLS_WIN_EN_WIN1: Enable measurement from window 1
> + * @RPPX1_BLS_WIN_EN_WIN2: enable measurement from window 2
> + * @RPPX1_BLS_WIN_EN_WIN12: enable measurement from window 1 and window 2
> + */
> +enum rppx1_bls_win_en {
> + RPPX1_BLS_WIN_EN_OFF,
> + RPPX1_BLS_WIN_EN_WIN1,
> + RPPX1_BLS_WIN_EN_WIN2,
> + RPPX1_BLS_WIN_EN_WIN12,
> +};
> +
> +/**
> + * struct rppx1_bls_params - RPP-X1 Black Level Subtraction Module
> + *
> + * The RPP-X1 Black Level Subtraction module is available on the PRE1 and PRE2
> + * pre-fusion pipes. Userspace selects which pipe to operate by setting the
> + * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1 or
> + * RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2.
> + *
> + * The BLS module operates on fixed or measured data according to the setting of
> + * the @mode field. When RPPX1_BLS_MODE_FIXED is used userspace shall provide
> + * the per-channel black levels in @fixed. When RPPX1_BLS_MODE_MEAS is used
> + * userspace shall configure the measurement windows @window1 and optionally
> + * @window2 to select the optically black pixels region in the input frame. The
> + * @samples fields controls how many measure samples are used for averaging the
> + * measured black levels.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1 or
> + * type == RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2)
> + * @mode: BLS subtraction mode (see enum rppx1_bls_mode)
> + * @en_windows: BLS measurement mode (see rppx1_bls_win_en)
> + * @samples: log2 of the number of measured pixels per Bayer position
> + * @window1: BLS measurement window 1 (14 bits)
> + * @window2: BLS measurement window 2 (14 bits)
> + * @fixed: fixed subtraction values (see enum rppx1_bls_fixed)
> + */
> +struct rppx1_bls_params {
> + struct v4l2_isp_params_block_header header;
> + __u8 mode;
> + __u8 en_windows;
> + __u8 samples;
> + struct rppx1_window window1;
> + struct rppx1_window window2;
> + struct rppx1_bls_fixed fixed;
> +};
> +
> /**
> * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> *
> @@ -334,7 +435,9 @@ struct rppx1_hist_params {
> sizeof(struct rppx1_exm_params) + \
> sizeof(struct rppx1_hist_params) + \
> sizeof(struct rppx1_hist_params) + \
> - sizeof(struct rppx1_hist_params))
> + sizeof(struct rppx1_hist_params) + \
> + sizeof(struct rppx1_bls_params) + \
> + sizeof(struct rppx1_bls_params))
>
> /* ---------------------------------------------------------------------------
> * Statistics Structures
> @@ -354,6 +457,8 @@ struct rppx1_hist_params {
> * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE1: pre-fusion pipe1 histogram
> * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE2: pre-fusion pipe2 histogram
> * @RPPX1_STATS_BLOCK_TYPE_HIST_POST: post-fusion histogram
> + * @RPPX1_STATS_BLOCK_TYPE_BLS_PRE1: pre-fusion pipe1 black level subtraction
> + * @RPPX1_STATS_BLOCK_TYPE_BLS_PRE2: pre-fusion pipe2 black level subtraction
> */
> enum rppx1_stats_block_type {
> RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST,
> @@ -362,6 +467,8 @@ enum rppx1_stats_block_type {
> RPPX1_STATS_BLOCK_TYPE_HIST_PRE1,
> RPPX1_STATS_BLOCK_TYPE_HIST_PRE2,
> RPPX1_STATS_BLOCK_TYPE_HIST_POST,
> + RPPX1_STATS_BLOCK_TYPE_BLS_PRE1,
> + RPPX1_STATS_BLOCK_TYPE_BLS_PRE2,
> };
>
> /**
> @@ -412,6 +519,24 @@ struct rppx1_hist_stats {
> __u32 hist_bins[RPPX1_HIST_NUM_BINS];
> };
>
> +/**
> + * struct rppx1_bls_stats - Black level subtraction measurements
If we want to report stats, please add documentation with a similar
level of details as the one of rppx1_bls_params when it comes to the
bit depth of the measured data.
> + *
> + * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_BLS_PRE1 or
> + * RPPX1_STATS_BLOCK_TYPE_BLS_PRE2)
> + * @meas_r: mean measured value for Bayer pattern R
> + * @meas_gr: mean measured value for Bayer pattern Gr
> + * @meas_gb: mean measured value for Bayer pattern Gb
> + * @meas_b: mean measured value for Bayer pattern B
> + */
> +struct rppx1_bls_stats {
> + struct v4l2_isp_block_header header;
> + __u32 meas_r;
> + __u32 meas_gr;
> + __u32 meas_gb;
> + __u32 meas_b;
> +};
> +
> /**
> * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics
> *
> @@ -424,6 +549,8 @@ struct rppx1_hist_stats {
> sizeof(struct rppx1_exm_stats) + \
> sizeof(struct rppx1_hist_stats) + \
> sizeof(struct rppx1_hist_stats) + \
> - sizeof(struct rppx1_hist_stats))
> + sizeof(struct rppx1_hist_stats) + \
> + sizeof(struct rppx1_bls_stats) + \
> + sizeof(struct rppx1_bls_stats))
>
> #endif /* __UAPI_RPP_X1_CONFIG_H */
> --
> 2.54.0
>
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [v8 09/14] media: rppx1: ccor: Add support for color correction matrix
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (7 preceding siblings ...)
2026-05-04 1:05 ` [v8 08/14] media: rppx1: bls: Add support for black level compensation Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-04 1:05 ` [v8 10/14] media: rppx1: lsc: Add support for lens shade correction Niklas Söderlund
` (5 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the color correction matrix
configuration parameters. It uses the RPPX1 framework for parameters and
its writer abstraction to allow the user to control how, and when,
configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 1 +
.../platform/dreamchip/rppx1/rpp_params.c | 4 ++
.../platform/dreamchip/rppx1/rppx1_ccor.c | 61 +++++++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 28 ++++++++-
4 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 9c761448717b..121fea99b237 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -87,6 +87,7 @@ union rppx1_params_block {
struct v4l2_isp_params_block_header header;
struct rppx1_bls_params bls;
struct rppx1_awbg_params awbg;
+ struct rppx1_ccor_params ccor;
struct rppx1_hist_params hist;
struct rppx1_exm_params exm;
struct rppx1_wbmeas_params wbmeas;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 7b006c68381b..57829e3b533d 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -20,6 +20,7 @@ rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor),
RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist),
RPPX1_PARAMS_BLOCK_INFO(HIST_POST, hist),
@@ -66,6 +67,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
module = &rpp->pre1.awbg;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST:
+ module = &rpp->post.ccor;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_HIST_POST:
module = &rpp->post.hist;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
index 4754b0bbce0a..1655c58c8d59 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
@@ -68,9 +68,70 @@ static int rppx1_ccor_start(struct rpp_module *mod,
return 0;
}
+static int
+rppx1_ccor_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_ccor_params *cfg = &block->ccor;
+
+ /* If the modules is disabled, configure in bypass mode. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + CCOR_COEFF_REG(0), 0x1000);
+ write(priv, mod->base + CCOR_COEFF_REG(1), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(2), 0x0000);
+
+ write(priv, mod->base + CCOR_COEFF_REG(3), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(4), 0x1000);
+ write(priv, mod->base + CCOR_COEFF_REG(5), 0x0000);
+
+ write(priv, mod->base + CCOR_COEFF_REG(6), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(7), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(8), 0x1000);
+
+ write(priv, mod->base + CCOR_OFFSET_R_REG, 0x00000000);
+ write(priv, mod->base + CCOR_OFFSET_G_REG, 0x00000000);
+ write(priv, mod->base + CCOR_OFFSET_B_REG, 0x00000000);
+
+ return 0;
+ }
+
+ /*
+ * Coefficient n for color correction matrix.
+ *
+ * RPP coefficients are 16-bit signed fixed-point numbers with 4 bit
+ * integer and 12 bit fractional part ranging from -8 (0x8000) to
+ * +7.9996 (0x7FFF). 0 is represented by 0x0000 and a coefficient
+ * value of 1 as 0x1000.
+ */
+ write(priv, mod->base + CCOR_COEFF_REG(0), cfg->coeff[0][0]);
+ write(priv, mod->base + CCOR_COEFF_REG(1), cfg->coeff[0][1]);
+ write(priv, mod->base + CCOR_COEFF_REG(2), cfg->coeff[0][2]);
+
+ write(priv, mod->base + CCOR_COEFF_REG(3), cfg->coeff[1][0]);
+ write(priv, mod->base + CCOR_COEFF_REG(4), cfg->coeff[1][1]);
+ write(priv, mod->base + CCOR_COEFF_REG(5), cfg->coeff[1][2]);
+
+ write(priv, mod->base + CCOR_COEFF_REG(6), cfg->coeff[2][0]);
+ write(priv, mod->base + CCOR_COEFF_REG(7), cfg->coeff[2][1]);
+ write(priv, mod->base + CCOR_COEFF_REG(8), cfg->coeff[2][2]);
+
+ /*
+ * Offset for color components correction matrix.
+ *
+ * Values are a two's complement integer with one sign bit.
+ */
+ write(priv, mod->base + CCOR_OFFSET_R_REG, cfg->offset[0]);
+ write(priv, mod->base + CCOR_OFFSET_G_REG, cfg->offset[1]);
+ write(priv, mod->base + CCOR_OFFSET_B_REG, cfg->offset[2]);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_ccor_ops = {
.probe = rppx1_ccor_probe,
.start = rppx1_ccor_start,
+ .fill_params = rppx1_ccor_fill_params,
};
static int rppx1_ccor_csm_start(struct rpp_module *mod,
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index b181ef08d093..3030369a2fbc 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -86,6 +86,7 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement
* @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction
* @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction
+ * @RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: POST pipe Color Correction
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -99,6 +100,7 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_HIST_POST,
RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1,
RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2,
+ RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST,
};
/**
@@ -420,6 +422,29 @@ struct rppx1_bls_params {
struct rppx1_bls_fixed fixed;
};
+/**
+ * struct rppx1_ccor_params - Color CORrection configuration
+ *
+ * The CCOR (Color Correction) module is available on the MAIN_POST pipe. It
+ * performs color space correction on a pixel-per-pixel basis using a 3x3 matrix
+ * of coefficients and per-color channel offsets.
+ *
+ * The matrix coefficients are represented as 16 bits signed fixed point values
+ * in Q4.12 format ranging from -8 to +7.999.
+ *
+ * The per-channel color offsets are represented as 2's complement values
+ * stored in 25 bits ranging from -16777216 to 16777215.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST)
+ * @coeff: color correction matrix coefficients, 16 bits signed Q4.12
+ * @offset: R, G, B offsets, 2's complement 25 bits
+ */
+struct rppx1_ccor_params {
+ struct v4l2_isp_params_block_header header;
+ __u16 coeff[3][3];
+ __u32 offset[3];
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -437,7 +462,8 @@ struct rppx1_bls_params {
sizeof(struct rppx1_hist_params) + \
sizeof(struct rppx1_hist_params) + \
sizeof(struct rppx1_bls_params) + \
- sizeof(struct rppx1_bls_params))
+ sizeof(struct rppx1_bls_params) + \
+ sizeof(struct rppx1_ccor_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [v8 10/14] media: rppx1: lsc: Add support for lens shade correction
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (8 preceding siblings ...)
2026-05-04 1:05 ` [v8 09/14] media: rppx1: ccor: Add support for color correction matrix Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-04 1:05 ` [v8 11/14] media: rppx1: ga: Add support for gamma out correction Niklas Söderlund
` (4 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the lens shade correction
configuration parameters. It uses the RPPX1 framework for parameters and
its writer abstraction to allow the user to control how, and when,
configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 1 +
.../platform/dreamchip/rppx1/rpp_params.c | 5 +
.../platform/dreamchip/rppx1/rppx1_lsc.c | 119 ++++++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 54 +++++++-
4 files changed, 178 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 121fea99b237..11c38ea843f4 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -86,6 +86,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
union rppx1_params_block {
struct v4l2_isp_params_block_header header;
struct rppx1_bls_params bls;
+ struct rppx1_lsc_params lsc;
struct rppx1_awbg_params awbg;
struct rppx1_ccor_params ccor;
struct rppx1_hist_params hist;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 57829e3b533d..f66640aff5b7 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -18,6 +18,8 @@ static const struct v4l2_isp_block_type_info
rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls),
RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
+ RPPX1_PARAMS_BLOCK_INFO(LSC_PRE1, lsc),
+ RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor),
@@ -64,6 +66,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
module = &rpp->pre1.bls;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1:
+ module = &rpp->pre1.lsc;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
module = &rpp->pre1.awbg;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
index e8acdf744956..541efbae379b 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
@@ -54,6 +54,10 @@
#define LSC_TABLE_SEL_REG 0x00a8
#define LSC_STATUS_REG 0x00ac
+#define LSC_R_TABLE_DATA_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 12))
+#define LSC_GRAD_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 16))
+#define LSC_SIZE_VALUE(v1, v2) (((v1) & 0x1ff) | (((v2) & 0x1ff) << 16))
+
static int rppx1_lsc_probe(struct rpp_module *mod)
{
/* Version check. */
@@ -63,6 +67,121 @@ static int rppx1_lsc_probe(struct rpp_module *mod)
return 0;
}
+static int
+rppx1_lsc_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_lsc_params *cfg = &block->lsc;
+ const __u16 *v;
+
+ /* Always disable module as it needs be disabled before configuring. */
+ write(priv, mod->base + LSC_CTRL_REG, 0);
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)
+ return 0;
+
+ /*
+ * Program the color correction sectors.
+ *
+ * There are two tables to one can program and switch between. As the
+ * RPPX1 supports preparing a buffer of commands to be applied later
+ * only use table 0. This works as long as the ISP is not used in
+ * inline-mode.
+ *
+ * For inline-mode support using DMA for configuration is not possible
+ * so this is not an issue, but needs to be address if inline-mode
+ * support is added to the driver.
+ */
+
+ /* Start writing at beginning of table 0. */
+ write(priv, mod->base + LSC_R_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_GR_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_B_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_GB_TABLE_ADDR_REG, 0);
+
+ /* Program data tables. */
+ for (unsigned int i = 0; i < RPPX1_LSC_SAMPLES_MAX; i++) {
+ const __u16 *r = cfg->r_data[i];
+ const __u16 *gr = cfg->gr_data[i];
+ const __u16 *b = cfg->b_data[i];
+ const __u16 *gb = cfg->gb_data[i];
+ unsigned int j;
+
+ for (j = 0; j < RPPX1_LSC_SAMPLES_MAX - 1; j += 2) {
+ write(priv, mod->base + LSC_R_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(r[j], r[j + 1]));
+ write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gr[j], gr[j + 1]));
+ write(priv, mod->base + LSC_B_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(b[j], b[j + 1]));
+ write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gb[j], gb[j + 1]));
+ }
+
+ write(priv, mod->base + LSC_R_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(r[j], 0));
+ write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gr[j], 0));
+ write(priv, mod->base + LSC_B_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(b[j], 0));
+ write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gb[j], 0));
+ }
+
+ /* Activate table 0. */
+ write(priv, mod->base + LSC_TABLE_SEL_REG, 0);
+
+ /*
+ * Program X- and Y- sizes, and gradients.
+ */
+
+ v = cfg->x_grad;
+ write(priv, mod->base + LSC_XGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_XGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_XGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_XGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_XGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ v = cfg->y_grad;
+ write(priv, mod->base + LSC_YGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_YGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_YGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_YGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_YGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ v = cfg->x_sect_size;
+ write(priv, mod->base + LSC_XSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_XSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_XSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_XSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_XSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ v = cfg->y_sect_size;
+ write(priv, mod->base + LSC_YSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_YSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_YSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_YSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_YSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ /* Enable module. */
+ write(priv, mod->base + LSC_CTRL_REG, LSC_CTRL_LSC_EN);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_lsc_ops = {
.probe = rppx1_lsc_probe,
+ .fill_params = rppx1_lsc_fill_params,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 3030369a2fbc..e9d8c379bd50 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -87,6 +87,8 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction
* @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction
* @RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: POST pipe Color Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1: PRE1 pipe Lens Shading Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 Lens Shading Correction
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -101,6 +103,8 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1,
RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2,
RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST,
+ RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1,
+ RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2,
};
/**
@@ -445,6 +449,52 @@ struct rppx1_ccor_params {
__u32 offset[3];
};
+/* Lens Shade Correction */
+#define RPPX1_LSC_SAMPLES_MAX 17
+#define RPPX1_LSC_NUM_SECTORS 16
+
+/**
+ * struct rppx1_lsc_params - Lens Shading Correction configuration
+ *
+ * The RPP-X1 Lens shading correction module is available on the PRE1 and PRE2
+ * pre-fusion pipes. Userspace selects which pipe to operate by setting the
+ * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1 or
+ * RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2.
+ *
+ * The module applies per-color channel correction factors @r_data, @gr_data,
+ * @gb_data and @b_data as a 16x16 grid mapped on the image. The size of each
+ * grid segment is expressed by the @x_sect_size and @y_sect_size arrays. Each
+ * segment shall be at least 8 pixels in size and the sum of all horizontal
+ * segments @x_sect_size shall match the input frame size width.
+ *
+ * The correction factors values are expressed as unsigned Q2.10 integers
+ * ranging from 1 to 3.999.
+ *
+ * Pre-calculated interpolation factors shall be provided in the @x_grad
+ * and @y_grad fields, expressed as 12 bits integer values.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LSC)
+ * @r_data: correction factors for the red channel in Q2.10 format
+ * @gr_data: correction factors for the green (red) channel in Q2.10 format
+ * @gb_data: correction factors for the green (blue) channel in Q2.10 format
+ * @b_data: correction factors for the blue channel in Q2.10 format
+ * @x_grad: Interpolation gradients for each horizontal sector (12 bits)
+ * @y_grad: Interpolation gradients for each vertical sector (12 bits)
+ * @x_sect_size: Horizontal sectors sizes
+ * @y_sect_size: Vertical sectors sizes
+ */
+struct rppx1_lsc_params {
+ struct v4l2_isp_params_block_header header;
+ __u16 r_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+ __u16 gr_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+ __u16 gb_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+ __u16 b_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+ __u16 x_grad[RPPX1_LSC_NUM_SECTORS];
+ __u16 y_grad[RPPX1_LSC_NUM_SECTORS];
+ __u16 x_sect_size[RPPX1_LSC_NUM_SECTORS];
+ __u16 y_sect_size[RPPX1_LSC_NUM_SECTORS];
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -463,7 +513,9 @@ struct rppx1_ccor_params {
sizeof(struct rppx1_hist_params) + \
sizeof(struct rppx1_bls_params) + \
sizeof(struct rppx1_bls_params) + \
- sizeof(struct rppx1_ccor_params))
+ sizeof(struct rppx1_ccor_params) + \
+ sizeof(struct rppx1_lsc_params) + \
+ sizeof(struct rppx1_lsc_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [v8 11/14] media: rppx1: ga: Add support for gamma out correction
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (9 preceding siblings ...)
2026-05-04 1:05 ` [v8 10/14] media: rppx1: lsc: Add support for lens shade correction Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-04 1:05 ` [v8 12/14] media: rppx1: db: Add support for debayering filters Niklas Söderlund
` (3 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the gamma out correction
configuration parameters. It uses the RPPX1 framework for parameters and
its writer abstraction to allow the user to control how, and when,
configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 1 +
.../platform/dreamchip/rppx1/rpp_params.c | 5 ++
.../media/platform/dreamchip/rppx1/rppx1_ga.c | 43 ++++++++++++++-
.../uapi/linux/media/dreamchip/rppx1-config.h | 53 ++++++++++++++++++-
4 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 11c38ea843f4..830ef0df7228 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -92,6 +92,7 @@ union rppx1_params_block {
struct rppx1_hist_params hist;
struct rppx1_exm_params exm;
struct rppx1_wbmeas_params wbmeas;
+ struct rppx1_ga_params ga;
};
union rppx1_stats_block {
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index f66640aff5b7..317ed715f1de 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -29,6 +29,8 @@ rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm),
RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm),
RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
+ RPPX1_PARAMS_BLOCK_INFO(GA_HV, ga),
+ RPPX1_PARAMS_BLOCK_INFO(GA_MV, ga),
};
int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
@@ -84,6 +86,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST:
module = &rpp->post.wbmeas;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_GA_HV:
+ module = &rpp->hv.ga;
+ break;
default:
module = NULL;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
index d6c7f951cf29..5dec868c1ed7 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
@@ -14,9 +14,11 @@
#define GAMMA_OUT_MODE_REG 0x0008
#define GAMMA_OUT_MODE_GAMMA_OUT_EQU_SEGM BIT(0)
-#define GAMMA_OUT_Y_REG_NUM 17
#define GAMMA_OUT_Y_REG(n) (0x000c + (4 * (n)))
+#define GAMMA_OUT_HV_GAMMA_CURVE_MASK GENMASK(11, 0)
+#define GAMMA_OUT_MV_GAMMA_CURVE_MASK GENMASK(23, 0)
+
static int rppx1_ga_probe(struct rpp_module *mod)
{
/* Version check. */
@@ -43,7 +45,46 @@ static int rppx1_ga_start(struct rpp_module *mod,
return 0;
}
+static int
+rppx1_ga_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_ga_params *cfg = &block->ga;
+ u32 mask;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + GAMMA_OUT_ENABLE_REG, 0);
+ return 0;
+ }
+
+ switch (cfg->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_GA_HV:
+ mask = GAMMA_OUT_HV_GAMMA_CURVE_MASK;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_GA_MV:
+ mask = GAMMA_OUT_MV_GAMMA_CURVE_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ write(priv, mod->base + GAMMA_OUT_MODE_REG, cfg->mode);
+
+ for (unsigned int i = 0; i < RPPX1_GA_MAX_SAMPLES; i++)
+ write(priv, mod->base + GAMMA_OUT_Y_REG(i),
+ cfg->gamma_y[i] & mask);
+
+ /* Enable module. */
+ write(priv, mod->base + GAMMA_OUT_ENABLE_REG,
+ GAMMA_OUT_ENABLE_GAMMA_OUT_EN);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_ga_ops = {
.probe = rppx1_ga_probe,
.start = rppx1_ga_start,
+ .fill_params = rppx1_ga_fill_params,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index e9d8c379bd50..d173c0c1bfc0 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -89,6 +89,8 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: POST pipe Color Correction
* @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1: PRE1 pipe Lens Shading Correction
* @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 Lens Shading Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_GA_HV: Human Vision Pipe Gamma Out Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_GA_MV: Machine Vision Gamma Out Correction
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -105,6 +107,8 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST,
RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1,
RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2,
+ RPPX1_PARAMS_BLOCK_TYPE_GA_HV,
+ RPPX1_PARAMS_BLOCK_TYPE_GA_MV,
};
/**
@@ -495,6 +499,51 @@ struct rppx1_lsc_params {
__u16 y_sect_size[RPPX1_LSC_NUM_SECTORS];
};
+/* Gamma Out */
+#define RPPX1_GA_MAX_SAMPLES 17
+
+/**
+ * enum rppx1_ga_seg_mode - Gamma out curve segmentation mode
+ *
+ * Segmentation mode of the 16 input sampling points for the Gamma Out
+ * Correction module.
+ *
+ * @RPPX1_GA_SEG_MODE_LOGARITHMIC: logarithmic-like segmentation mode
+ * @RPPX1_GA_SEG_MODE_EQUIDISTANT: equidistant segmentation mode
+ */
+enum rppx1_ga_seg_mode {
+ RPPX1_GA_SEG_MODE_LOGARITHMIC,
+ RPPX1_GA_SEG_MODE_EQUIDISTANT
+};
+
+/**
+ * struct rppx1_ga_params - Gamma Out Correction configuration
+ *
+ * The Gamma Out Correction module is available on the Human Vision Output
+ * Pipe (HV) and the Machine Vision Output Pipe (MV). Userspace selects
+ * which pipe to operate by setting the @header.type field to
+ * RPPX1_PARAMS_BLOCK_TYPE_GA_HV or RPPX1_PARAMS_BLOCK_TYPE_GA_MV.
+ *
+ * The module allows to apply a @gamma_y gamma correction curve to RGB data
+ * represented as a table of 16 entries. The 16 input sampling points can be
+ * equidistant or segmented using a logarithmic scale according to the value of
+ * @mode.
+ *
+ * The gamma curve values are 12 bits on the HV output pipe and 24 bits on the
+ * MV output pipe. Userspace is expected to provide the curve values with a
+ * bit-depth matching the one of pipe in use.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_GA_HV or
+ * type = RPPX1_PARAMS_BLOCK_TYPE_GA_MV)
+ * @mode: gamma curve input segmentation mode (see rppx1_ga_seg_mode)
+ * @gamma_y: gamma out curve y-axis values
+ */
+struct rppx1_ga_params {
+ struct v4l2_isp_params_block_header header;
+ __u8 mode;
+ __u32 gamma_y[RPPX1_GA_MAX_SAMPLES];
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -515,7 +564,9 @@ struct rppx1_lsc_params {
sizeof(struct rppx1_bls_params) + \
sizeof(struct rppx1_ccor_params) + \
sizeof(struct rppx1_lsc_params) + \
- sizeof(struct rppx1_lsc_params))
+ sizeof(struct rppx1_lsc_params) + \
+ sizeof(struct rppx1_ga_params) + \
+ sizeof(struct rppx1_ga_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [v8 12/14] media: rppx1: db: Add support for debayering filters
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (10 preceding siblings ...)
2026-05-04 1:05 ` [v8 11/14] media: rppx1: ga: Add support for gamma out correction Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 15:54 ` Jacopo Mondi
2026-05-04 1:05 ` [v8 13/14] media: rppx1: bd: Add support for bilateral denoising Niklas Söderlund
` (2 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the debayering filters
configuration parameters. It uses the RPPX1 framework for parameters and
its writer abstraction to allow the user to control how, and when,
configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 2 +
.../platform/dreamchip/rppx1/rpp_params.c | 7 ++
.../media/platform/dreamchip/rppx1/rppx1_db.c | 82 +++++++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 58 ++++++++++++-
4 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 830ef0df7228..064da13082fe 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -88,6 +88,8 @@ union rppx1_params_block {
struct rppx1_bls_params bls;
struct rppx1_lsc_params lsc;
struct rppx1_awbg_params awbg;
+ struct rppx1_db_demosaic_params db_demosaic;
+ struct rppx1_db_filter_params db_filter;
struct rppx1_ccor_params ccor;
struct rppx1_hist_params hist;
struct rppx1_exm_params exm;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 317ed715f1de..3320ca3998bd 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -22,6 +22,8 @@ rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(DB_DEMOSAIC_POST, db_demosaic),
+ RPPX1_PARAMS_BLOCK_INFO(DB_FILTER_POST, db_filter),
RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor),
RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist),
@@ -74,6 +76,11 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
module = &rpp->pre1.awbg;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST:
+ case RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST:
+ /* Both types handled by the same block. */
+ module = &rpp->post.db;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST:
module = &rpp->post.ccor;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
index 5e233896cfc8..5571b3a9562d 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
@@ -39,6 +39,88 @@ static int rppx1_db_probe(struct rpp_module *mod)
return 0;
}
+static int
+rppx1_db_fill_params_demosaic(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_db_demosaic_params *cfg = &block->db_demosaic;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + DEMOSAIC_REG, 0x400);
+ return 0;
+ }
+
+ /* Native threshold is at RPP 16-bit precision. */
+ write(priv, mod->base + DEMOSAIC_REG, cfg->demosaic_th);
+
+ return 0;
+}
+
+static int
+rppx1_db_fill_params_filter(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_db_filter_params *cfg = &block->db_filter;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + FILT_MODE_REG, 0);
+ return 0;
+ }
+
+ /* Native values are at RPP 18-bit precision. */
+ write(priv, mod->base + FILT_THRESH_BL0_REG, cfg->thresh_bl0);
+ write(priv, mod->base + FILT_THRESH_BL0_REG, cfg->thresh_bl1);
+ write(priv, mod->base + FILT_THRESH_SH0_REG, cfg->thresh_sh0);
+ write(priv, mod->base + FILT_THRESH_SH1_REG, cfg->thresh_sh1);
+
+ /* Native values are at RPP 8-bit precision. */
+ write(priv, mod->base + FILT_FAC_BL0_REG, cfg->fac_bl0);
+ write(priv, mod->base + FILT_FAC_BL1_REG, cfg->fac_bl1);
+ write(priv, mod->base + FILT_FAC_MID_REG, cfg->fac_mid);
+ write(priv, mod->base + FILT_FAC_SH0_REG, cfg->fac_sh0);
+ write(priv, mod->base + FILT_FAC_SH1_REG, cfg->fac_sh1);
+
+ /*
+ * The lum_weight field is provided in RPP register format:
+ *
+ * 31 unused
+ * 30:28 lum_weight_gain
+ * 27:24 unused
+ * 23:12 lum_weight_kink
+ * 11:0 lum_weight_min
+ */
+ write(priv, mod->base + FILT_LUM_WEIGHT_REG, cfg->lum_weight);
+
+ write(priv, mod->base + FILT_MODE_REG,
+ (cfg->chr_v_mode << 4) |
+ (cfg->chr_h_mode << 6) |
+ (cfg->grn_stage1 << 8) |
+ (cfg->mode ? FILT_MODE_FILT_MODE : 0) |
+ FILT_MODE_FILT_ENABLE);
+
+ return 0;
+}
+
+static int
+rppx1_db_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST:
+ return rppx1_db_fill_params_demosaic(mod, block, write, priv);
+ case RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST:
+ return rppx1_db_fill_params_filter(mod, block, write, priv);
+ }
+
+ return -EINVAL;
+}
+
const struct rpp_module_ops rppx1_db_ops = {
.probe = rppx1_db_probe,
+ .fill_params = rppx1_db_fill_params,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index d173c0c1bfc0..378b18dbc48d 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -91,6 +91,8 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 Lens Shading Correction
* @RPPX1_PARAMS_BLOCK_TYPE_GA_HV: Human Vision Pipe Gamma Out Correction
* @RPPX1_PARAMS_BLOCK_TYPE_GA_MV: Machine Vision Gamma Out Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST: Debayer demosaicing
+ * @RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST: Debayer filtering
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -109,6 +111,8 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2,
RPPX1_PARAMS_BLOCK_TYPE_GA_HV,
RPPX1_PARAMS_BLOCK_TYPE_GA_MV,
+ RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST,
+ RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST,
};
/**
@@ -544,6 +548,56 @@ struct rppx1_ga_params {
__u32 gamma_y[RPPX1_GA_MAX_SAMPLES];
};
+/**
+ * struct rppx1_db_demosaic_params - Debayer demosaic configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST)
+ * @demosaic_th: threshold for texture detection, 16-bit
+ */
+struct rppx1_db_demosaic_params {
+ struct v4l2_isp_params_block_header header;
+ __u16 demosaic_th;
+};
+
+/**
+ * struct rppx1_db_filter_params - Debayer filter (denoise) configuration
+ *
+ * RPP-X1 thresholds are 18-bit and factors are 8-bit.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST)
+ * @mode: filter mode
+ * @grn_stage1: green filter stage 1 select (range 0x0...0x8)
+ * @chr_h_mode: chroma filter horizontal mode
+ * @chr_v_mode: chroma filter vertical mode
+ * @thresh_bl0: If thresh_bl1 < sum_grad < thresh_bl0 then fac_bl0 is selected (blurring th)
+ * @thresh_bl1: If sum_grad < thresh_bl1 then fac_bl1 is selected (blurring th)
+ * @thresh_sh0: If thresh_sh0 < sum_grad < thresh_sh1 then thresh_sh0 is selected (sharpening th)
+ * @thresh_sh1: If thresh_sh1 < sum_grad then thresh_sh1 is selected (sharpening th)
+ * @lum_weight: luminance weight, min (bits 0:11), kink (bits 12:23), gain (bits 28:30)
+ * @fac_sh1: filter factor for sharp1 level
+ * @fac_sh0: filter factor for sharp0 level
+ * @fac_mid: filter factor for mid level and for static filter mode
+ * @fac_bl0: filter factor for blur0 level
+ * @fac_bl1: filter factor for blur1 level (max blur)
+ */
+struct rppx1_db_filter_params {
+ struct v4l2_isp_params_block_header header;
+ __u32 mode;
+ __u8 grn_stage1;
+ __u8 chr_h_mode;
+ __u8 chr_v_mode;
+ __u32 thresh_bl0;
+ __u32 thresh_bl1;
+ __u32 thresh_sh0;
+ __u32 thresh_sh1;
+ __u32 lum_weight;
+ __u32 fac_sh1;
+ __u32 fac_sh0;
+ __u32 fac_mid;
+ __u32 fac_bl0;
+ __u32 fac_bl1;
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -566,7 +620,9 @@ struct rppx1_ga_params {
sizeof(struct rppx1_lsc_params) + \
sizeof(struct rppx1_lsc_params) + \
sizeof(struct rppx1_ga_params) + \
- sizeof(struct rppx1_ga_params))
+ sizeof(struct rppx1_ga_params) + \
+ sizeof(struct rppx1_db_demosaic_params) + \
+ sizeof(struct rppx1_db_filter_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 12/14] media: rppx1: db: Add support for debayering filters
2026-05-04 1:05 ` [v8 12/14] media: rppx1: db: Add support for debayering filters Niklas Söderlund
@ 2026-05-06 15:54 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 15:54 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Niklas
On Mon, May 04, 2026 at 03:05:54AM +0200, Niklas Söderlund wrote:
> Extend the RPPX1 driver to allow setting the debayering filters
> configuration parameters. It uses the RPPX1 framework for parameters and
> its writer abstraction to allow the user to control how, and when,
> configuration is applied to the RPPX1.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> ---
> .../platform/dreamchip/rppx1/rpp_module.h | 2 +
> .../platform/dreamchip/rppx1/rpp_params.c | 7 ++
> .../media/platform/dreamchip/rppx1/rppx1_db.c | 82 +++++++++++++++++++
> .../uapi/linux/media/dreamchip/rppx1-config.h | 58 ++++++++++++-
As this block still doesn't have a user in libcamera, but I understand
there might be value in having the module upstream, what if we
upstream rppx1_db.c but we don't commit to a uAPI yet ? We can keep
the types you have defined in the uAPI internal to the driver for now?
> 4 files changed, 148 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> index 830ef0df7228..064da13082fe 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> @@ -88,6 +88,8 @@ union rppx1_params_block {
> struct rppx1_bls_params bls;
> struct rppx1_lsc_params lsc;
> struct rppx1_awbg_params awbg;
> + struct rppx1_db_demosaic_params db_demosaic;
> + struct rppx1_db_filter_params db_filter;
> struct rppx1_ccor_params ccor;
> struct rppx1_hist_params hist;
> struct rppx1_exm_params exm;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> index 317ed715f1de..3320ca3998bd 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> @@ -22,6 +22,8 @@ rppx1_ext_params_blocks_info[] = {
> RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
> RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
> RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
> + RPPX1_PARAMS_BLOCK_INFO(DB_DEMOSAIC_POST, db_demosaic),
> + RPPX1_PARAMS_BLOCK_INFO(DB_FILTER_POST, db_filter),
> RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor),
> RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
> RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist),
> @@ -74,6 +76,11 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
> module = &rpp->pre1.awbg;
> break;
> + case RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST:
> + case RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST:
> + /* Both types handled by the same block. */
> + module = &rpp->post.db;
> + break;
> case RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST:
> module = &rpp->post.ccor;
> break;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
> index 5e233896cfc8..5571b3a9562d 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
> @@ -39,6 +39,88 @@ static int rppx1_db_probe(struct rpp_module *mod)
> return 0;
> }
>
> +static int
> +rppx1_db_fill_params_demosaic(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv)
> +{
> + const struct rppx1_db_demosaic_params *cfg = &block->db_demosaic;
> +
> + /* If the modules is disabled, simply bypass it. */
> + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
> + write(priv, mod->base + DEMOSAIC_REG, 0x400);
> + return 0;
> + }
> +
> + /* Native threshold is at RPP 16-bit precision. */
> + write(priv, mod->base + DEMOSAIC_REG, cfg->demosaic_th);
> +
> + return 0;
> +}
> +
> +static int
> +rppx1_db_fill_params_filter(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv)
> +{
> + const struct rppx1_db_filter_params *cfg = &block->db_filter;
> +
> + /* If the modules is disabled, simply bypass it. */
> + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
> + write(priv, mod->base + FILT_MODE_REG, 0);
> + return 0;
> + }
> +
> + /* Native values are at RPP 18-bit precision. */
> + write(priv, mod->base + FILT_THRESH_BL0_REG, cfg->thresh_bl0);
> + write(priv, mod->base + FILT_THRESH_BL0_REG, cfg->thresh_bl1);
> + write(priv, mod->base + FILT_THRESH_SH0_REG, cfg->thresh_sh0);
> + write(priv, mod->base + FILT_THRESH_SH1_REG, cfg->thresh_sh1);
> +
> + /* Native values are at RPP 8-bit precision. */
> + write(priv, mod->base + FILT_FAC_BL0_REG, cfg->fac_bl0);
> + write(priv, mod->base + FILT_FAC_BL1_REG, cfg->fac_bl1);
> + write(priv, mod->base + FILT_FAC_MID_REG, cfg->fac_mid);
> + write(priv, mod->base + FILT_FAC_SH0_REG, cfg->fac_sh0);
> + write(priv, mod->base + FILT_FAC_SH1_REG, cfg->fac_sh1);
> +
> + /*
> + * The lum_weight field is provided in RPP register format:
> + *
> + * 31 unused
> + * 30:28 lum_weight_gain
> + * 27:24 unused
> + * 23:12 lum_weight_kink
> + * 11:0 lum_weight_min
> + */
> + write(priv, mod->base + FILT_LUM_WEIGHT_REG, cfg->lum_weight);
> +
> + write(priv, mod->base + FILT_MODE_REG,
> + (cfg->chr_v_mode << 4) |
> + (cfg->chr_h_mode << 6) |
> + (cfg->grn_stage1 << 8) |
> + (cfg->mode ? FILT_MODE_FILT_MODE : 0) |
> + FILT_MODE_FILT_ENABLE);
> +
> + return 0;
> +}
> +
> +static int
> +rppx1_db_fill_params(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv)
> +{
> + switch (block->header.type) {
> + case RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST:
> + return rppx1_db_fill_params_demosaic(mod, block, write, priv);
> + case RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST:
> + return rppx1_db_fill_params_filter(mod, block, write, priv);
> + }
> +
> + return -EINVAL;
> +}
> +
> const struct rpp_module_ops rppx1_db_ops = {
> .probe = rppx1_db_probe,
> + .fill_params = rppx1_db_fill_params,
> };
> diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
> index d173c0c1bfc0..378b18dbc48d 100644
> --- a/include/uapi/linux/media/dreamchip/rppx1-config.h
> +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
> @@ -91,6 +91,8 @@ enum rppx1_meas_chan {
> * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 Lens Shading Correction
> * @RPPX1_PARAMS_BLOCK_TYPE_GA_HV: Human Vision Pipe Gamma Out Correction
> * @RPPX1_PARAMS_BLOCK_TYPE_GA_MV: Machine Vision Gamma Out Correction
> + * @RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST: Debayer demosaicing
> + * @RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST: Debayer filtering
> */
> enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
> @@ -109,6 +111,8 @@ enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2,
> RPPX1_PARAMS_BLOCK_TYPE_GA_HV,
> RPPX1_PARAMS_BLOCK_TYPE_GA_MV,
> + RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST,
> + RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST,
> };
>
> /**
> @@ -544,6 +548,56 @@ struct rppx1_ga_params {
> __u32 gamma_y[RPPX1_GA_MAX_SAMPLES];
> };
>
> +/**
> + * struct rppx1_db_demosaic_params - Debayer demosaic configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST)
> + * @demosaic_th: threshold for texture detection, 16-bit
> + */
> +struct rppx1_db_demosaic_params {
> + struct v4l2_isp_params_block_header header;
> + __u16 demosaic_th;
> +};
> +
> +/**
> + * struct rppx1_db_filter_params - Debayer filter (denoise) configuration
> + *
> + * RPP-X1 thresholds are 18-bit and factors are 8-bit.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST)
> + * @mode: filter mode
> + * @grn_stage1: green filter stage 1 select (range 0x0...0x8)
> + * @chr_h_mode: chroma filter horizontal mode
> + * @chr_v_mode: chroma filter vertical mode
> + * @thresh_bl0: If thresh_bl1 < sum_grad < thresh_bl0 then fac_bl0 is selected (blurring th)
> + * @thresh_bl1: If sum_grad < thresh_bl1 then fac_bl1 is selected (blurring th)
> + * @thresh_sh0: If thresh_sh0 < sum_grad < thresh_sh1 then thresh_sh0 is selected (sharpening th)
> + * @thresh_sh1: If thresh_sh1 < sum_grad then thresh_sh1 is selected (sharpening th)
> + * @lum_weight: luminance weight, min (bits 0:11), kink (bits 12:23), gain (bits 28:30)
> + * @fac_sh1: filter factor for sharp1 level
> + * @fac_sh0: filter factor for sharp0 level
> + * @fac_mid: filter factor for mid level and for static filter mode
> + * @fac_bl0: filter factor for blur0 level
> + * @fac_bl1: filter factor for blur1 level (max blur)
> + */
> +struct rppx1_db_filter_params {
> + struct v4l2_isp_params_block_header header;
> + __u32 mode;
> + __u8 grn_stage1;
> + __u8 chr_h_mode;
> + __u8 chr_v_mode;
> + __u32 thresh_bl0;
> + __u32 thresh_bl1;
> + __u32 thresh_sh0;
> + __u32 thresh_sh1;
> + __u32 lum_weight;
> + __u32 fac_sh1;
> + __u32 fac_sh0;
> + __u32 fac_mid;
> + __u32 fac_bl0;
> + __u32 fac_bl1;
> +};
> +
> /**
> * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> *
> @@ -566,7 +620,9 @@ struct rppx1_ga_params {
> sizeof(struct rppx1_lsc_params) + \
> sizeof(struct rppx1_lsc_params) + \
> sizeof(struct rppx1_ga_params) + \
> - sizeof(struct rppx1_ga_params))
> + sizeof(struct rppx1_ga_params) + \
> + sizeof(struct rppx1_db_demosaic_params) + \
> + sizeof(struct rppx1_db_filter_params))
>
> /* ---------------------------------------------------------------------------
> * Statistics Structures
> --
> 2.54.0
>
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [v8 13/14] media: rppx1: bd: Add support for bilateral denoising
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (11 preceding siblings ...)
2026-05-04 1:05 ` [v8 12/14] media: rppx1: db: Add support for debayering filters Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-04 1:05 ` [v8 14/14] media: rppx1: lin: Add support for gamma sensor linearization Niklas Söderlund
2026-05-06 12:19 ` [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Geert Uytterhoeven
14 siblings, 0 replies; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Niklas Söderlund
Extend the RPPX1 driver to allow setting the bilateral denoising
configuration parameters. It uses the RPPX1 framework for parameters and
its writer abstraction to allow the user to control how, and when,
configuration is applied to the RPPX1.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Co-developed-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
.../platform/dreamchip/rppx1/rpp_module.h | 1 +
.../platform/dreamchip/rppx1/rpp_params.c | 5 +
.../media/platform/dreamchip/rppx1/rppx1_bd.c | 119 +++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 141 +++++++++++++++++-
4 files changed, 265 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 064da13082fe..136ec4d48054 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -88,6 +88,7 @@ union rppx1_params_block {
struct rppx1_bls_params bls;
struct rppx1_lsc_params lsc;
struct rppx1_awbg_params awbg;
+ struct rppx1_bd_params bd;
struct rppx1_db_demosaic_params db_demosaic;
struct rppx1_db_filter_params db_filter;
struct rppx1_ccor_params ccor;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 3320ca3998bd..edea25293d64 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -22,6 +22,8 @@ rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(BD_PRE1, bd),
+ RPPX1_PARAMS_BLOCK_INFO(BD_PRE2, bd),
RPPX1_PARAMS_BLOCK_INFO(DB_DEMOSAIC_POST, db_demosaic),
RPPX1_PARAMS_BLOCK_INFO(DB_FILTER_POST, db_filter),
RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor),
@@ -76,6 +78,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
module = &rpp->pre1.awbg;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_BD_PRE1:
+ module = &rpp->pre1.bd;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST:
case RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST:
/* Both types handled by the same block. */
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
index acbfbcd59591..08f9eca80b4d 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
@@ -47,6 +47,125 @@ static int rppx1_bd_probe(struct rpp_module *mod)
return 0;
}
+static int
+rppx1_bd_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_bd_params *cfg = &block->bd;
+ unsigned int isp_dpf_mode, spatial_coeff;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + DPF_MODE_REG, 0);
+ return 0;
+ }
+
+ /* Module version 5 adds shadowing for mode and spatial weights. */
+ write(priv, mod->base + DPF_STRENGTH_R_REG, cfg->strength.r);
+ write(priv, mod->base + DPF_STRENGTH_G_REG, cfg->strength.g);
+ write(priv, mod->base + DPF_STRENGTH_B_REG, cfg->strength.b);
+
+ /*
+ * RPP DB module version 4 and later removed the AWB_GAIN_COMP bit.
+ * Always use programmed nf-gains for gain compensation. Emulate the
+ * old behavior by programming default gains when gain compensation
+ * is not requested.
+ */
+ bool awb_gain_comp = false;
+
+ switch (cfg->gain.mode) {
+ case RPPX1_BD_GAIN_USAGE_NF_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = DPF_MODE_USE_NF_GAIN;
+ break;
+ case RPPX1_BD_GAIN_USAGE_LSC_GAINS:
+ isp_dpf_mode = DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RPPX1_BD_GAIN_USAGE_NF_LSC_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = DPF_MODE_USE_NF_GAIN | DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RPPX1_BD_GAIN_USAGE_AWB_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = 0;
+ break;
+ case RPPX1_BD_GAIN_USAGE_AWB_LSC_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RPPX1_BD_GAIN_USAGE_DISABLED:
+ default:
+ isp_dpf_mode = 0;
+ break;
+ }
+
+ /* NOTE: Hardware bit for scale_mode is inverted compared to RkISP1. */
+ if (cfg->nll.scale_mode == RPPX1_BD_SCALE_LINEAR)
+ isp_dpf_mode |= DPF_MODE_NLL_SEGMENTATION;
+ if (cfg->rb_flt.fltsize == RPPX1_BD_FILTERSIZE_9x9)
+ isp_dpf_mode |= DPF_MODE_RB_FILTER_SIZE;
+ if (!cfg->rb_flt.r_enable)
+ isp_dpf_mode |= DPF_MODE_R_FILTER_OFF;
+ if (!cfg->rb_flt.b_enable)
+ isp_dpf_mode |= DPF_MODE_B_FILTER_OFF;
+ if (!cfg->g_flt.gb_enable)
+ isp_dpf_mode |= DPF_MODE_GB_FILTER_OFF;
+ if (!cfg->g_flt.gr_enable)
+ isp_dpf_mode |= DPF_MODE_GR_FILTER_OFF;
+
+ isp_dpf_mode |= DPF_MODE_DPF_ENABLE;
+
+ if (awb_gain_comp) {
+ write(priv, mod->base + DPF_NF_GAIN_B_REG, cfg->gain.nf_b_gain);
+ write(priv, mod->base + DPF_NF_GAIN_R_REG, cfg->gain.nf_r_gain);
+ write(priv, mod->base + DPF_NF_GAIN_GB_REG, cfg->gain.nf_gb_gain);
+ write(priv, mod->base + DPF_NF_GAIN_GR_REG, cfg->gain.nf_gr_gain);
+ } else {
+ write(priv, mod->base + DPF_NF_GAIN_B_REG, 0x100);
+ write(priv, mod->base + DPF_NF_GAIN_R_REG, 0x100);
+ write(priv, mod->base + DPF_NF_GAIN_GB_REG, 0x100);
+ write(priv, mod->base + DPF_NF_GAIN_GR_REG, 0x100);
+ }
+
+ for (unsigned int i = 0; i < RPPX1_BD_NLF_COEFFS_MAX; i++) {
+ write(priv, mod->base + DPF_NLL_G_COEFF_REG(i), cfg->nll.coeff[i]);
+ write(priv, mod->base + DPF_NLL_RB_COEFF_REG(i), cfg->nll.coeff[i]);
+ }
+
+ spatial_coeff = cfg->g_flt.spatial_coeff[0] |
+ (cfg->g_flt.spatial_coeff[1] << 8) |
+ (cfg->g_flt.spatial_coeff[2] << 16) |
+ (cfg->g_flt.spatial_coeff[3] << 24);
+ write(priv, mod->base + DPF_S_WEIGHT_G_1_4_REG, spatial_coeff);
+
+ spatial_coeff = cfg->g_flt.spatial_coeff[4] |
+ (cfg->g_flt.spatial_coeff[5] << 8);
+ write(priv, mod->base + DPF_S_WEIGHT_G_5_6_REG, spatial_coeff);
+
+ spatial_coeff = cfg->rb_flt.spatial_coeff[0] |
+ (cfg->rb_flt.spatial_coeff[1] << 8) |
+ (cfg->rb_flt.spatial_coeff[2] << 16) |
+ (cfg->rb_flt.spatial_coeff[3] << 24);
+ write(priv, mod->base + DPF_S_WEIGHT_RB_1_4_REG, spatial_coeff);
+
+ spatial_coeff = cfg->rb_flt.spatial_coeff[4] |
+ (cfg->rb_flt.spatial_coeff[5] << 8);
+ write(priv, mod->base + DPF_S_WEIGHT_RB_5_6_REG, spatial_coeff);
+
+ /*
+ * Bilateral Denoising does not react on RPP_HDR_UPD::regs_gen_cfg_upd
+ * (see Table 25). A change in configuration needs write of 1 to
+ * RPP_HDR_UPD::regs_cfg_upd.
+ */
+ write(priv, 4, 1);
+
+ write(priv, mod->base + DPF_MODE_REG, isp_dpf_mode);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_bd_ops = {
.probe = rppx1_bd_probe,
+ .fill_params = rppx1_bd_fill_params,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 378b18dbc48d..0246e9af1d1d 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -93,6 +93,8 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_GA_MV: Machine Vision Gamma Out Correction
* @RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST: Debayer demosaicing
* @RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST: Debayer filtering
+ * @RPPX1_PARAMS_BLOCK_TYPE_BD_PRE1: PRE1 pipe De-noise Pre-Filter
+ * @RPPX1_PARAMS_BLOCK_TYPE_BD_PRE2: PRE2 pipe De-noise Pre-Filter
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -113,6 +115,8 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_GA_MV,
RPPX1_PARAMS_BLOCK_TYPE_DB_DEMOSAIC_POST,
RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST,
+ RPPX1_PARAMS_BLOCK_TYPE_BD_PRE1,
+ RPPX1_PARAMS_BLOCK_TYPE_BD_PRE2,
};
/**
@@ -598,6 +602,139 @@ struct rppx1_db_filter_params {
__u32 fac_bl1;
};
+/**
+ * struct rppx1_bd_strength - Bilateral filter strength configuration
+ *
+ * @r: filter strength for RED
+ * @g: filter strength for GREEN
+ * @b: filter strength for BLUE
+ */
+struct rppx1_bd_strength {
+ __u8 r;
+ __u8 g;
+ __u8 b;
+};
+
+/**
+ * enum rppx1_bd_gain_mode - Bilateral filter noise function gain usage mode
+ *
+ * @RPPX1_BD_GAIN_USAGE_DISABLED: gain not used
+ * @RPPX1_BD_GAIN_USAGE_NF_GAINS: use noise function gains
+ * @RPPX1_BD_GAIN_USAGE_LSC_GAINS: use LSC gains
+ * @RPPX1_BD_GAIN_USAGE_NF_LSC_GAINS: use noise function and LSC gains
+ * @RPPX1_BD_GAIN_USAGE_AWB_GAINS: use AWB gains
+ * @RPPX1_BD_GAIN_USAGE_AWB_LSC_GAINS: use AWB and LSC gains
+ */
+enum rppx1_bd_gain_mode {
+ RPPX1_BD_GAIN_USAGE_DISABLED,
+ RPPX1_BD_GAIN_USAGE_NF_GAINS,
+ RPPX1_BD_GAIN_USAGE_LSC_GAINS,
+ RPPX1_BD_GAIN_USAGE_NF_LSC_GAINS,
+ RPPX1_BD_GAIN_USAGE_AWB_GAINS,
+ RPPX1_BD_GAIN_USAGE_AWB_LSC_GAINS,
+};
+
+/**
+ * struct rppx1_bd_gain - Bilateral filter noise function gain configuration
+ *
+ * @mode: gain usage mode (from enum rppx1_bd_gain_mode)
+ * @nf_r_gain: noise function gain replacing AWB gain for red
+ * @nf_b_gain: noise function gain replacing AWB gain for blue
+ * @nf_gr_gain: noise function gain replacing AWB gain for green-in-red
+ * @nf_gb_gain: noise function gain replacing AWB gain for green-in-blue
+ */
+struct rppx1_bd_gain {
+ __u32 mode;
+ __u16 nf_r_gain;
+ __u16 nf_b_gain;
+ __u16 nf_gr_gain;
+ __u16 nf_gb_gain;
+};
+
+#define RPPX1_DB_SPATIAL_COEFFS_MAX 6
+
+/**
+ * struct rppx1_bd_g_flt - Bilateral filter green filter configuration
+ *
+ * @spatial_coeff: spatial weight coefficients
+ * @gr_enable: enable filter for green-in-red pixels
+ * @gb_enable: enable filter for green-in-blue pixels
+ */
+struct rppx1_bd_g_flt {
+ __u8 spatial_coeff[RPPX1_DB_SPATIAL_COEFFS_MAX];
+ __u8 gr_enable;
+ __u8 gb_enable;
+};
+
+/**
+ * enum rppx1_bd_filtersize - Bilateral filter red/blue filter kernel size
+ *
+ * @RPPX1_BD_FILTERSIZE_13x9: 13x9 filter size
+ * @RPPX1_BD_FILTERSIZE_9x9: 9x9 filter size
+ */
+enum rppx1_bd_filtersize {
+ RPPX1_BD_FILTERSIZE_13x9,
+ RPPX1_BD_FILTERSIZE_9x9,
+};
+
+/**
+ * struct rppx1_bd_rb_flt - Bilateral filter red/blue filter configuration
+ *
+ * @fltsize: filter kernel size (from enum rppx1_bd_filtersize)
+ * @spatial_coeff: spatial weight coefficients
+ * @r_enable: enable filter for red pixels
+ * @b_enable: enable filter for blue pixels
+ */
+struct rppx1_bd_rb_flt {
+ __u32 fltsize;
+ __u8 spatial_coeff[RPPX1_DB_SPATIAL_COEFFS_MAX];
+ __u8 r_enable;
+ __u8 b_enable;
+};
+
+#define RPPX1_BD_NLF_COEFFS_MAX 17
+
+/**
+ * enum rppx1_bd_scale_mode - Bilateral filter noise level lookup scale mode
+ *
+ * @RPPX1_BD_SCALE_LINEAR: linear scaling
+ * @RPPX1_BD_SCALE_LOGARITHMIC: logarithmic scaling
+ */
+enum rppx1_bd_scale_mode {
+ RPPX1_BD_SCALE_LINEAR,
+ RPPX1_BD_SCALE_LOGARITHMIC,
+};
+
+/**
+ * struct rppx1_bd_nll - Bilateral filter noise level lookup
+ *
+ * @coeff: noise level lookup coefficients
+ * @scale_mode: noise level lookup scale mode (from enum rppx1_bd_scale_mode)
+ */
+struct rppx1_bd_nll {
+ __u16 coeff[RPPX1_BD_NLF_COEFFS_MAX];
+ __u32 scale_mode;
+};
+
+/**
+ * struct rppx1_bd_params - De-noising Pre-Filter configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BD)
+ * @strength: colour filter strength
+ * @gain: noise function gain
+ * @g_flt: green filter configuration
+ * @rb_flt: red/blue filter configuration
+ * @nll: noise level lookup
+ */
+struct rppx1_bd_params {
+ struct v4l2_isp_params_block_header header;
+ struct rppx1_bd_strength strength;
+ struct rppx1_bd_gain gain;
+ struct rppx1_bd_g_flt g_flt;
+ struct rppx1_bd_rb_flt rb_flt;
+ struct rppx1_bd_nll nll;
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -622,7 +759,9 @@ struct rppx1_db_filter_params {
sizeof(struct rppx1_ga_params) + \
sizeof(struct rppx1_ga_params) + \
sizeof(struct rppx1_db_demosaic_params) + \
- sizeof(struct rppx1_db_filter_params))
+ sizeof(struct rppx1_db_filter_params) + \
+ sizeof(struct rppx1_bd_params) + \
+ sizeof(struct rppx1_bd_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [v8 14/14] media: rppx1: lin: Add support for gamma sensor linearization
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (12 preceding siblings ...)
2026-05-04 1:05 ` [v8 13/14] media: rppx1: bd: Add support for bilateral denoising Niklas Söderlund
@ 2026-05-04 1:05 ` Niklas Söderlund
2026-05-06 15:57 ` Jacopo Mondi
2026-05-06 12:19 ` [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Geert Uytterhoeven
14 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-04 1:05 UTC (permalink / raw)
To: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Cc: Jai Luthra, Niklas Söderlund
From: Jai Luthra <jai.luthra@ideasonboard.com>
Extend the RPPX1 driver to allow setting the gamma sensor linearization
configuration parameters. It uses the RPPX1 framework for parameters and
its writer abstraction to allow the user to control how, and when,
configuration is applied to the RPPX1.
Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
.../platform/dreamchip/rppx1/rpp_module.h | 1 +
.../platform/dreamchip/rppx1/rpp_params.c | 5 ++
.../platform/dreamchip/rppx1/rppx1_lin.c | 55 +++++++++++++++++++
.../uapi/linux/media/dreamchip/rppx1-config.h | 48 +++++++++++++++-
4 files changed, 108 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
index 136ec4d48054..27235fdfb749 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -86,6 +86,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
union rppx1_params_block {
struct v4l2_isp_params_block_header header;
struct rppx1_bls_params bls;
+ struct rppx1_lin_params lin;
struct rppx1_lsc_params lsc;
struct rppx1_awbg_params awbg;
struct rppx1_bd_params bd;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index edea25293d64..1995a80890f4 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -18,6 +18,8 @@ static const struct v4l2_isp_block_type_info
rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls),
RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
+ RPPX1_PARAMS_BLOCK_INFO(LIN_PRE1, lin),
+ RPPX1_PARAMS_BLOCK_INFO(LIN_PRE2, lin),
RPPX1_PARAMS_BLOCK_INFO(LSC_PRE1, lsc),
RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
@@ -72,6 +74,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
module = &rpp->pre1.bls;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1:
+ module = &rpp->pre1.lin;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1:
module = &rpp->pre1.lsc;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
index f595f56a292e..cc8efe3b3f2c 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
@@ -24,6 +24,11 @@
#define LIN_B_Y_REG_NUM 17
#define LIN_B_Y_REG(n) (0x0098 + (4 * (n)))
+#define LIN_PRE1_DEGAMMA_CURVE_MASK GENMASK(23, 0)
+#define LIN_PRE1_SAMPLE_POINTS_MASK GENMASK(3, 0)
+#define LIN_PRE2_DEGAMMA_CURVE_MASK GENMASK(11, 0)
+#define LIN_PRE2_SAMPLE_POINTS_MASK GENMASK(2, 0)
+
static int rppx1_lin_probe(struct rpp_module *mod)
{
/* Version check. */
@@ -52,7 +57,57 @@ static int rppx1_lin_start(struct rpp_module *mod,
return 0;
}
+static int rppx1_lin_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_lin_params *cfg = &block->lin;
+ u8 sample_mask;
+ u32 mask;
+
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + LIN_ENABLE_REG, 0);
+ return 0;
+ }
+
+ switch (cfg->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1:
+ mask = LIN_PRE1_DEGAMMA_CURVE_MASK;
+ sample_mask = LIN_PRE1_SAMPLE_POINTS_MASK;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2:
+ mask = LIN_PRE2_DEGAMMA_CURVE_MASK;
+ sample_mask = LIN_PRE2_SAMPLE_POINTS_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ u32 dx_lo = 0;
+ u32 dx_hi = 0;
+
+ for (unsigned int i = 0; i < 8; ++i) {
+ dx_lo |= (cfg->dx[i] & sample_mask) << 4 * i;
+ dx_hi |= (cfg->dx[i + 8] & sample_mask) << 4 * i;
+ }
+
+ write(priv, mod->base + LIN_DX_LO_REG, dx_lo);
+ write(priv, mod->base + LIN_DX_HI_REG, dx_hi);
+
+ for (unsigned int i = 0; i < RPPX1_LIN_DEGAMMA_CURVE_NUM; i++) {
+ write(priv, mod->base + LIN_R_Y_REG(i), cfg->curve_r[i] & mask);
+ write(priv, mod->base + LIN_G_Y_REG(i), cfg->curve_g[i] & mask);
+ write(priv, mod->base + LIN_B_Y_REG(i), cfg->curve_b[i] & mask);
+ }
+
+ if ((cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+ write(priv, mod->base + LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_lin_ops = {
.probe = rppx1_lin_probe,
.start = rppx1_lin_start,
+ .fill_params = rppx1_lin_fill_params,
};
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 0246e9af1d1d..e743e11d11e9 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -95,6 +95,8 @@ enum rppx1_meas_chan {
* @RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST: Debayer filtering
* @RPPX1_PARAMS_BLOCK_TYPE_BD_PRE1: PRE1 pipe De-noise Pre-Filter
* @RPPX1_PARAMS_BLOCK_TYPE_BD_PRE2: PRE2 pipe De-noise Pre-Filter
+ * @RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1: PRE1 pipe Linearization (Sensor De-gamma)
+ * @RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2: PRE2 pipe Linearization (Sensor De-gamma)
*/
enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
@@ -117,6 +119,8 @@ enum rppx1_params_block_type {
RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST,
RPPX1_PARAMS_BLOCK_TYPE_BD_PRE1,
RPPX1_PARAMS_BLOCK_TYPE_BD_PRE2,
+ RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1,
+ RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2,
};
/**
@@ -735,6 +739,46 @@ struct rppx1_bd_params {
struct rppx1_bd_nll nll;
};
+/* Linearization (Sensor De-gamma) */
+#define RPPX1_LIN_SAMPLE_POINTS_NUM 16
+#define RPPX1_LIN_DEGAMMA_CURVE_NUM 17
+
+/**
+ * struct rppx1_lin_params - Linearization (Sensor De-gamma) configuration
+ *
+ * The RPP-X1 linearization module is available on the PRE1 and PRE2 pre-fusion
+ * pipes. Userspace selects which pipe to operate by setting the @header.type
+ * field to RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1 or
+ * RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2.
+ *
+ * The LIN module applies the per-color channel de-gamma linearization curves
+ * @curve_r, @curve_g and @curve_b defined on the input sampling points @dx.
+ *
+ * For the PRE1 pipe the de-gamma curves values are 24-bits, for the PRE2 pipe
+ * the de-gamma curve values are 12-bits.
+ *
+ * For the PRE1 pipe de-gamma module sampling points @dx values are in the range
+ * [0, 15] (4 bits). For the PRE2 pipe de-gamma module sampling points values
+ * are in the range [0, 7] (3 bits).
+ *
+ * Userspace is expected to provide the curve values and sampling points with a
+ * bit-depth matching the one of pipe in use.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1 or
+ * RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2)
+ * @curve_r: de-gamma linearization curve for red channel
+ * @curve_g: de-gamma linearization curve for green channel
+ * @curve_b: de-gamma linearization curve for blue channel
+ * @dx: input sampling points
+ */
+struct rppx1_lin_params {
+ struct v4l2_isp_params_block_header header;
+ __u32 curve_r[RPPX1_LIN_DEGAMMA_CURVE_NUM];
+ __u32 curve_g[RPPX1_LIN_DEGAMMA_CURVE_NUM];
+ __u32 curve_b[RPPX1_LIN_DEGAMMA_CURVE_NUM];
+ __u8 dx[RPPX1_LIN_SAMPLE_POINTS_NUM];
+};
+
/**
* RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
*
@@ -761,7 +805,9 @@ struct rppx1_bd_params {
sizeof(struct rppx1_db_demosaic_params) + \
sizeof(struct rppx1_db_filter_params) + \
sizeof(struct rppx1_bd_params) + \
- sizeof(struct rppx1_bd_params))
+ sizeof(struct rppx1_bd_params) + \
+ sizeof(struct rppx1_lin_params) + \
+ sizeof(struct rppx1_lin_params))
/* ---------------------------------------------------------------------------
* Statistics Structures
--
2.54.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [v8 14/14] media: rppx1: lin: Add support for gamma sensor linearization
2026-05-04 1:05 ` [v8 14/14] media: rppx1: lin: Add support for gamma sensor linearization Niklas Söderlund
@ 2026-05-06 15:57 ` Jacopo Mondi
0 siblings, 0 replies; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 15:57 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel, Jai Luthra
Hi Niklas,
On Mon, May 04, 2026 at 03:05:56AM +0200, Niklas Söderlund wrote:
> From: Jai Luthra <jai.luthra@ideasonboard.com>
>
> Extend the RPPX1 driver to allow setting the gamma sensor linearization
> configuration parameters. It uses the RPPX1 framework for parameters and
> its writer abstraction to allow the user to control how, and when,
> configuration is applied to the RPPX1.
>
> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> ---
> .../platform/dreamchip/rppx1/rpp_module.h | 1 +
> .../platform/dreamchip/rppx1/rpp_params.c | 5 ++
> .../platform/dreamchip/rppx1/rppx1_lin.c | 55 +++++++++++++++++++
> .../uapi/linux/media/dreamchip/rppx1-config.h | 48 +++++++++++++++-
> 4 files changed, 108 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> index 136ec4d48054..27235fdfb749 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
> @@ -86,6 +86,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
> union rppx1_params_block {
> struct v4l2_isp_params_block_header header;
> struct rppx1_bls_params bls;
> + struct rppx1_lin_params lin;
> struct rppx1_lsc_params lsc;
> struct rppx1_awbg_params awbg;
> struct rppx1_bd_params bd;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> index edea25293d64..1995a80890f4 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
> @@ -18,6 +18,8 @@ static const struct v4l2_isp_block_type_info
> rppx1_ext_params_blocks_info[] = {
> RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls),
> RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
> + RPPX1_PARAMS_BLOCK_INFO(LIN_PRE1, lin),
> + RPPX1_PARAMS_BLOCK_INFO(LIN_PRE2, lin),
> RPPX1_PARAMS_BLOCK_INFO(LSC_PRE1, lsc),
> RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
> RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
> @@ -72,6 +74,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
> case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
> module = &rpp->pre1.bls;
> break;
> + case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1:
> + module = &rpp->pre1.lin;
> + break;
> case RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1:
> module = &rpp->pre1.lsc;
> break;
> diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
> index f595f56a292e..cc8efe3b3f2c 100644
> --- a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
> +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
> @@ -24,6 +24,11 @@
> #define LIN_B_Y_REG_NUM 17
> #define LIN_B_Y_REG(n) (0x0098 + (4 * (n)))
>
> +#define LIN_PRE1_DEGAMMA_CURVE_MASK GENMASK(23, 0)
> +#define LIN_PRE1_SAMPLE_POINTS_MASK GENMASK(3, 0)
> +#define LIN_PRE2_DEGAMMA_CURVE_MASK GENMASK(11, 0)
> +#define LIN_PRE2_SAMPLE_POINTS_MASK GENMASK(2, 0)
> +
> static int rppx1_lin_probe(struct rpp_module *mod)
> {
> /* Version check. */
> @@ -52,7 +57,57 @@ static int rppx1_lin_start(struct rpp_module *mod,
> return 0;
> }
>
> +static int rppx1_lin_fill_params(struct rpp_module *mod,
> + const union rppx1_params_block *block,
> + rppx1_reg_write write, void *priv)
> +{
> + const struct rppx1_lin_params *cfg = &block->lin;
> + u8 sample_mask;
> + u32 mask;
> +
> + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
> + write(priv, mod->base + LIN_ENABLE_REG, 0);
> + return 0;
> + }
> +
> + switch (cfg->header.type) {
> + case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1:
> + mask = LIN_PRE1_DEGAMMA_CURVE_MASK;
> + sample_mask = LIN_PRE1_SAMPLE_POINTS_MASK;
> + break;
> + case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2:
> + mask = LIN_PRE2_DEGAMMA_CURVE_MASK;
> + sample_mask = LIN_PRE2_SAMPLE_POINTS_MASK;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + u32 dx_lo = 0;
> + u32 dx_hi = 0;
I was clearly over-excited by the modernity of C99 that allows mixing
declarations and code. However this is still a discouraged practice
unless there are good reasons to do so. Probably in this case there
aren't enough.
> +
> + for (unsigned int i = 0; i < 8; ++i) {
> + dx_lo |= (cfg->dx[i] & sample_mask) << 4 * i;
> + dx_hi |= (cfg->dx[i + 8] & sample_mask) << 4 * i;
> + }
> +
> + write(priv, mod->base + LIN_DX_LO_REG, dx_lo);
> + write(priv, mod->base + LIN_DX_HI_REG, dx_hi);
> +
> + for (unsigned int i = 0; i < RPPX1_LIN_DEGAMMA_CURVE_NUM; i++) {
> + write(priv, mod->base + LIN_R_Y_REG(i), cfg->curve_r[i] & mask);
> + write(priv, mod->base + LIN_G_Y_REG(i), cfg->curve_g[i] & mask);
> + write(priv, mod->base + LIN_B_Y_REG(i), cfg->curve_b[i] & mask);
> + }
> +
> + if ((cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
> + write(priv, mod->base + LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN);
> +
> + return 0;
> +}
> +
> const struct rpp_module_ops rppx1_lin_ops = {
> .probe = rppx1_lin_probe,
> .start = rppx1_lin_start,
> + .fill_params = rppx1_lin_fill_params,
> };
> diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
> index 0246e9af1d1d..e743e11d11e9 100644
> --- a/include/uapi/linux/media/dreamchip/rppx1-config.h
> +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
> @@ -95,6 +95,8 @@ enum rppx1_meas_chan {
> * @RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST: Debayer filtering
> * @RPPX1_PARAMS_BLOCK_TYPE_BD_PRE1: PRE1 pipe De-noise Pre-Filter
> * @RPPX1_PARAMS_BLOCK_TYPE_BD_PRE2: PRE2 pipe De-noise Pre-Filter
> + * @RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1: PRE1 pipe Linearization (Sensor De-gamma)
> + * @RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2: PRE2 pipe Linearization (Sensor De-gamma)
> */
> enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST,
> @@ -117,6 +119,8 @@ enum rppx1_params_block_type {
> RPPX1_PARAMS_BLOCK_TYPE_DB_FILTER_POST,
> RPPX1_PARAMS_BLOCK_TYPE_BD_PRE1,
> RPPX1_PARAMS_BLOCK_TYPE_BD_PRE2,
> + RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1,
> + RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2,
> };
>
> /**
> @@ -735,6 +739,46 @@ struct rppx1_bd_params {
> struct rppx1_bd_nll nll;
> };
>
> +/* Linearization (Sensor De-gamma) */
> +#define RPPX1_LIN_SAMPLE_POINTS_NUM 16
> +#define RPPX1_LIN_DEGAMMA_CURVE_NUM 17
> +
> +/**
> + * struct rppx1_lin_params - Linearization (Sensor De-gamma) configuration
> + *
> + * The RPP-X1 linearization module is available on the PRE1 and PRE2 pre-fusion
> + * pipes. Userspace selects which pipe to operate by setting the @header.type
> + * field to RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1 or
> + * RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2.
> + *
> + * The LIN module applies the per-color channel de-gamma linearization curves
> + * @curve_r, @curve_g and @curve_b defined on the input sampling points @dx.
> + *
> + * For the PRE1 pipe the de-gamma curves values are 24-bits, for the PRE2 pipe
> + * the de-gamma curve values are 12-bits.
> + *
> + * For the PRE1 pipe de-gamma module sampling points @dx values are in the range
> + * [0, 15] (4 bits). For the PRE2 pipe de-gamma module sampling points values
> + * are in the range [0, 7] (3 bits).
> + *
> + * Userspace is expected to provide the curve values and sampling points with a
> + * bit-depth matching the one of pipe in use.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1 or
> + * RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2)
> + * @curve_r: de-gamma linearization curve for red channel
> + * @curve_g: de-gamma linearization curve for green channel
> + * @curve_b: de-gamma linearization curve for blue channel
> + * @dx: input sampling points
> + */
> +struct rppx1_lin_params {
> + struct v4l2_isp_params_block_header header;
> + __u32 curve_r[RPPX1_LIN_DEGAMMA_CURVE_NUM];
> + __u32 curve_g[RPPX1_LIN_DEGAMMA_CURVE_NUM];
> + __u32 curve_b[RPPX1_LIN_DEGAMMA_CURVE_NUM];
> + __u8 dx[RPPX1_LIN_SAMPLE_POINTS_NUM];
> +};
> +
> /**
> * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> *
> @@ -761,7 +805,9 @@ struct rppx1_bd_params {
> sizeof(struct rppx1_db_demosaic_params) + \
> sizeof(struct rppx1_db_filter_params) + \
> sizeof(struct rppx1_bd_params) + \
> - sizeof(struct rppx1_bd_params))
> + sizeof(struct rppx1_bd_params) + \
> + sizeof(struct rppx1_lin_params) + \
> + sizeof(struct rppx1_lin_params))
>
> /* ---------------------------------------------------------------------------
> * Statistics Structures
> --
> 2.54.0
>
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP
2026-05-04 1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
` (13 preceding siblings ...)
2026-05-04 1:05 ` [v8 14/14] media: rppx1: lin: Add support for gamma sensor linearization Niklas Söderlund
@ 2026-05-06 12:19 ` Geert Uytterhoeven
2026-05-06 12:29 ` Niklas Söderlund
14 siblings, 1 reply; 28+ messages in thread
From: Geert Uytterhoeven @ 2026-05-06 12:19 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Niklas,
On Mon, 4 May 2026 at 03:08, Niklas Söderlund
<niklas.soderlund+renesas@ragnatech.se> wrote:
> This series adds support for two different devices that together enable
> ISP support on Renesas R-Car Gen4 ISP processing. The first driver added
> is for Dreamchip RPPX1 ISP, this device purely deals with image
> processing algorithms, statistics and image conversion; but have no DMA
> engines. The second driver is for the R-Car ISP CORE, this device
> deals with DMA to/from the RPPX1 ISP and provides a V4L2 user-space
> interface for the ISP.
Thanks for your series!
> This series depends on the ISP extensible statistics definitions
> out-of-tree patches.
So that is:
1. "[PATCH v1 00/11] media: Add iMX95 neoisp driver"
https://lore.kernel.org/20260413160331.2611829-1-antoine.bouyer@nxp.com/
for v4l2_isp_block_header,
2. Jacopo's reply https://lore.kernel.org/aeHd0G_JDaROMWDd@zed/
for v4l2_isp_buffer_size.
However, that still leaves me with undefined struct v4l2_isp_block_type_info,
and lore couldn't help in locating the patch series that adds it :-(
Where can I find it? Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP
2026-05-06 12:19 ` [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Geert Uytterhoeven
@ 2026-05-06 12:29 ` Niklas Söderlund
2026-05-06 12:49 ` Jacopo Mondi
0 siblings, 1 reply; 28+ messages in thread
From: Niklas Söderlund @ 2026-05-06 12:29 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Jai Luthra, Mauro Carvalho Chehab, Kuninori Morimoto,
Jacopo Mondi, Laurent Pinchart, linux-media, linux-renesas-soc,
linux-kernel
Hi Geert,
On 2026-05-06 14:19:28 +0200, Geert Uytterhoeven wrote:
> Hi Niklas,
>
> On Mon, 4 May 2026 at 03:08, Niklas Söderlund
> <niklas.soderlund+renesas@ragnatech.se> wrote:
> > This series adds support for two different devices that together enable
> > ISP support on Renesas R-Car Gen4 ISP processing. The first driver added
> > is for Dreamchip RPPX1 ISP, this device purely deals with image
> > processing algorithms, statistics and image conversion; but have no DMA
> > engines. The second driver is for the R-Car ISP CORE, this device
> > deals with DMA to/from the RPPX1 ISP and provides a V4L2 user-space
> > interface for the ISP.
>
> Thanks for your series!
>
> > This series depends on the ISP extensible statistics definitions
> > out-of-tree patches.
>
> So that is:
> 1. "[PATCH v1 00/11] media: Add iMX95 neoisp driver"
> https://lore.kernel.org/20260413160331.2611829-1-antoine.bouyer@nxp.com/
> for v4l2_isp_block_header,
> 2. Jacopo's reply https://lore.kernel.org/aeHd0G_JDaROMWDd@zed/
> for v4l2_isp_buffer_size.
>
> However, that still leaves me with undefined struct v4l2_isp_block_type_info,
> and lore couldn't help in locating the patch series that adds it :-(
As Jacopo have kindly pointed out to me in private, this work was based
on an early version of [2] that was not published, but as I found [1] I
thought it was.
I will rebase this on [2] and address Jacopo's comments and repost.
>
> Where can I find it? Thanks!
>
> Gr{oetje,eeting}s,
>
> Geert
>
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
--
Kind Regards,
Niklas Söderlund
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP
2026-05-06 12:29 ` Niklas Söderlund
@ 2026-05-06 12:49 ` Jacopo Mondi
2026-05-06 12:56 ` Geert Uytterhoeven
0 siblings, 1 reply; 28+ messages in thread
From: Jacopo Mondi @ 2026-05-06 12:49 UTC (permalink / raw)
To: Niklas Söderlund
Cc: Geert Uytterhoeven, Jai Luthra, Mauro Carvalho Chehab,
Kuninori Morimoto, Jacopo Mondi, Laurent Pinchart, linux-media,
linux-renesas-soc, linux-kernel
Hello,
On Wed, May 06, 2026 at 02:29:54PM +0200, Niklas Söderlund wrote:
> Hi Geert,
>
> On 2026-05-06 14:19:28 +0200, Geert Uytterhoeven wrote:
> > Hi Niklas,
> >
> > On Mon, 4 May 2026 at 03:08, Niklas Söderlund
> > <niklas.soderlund+renesas@ragnatech.se> wrote:
> > > This series adds support for two different devices that together enable
> > > ISP support on Renesas R-Car Gen4 ISP processing. The first driver added
> > > is for Dreamchip RPPX1 ISP, this device purely deals with image
> > > processing algorithms, statistics and image conversion; but have no DMA
> > > engines. The second driver is for the R-Car ISP CORE, this device
> > > deals with DMA to/from the RPPX1 ISP and provides a V4L2 user-space
> > > interface for the ISP.
> >
> > Thanks for your series!
> >
> > > This series depends on the ISP extensible statistics definitions
> > > out-of-tree patches.
> >
> > So that is:
> > 1. "[PATCH v1 00/11] media: Add iMX95 neoisp driver"
> > https://lore.kernel.org/20260413160331.2611829-1-antoine.bouyer@nxp.com/
> > for v4l2_isp_block_header,
> > 2. Jacopo's reply https://lore.kernel.org/aeHd0G_JDaROMWDd@zed/
> > for v4l2_isp_buffer_size.
> >
> > However, that still leaves me with undefined struct v4l2_isp_block_type_info,
> > and lore couldn't help in locating the patch series that adds it :-(
This version is based on a preliminary version of the extensible
statistics. I can provide you the patches in case you're interested.
>
> As Jacopo have kindly pointed out to me in private, this work was based
> on an early version of [2] that was not published, but as I found [1] I
> thought it was.
>
> I will rebase this on [2] and address Jacopo's comments and repost.
Don't :)
Please re-base on:
https://patchwork.linuxtv.org/project/linux-media/list/?series=24772
which I sent yesterday instead.
If you're using b4 you can simply point the pre-requisites to this
change-id: 20260504-extensible-stats-f2d6befcc1ce
Thanks
j
>
> >
> > Where can I find it? Thanks!
> >
> > Gr{oetje,eeting}s,
> >
> > Geert
> >
> >
> > --
> > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> >
> > In personal conversations with technical people, I call myself a hacker. But
> > when I'm talking to journalists I just say "programmer" or something like that.
> > -- Linus Torvalds
>
> --
> Kind Regards,
> Niklas Söderlund
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP
2026-05-06 12:49 ` Jacopo Mondi
@ 2026-05-06 12:56 ` Geert Uytterhoeven
0 siblings, 0 replies; 28+ messages in thread
From: Geert Uytterhoeven @ 2026-05-06 12:56 UTC (permalink / raw)
To: Jacopo Mondi
Cc: Niklas Söderlund, Jai Luthra, Mauro Carvalho Chehab,
Kuninori Morimoto, Laurent Pinchart, linux-media,
linux-renesas-soc, linux-kernel
Hi Jacopo,
On Wed, 6 May 2026 at 14:49, Jacopo Mondi <jacopo.mondi@ideasonboard.com> wrote:
> On Wed, May 06, 2026 at 02:29:54PM +0200, Niklas Söderlund wrote:
> > On 2026-05-06 14:19:28 +0200, Geert Uytterhoeven wrote:
> > > On Mon, 4 May 2026 at 03:08, Niklas Söderlund
> > > <niklas.soderlund+renesas@ragnatech.se> wrote:
> > > > This series adds support for two different devices that together enable
> > > > ISP support on Renesas R-Car Gen4 ISP processing. The first driver added
> > > > is for Dreamchip RPPX1 ISP, this device purely deals with image
> > > > processing algorithms, statistics and image conversion; but have no DMA
> > > > engines. The second driver is for the R-Car ISP CORE, this device
> > > > deals with DMA to/from the RPPX1 ISP and provides a V4L2 user-space
> > > > interface for the ISP.
> > >
> > > Thanks for your series!
> > >
> > > > This series depends on the ISP extensible statistics definitions
> > > > out-of-tree patches.
> > >
> > > So that is:
> > > 1. "[PATCH v1 00/11] media: Add iMX95 neoisp driver"
> > > https://lore.kernel.org/20260413160331.2611829-1-antoine.bouyer@nxp.com/
> > > for v4l2_isp_block_header,
> > > 2. Jacopo's reply https://lore.kernel.org/aeHd0G_JDaROMWDd@zed/
> > > for v4l2_isp_buffer_size.
> > >
> > > However, that still leaves me with undefined struct v4l2_isp_block_type_info,
> > > and lore couldn't help in locating the patch series that adds it :-(
>
> This version is based on a preliminary version of the extensible
> statistics. I can provide you the patches in case you're interested.
>
> >
> > As Jacopo have kindly pointed out to me in private, this work was based
> > on an early version of [2] that was not published, but as I found [1] I
> > thought it was.
> >
> > I will rebase this on [2] and address Jacopo's comments and repost.
>
> Don't :)
>
> Please re-base on:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=24772
> which I sent yesterday instead.
>
> If you're using b4 you can simply point the pre-requisites to this
>
> change-id: 20260504-extensible-stats-f2d6befcc1ce
b4 am takes none of the above, but does take
https://lore.kernel.org/20260505-extensible-stats-v1-0-e16f326b8dad@ideasonboard.com/
:-)
OK, I'll disable the driver again in my .config, and will wait for
Niklas' rebase...
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 28+ messages in thread