public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP
@ 2026-04-10  9:05 Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 01/18] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Jai Luthra
                   ` (17 more replies)
  0 siblings, 18 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

Hello,

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.

The R-Car ISP driver uses the RPPX1 framework to drive the ISP and
together the two devices provide a functional ISP. For detailed
description of the RPPX1 see patch 3/18, and for details about the
R-Car ISP see commit message in patch 4/18.

All functional blocks present on the RPPX1 are not yet added to the
driver, but most are.

The RPPX1 ISP is similar in design to the RkISP1 already supported
upstream. The primary difference is around pipeline logic, and higher
precision configuration and statistic values supported by various
blocks. Along with that, there are some new features or minor
differences here and there.

Thus, this revision defines a completely new uAPI and buffer formats
from RkISP1 for RPPX1. See patches 1/18 and 2/18 for more details.

Patch 1/18 adds metadata formats for RPPX1 params and stats

Patch 2/18 adds the uAPI for configuring RPPX1 using V4L2 extensible
parameters

Patch 3/18 adds the foundation for the RPPX1 framework. It deals with
probing all function blocks making sure every blocks version register is
supported and setup a "passthru" pipeline that just debayer RAW images.

Patch 4/18 adds the R-Car ISP CORE DMA parts and integrates with the
RPPX1 framework.

Patches 5/18 to 18/18 extend the RPPX1 framework with the logic to
drive the different ISP modules.

For testing this series with the new uAPI, you can use this WIP
libcamera branch, which has mostly copied over the RkISP IPA and thus
still tuned with lower precision values but shifted to match the new
uAPI:
https://git.ideasonboard.com/renesas-v4h-isp/libcamera/commits/branch/jluthra/rppx1

Future revisions will add support for extensible statistics once the
design is finalized, currently WIP here:
https://lore.kernel.org/all/20260123080938.3367348-2-antoine.bouyer@nxp.com/

Changes in v7:
- Add new uAPI, parameter and statistics formats for RPPX1, no longer
  reusing RkISP1
- For each module:
    - Switch to RPPX1 uAPI
    - Support native precision for params and stats
    - Where module can support different precision for params depending
      upon the instantiation in hardware, use the biggest value for uAPI
      and scale it down in driver after reading the relevant hardware
      register
- EXM: Add [SQUASH] patch that supports changing the measurement point
  for AE stats, allows configuring the coefficients, and RGB input mode
- HIST: Add [SQUASH] patch that supports changing the measurement point
  for Histogram statistics
- LSC: Add [SQUASH] patch for describing full lens grid instead of only
  one quadrant
- Add support for Sensor (Gamma) Linearization module
- Link to v6: https://lore.kernel.org/all/20260314215944.3674865-1-niklas.soderlund+renesas@ragnatech.se/

Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
Jai Luthra (6):
      media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats
      media: uapi: Add extensible param and stats blocks for RPPX1
      [SQUASH] media: rppx1: exm: Expose coefficients, RGB mode and channel selection
      [SQUASH] media: rppx1: hist: Expose channel selection
      [SQUASH] media: rppx1: lsc: Make full lens grid programmable
      media: rppx1: Add support for Sensor (Gamma) Linearization

Niklas Söderlund (12):
      media: rppx1: Add framework to support Dreamchip RPPX1 ISP
      media: rcar-isp: Add support for ISPCORE
      media: rppx1: Add support for AWB measurement parameters and statistics
      media: rppx1: Add support for AWB gain settings
      media: rppx1: Add support for Auto Exposure Measurement
      media: rppx1: Add support for Histogram Measurement
      media: rppx1: Add support for Black Level Subtraction
      media: rppx1: Add support for Color Correction Matrix
      media: rppx1: Add support for Lens Shade Correction
      media: rppx1: Add support for Gamma Correction
      media: rppx1: Add support for Bayer Demosaicing
      media: rppx1: Add support for Bilateral Denoising

 MAINTAINERS                                        |    7 +
 drivers/media/platform/Kconfig                     |    1 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/dreamchip/Kconfig           |    3 +
 drivers/media/platform/dreamchip/Makefile          |    6 +
 drivers/media/platform/dreamchip/rppx1/Kconfig     |   12 +
 drivers/media/platform/dreamchip/rppx1/Makefile    |   33 +
 .../media/platform/dreamchip/rppx1/rpp_module.c    |   40 +
 .../media/platform/dreamchip/rppx1/rpp_module.h    |  155 +++
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  122 +++
 drivers/media/platform/dreamchip/rppx1/rpp_stats.c |   30 +
 drivers/media/platform/dreamchip/rppx1/rppx1.c     |  338 +++++++
 drivers/media/platform/dreamchip/rppx1/rppx1.h     |   99 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_acq.c |  147 +++
 .../media/platform/dreamchip/rppx1/rppx1_awbg.c    |   62 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_bd.c  |  202 ++++
 .../media/platform/dreamchip/rppx1/rppx1_bdrgb.c   |   80 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_bls.c |  169 ++++
 drivers/media/platform/dreamchip/rppx1/rppx1_cac.c |   29 +
 .../media/platform/dreamchip/rppx1/rppx1_ccor.c    |  173 ++++
 drivers/media/platform/dreamchip/rppx1/rppx1_db.c  |  126 +++
 .../media/platform/dreamchip/rppx1/rppx1_dpcc.c    |   76 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_exm.c |  126 +++
 drivers/media/platform/dreamchip/rppx1/rppx1_ga.c  |   83 ++
 .../media/platform/dreamchip/rppx1/rppx1_hist.c    |  249 +++++
 .../media/platform/dreamchip/rppx1/rppx1_hist256.c |   46 +
 drivers/media/platform/dreamchip/rppx1/rppx1_is.c  |   42 +
 drivers/media/platform/dreamchip/rppx1/rppx1_lin.c |   91 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c |  187 ++++
 drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c |   48 +
 .../media/platform/dreamchip/rppx1/rppx1_ltmmeas.c |   41 +
 .../media/platform/dreamchip/rppx1/rppx1_outif.c   |   45 +
 .../media/platform/dreamchip/rppx1/rppx1_outregs.c |   75 ++
 .../media/platform/dreamchip/rppx1/rppx1_rmap.c    |   64 ++
 .../platform/dreamchip/rppx1/rppx1_rmapmeas.c      |   47 +
 .../media/platform/dreamchip/rppx1/rppx1_shrp.c    |   64 ++
 .../media/platform/dreamchip/rppx1/rppx1_wbmeas.c  |  179 ++++
 .../media/platform/dreamchip/rppx1/rppx1_xyz2luv.c |   26 +
 drivers/media/platform/renesas/rcar-isp/Kconfig    |    2 +
 drivers/media/platform/renesas/rcar-isp/Makefile   |    2 +-
 drivers/media/platform/renesas/rcar-isp/core-io.c  | 1017 ++++++++++++++++++++
 drivers/media/platform/renesas/rcar-isp/core.c     |  826 ++++++++++++++++
 drivers/media/platform/renesas/rcar-isp/csisp.c    |   46 +-
 .../media/platform/renesas/rcar-isp/risp-core.h    |  170 ++++
 drivers/media/v4l2-core/v4l2-ioctl.c               |    2 +
 include/media/rppx1.h                              |   33 +
 include/uapi/linux/media/dreamchip/rppx1-config.h  |  779 +++++++++++++++
 include/uapi/linux/videodev2.h                     |    4 +
 48 files changed, 6198 insertions(+), 7 deletions(-)
---
base-commit: 591cd656a1bf5ea94a222af5ef2ee76df029c1d2
change-id: 20260410-rppx1-912a0fc12599

Best regards,
-- 
Jai Luthra <jai.luthra+renesas@ideasonboard.com>


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

* [PATCH v7 01/18] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10 11:15   ` Jacopo Mondi
  2026-04-10  9:05 ` [PATCH v7 02/18] media: uapi: Add extensible param and stats blocks for RPPX1 Jai Luthra
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

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>
---
 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 a2b650f4ec3c32a4883521f34fb51eed13c71d76..cd3f4a86e27f22a0108ad2932cba755295af9a98 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 eda4492e40dc14a90a230601d8e23b0e13845d34..1f78b5378b3bde31a5ec464a6a609fac94e6d0d6 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.53.0


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

* [PATCH v7 02/18] media: uapi: Add extensible param and stats blocks for RPPX1
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 01/18] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10 16:10   ` Jacopo Mondi
  2026-04-10  9:05 ` [PATCH v7 03/18] media: rppx1: Add framework to support Dreamchip RPPX1 ISP Jai Luthra
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

Define the userspace API for the Dreamchip RPP-X1 ISP extensible
parameters and statistics. The RPP-X1 is functionally similar to the
RkISP1 already supported upstream, but operates at higher bit depths (up
to 24-bit precision in many blocks) and exposes additional configuration
options. This warrants a dedicated uAPI rather than reusing the RkISP1
definitions.

The parameter blocks follow the V4L2 extensible parameters framework
using struct v4l2_isp_params_block_header, with each ISP functional
block represented as a tagged configuration structure. The statistics
buffer provides AWB, auto-exposure and histogram measurement results at
native RPP-X1 precision.

Not all functional blocks present on the RPP-X1 hardware are included
yet, but the format is extensible and new blocks can be added without
breaking existing userspace.

Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 include/uapi/linux/media/dreamchip/rppx1-config.h | 728 ++++++++++++++++++++++
 1 file changed, 728 insertions(+)

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 0000000000000000000000000000000000000000..b9083e6f32b15329333eb13491b50c0aea8d1a32
--- /dev/null
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -0,0 +1,728 @@
+/* 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>
+
+/*
+ * Defect Pixel Cluster Correction
+ */
+#define RPPX1_DPCC_METHODS_MAX				3
+
+/* Linearization (Sensor De-gamma) */
+#define RPPX1_LIN_SAMPLES_NUM				17
+
+/* Gamma Out */
+#define RPPX1_GAMMA_OUT_MAX_SAMPLES			17
+
+/* Lens Shade Correction */
+#define RPPX1_LSC_SECTORS_TBL_SIZE			8
+#define RPPX1_LSC_SAMPLES_MAX				17
+
+/* Histogram */
+#define RPPX1_HIST_BIN_N_MAX				32
+
+/* Exposure Measurement */
+#define RPPX1_EXM_MEAN_MAX				25
+
+/* AWB Measurement */
+#define RPPX1_AWB_MAX_GRID				1
+
+/* Color Correction Matrix */
+#define RPPX1_CTK_COEFF_MAX				0x8000
+#define RPPX1_CTK_OFFSET_MAX				0x800000
+
+/* Filter */
+#define RPPX1_BDM_MAX_TH				0xffff
+
+/**
+ * enum rppx1_params_block_type - RPP-X1 extensible params block types
+ *
+ * @RPPX1_PARAMS_BLOCK_TYPE_BLS: Black Level Subtraction
+ * @RPPX1_PARAMS_BLOCK_TYPE_DPCC: Defect Pixel Cluster Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_LIN: Linearization (Sensor De-gamma)
+ * @RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN: Auto White Balance Gains
+ * @RPPX1_PARAMS_BLOCK_TYPE_FLT: ISP Filtering
+ * @RPPX1_PARAMS_BLOCK_TYPE_BDM: Bayer Demosaic
+ * @RPPX1_PARAMS_BLOCK_TYPE_CTK: Color Correction (Cross-Talk)
+ * @RPPX1_PARAMS_BLOCK_TYPE_GOC: Gamma Out Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_DPF: De-noise Pre-Filter
+ * @RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH: De-noise Pre-Filter Strength
+ * @RPPX1_PARAMS_BLOCK_TYPE_LSC: Lens Shading Correction
+ * @RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS: AWB Measurement Configuration
+ * @RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS: Histogram Measurement Configuration
+ * @RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS: Auto Exposure Measurement Configuration
+ */
+enum rppx1_params_block_type {
+	RPPX1_PARAMS_BLOCK_TYPE_BLS,
+	RPPX1_PARAMS_BLOCK_TYPE_DPCC,
+	RPPX1_PARAMS_BLOCK_TYPE_LIN,
+	RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN,
+	RPPX1_PARAMS_BLOCK_TYPE_FLT,
+	RPPX1_PARAMS_BLOCK_TYPE_BDM,
+	RPPX1_PARAMS_BLOCK_TYPE_CTK,
+	RPPX1_PARAMS_BLOCK_TYPE_GOC,
+	RPPX1_PARAMS_BLOCK_TYPE_DPF,
+	RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH,
+	RPPX1_PARAMS_BLOCK_TYPE_LSC,
+	RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS,
+	RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS,
+	RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS,
+};
+
+/**
+ * struct rppx1_window - Measurement window
+ *
+ * @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;
+};
+
+/**
+ * struct rppx1_bls_fixed_val - BLS fixed subtraction values
+ *
+ * Fixed black level values subtracted from sensor data per Bayer channel.
+ * Negative values result in addition. Each value is a 24-bit + sign
+ * (25-bit signed) fixed-point number stored in a __s32.
+ *
+ * RPP-X1 supports 12/20/24-bit + sign depending on hardware version.
+ * Userspace should provide values at full 24-bit precision; the driver
+ * truncates to match the hardware.
+ *
+ * @r: subtraction value for Bayer pattern R
+ * @gr: subtraction value for Bayer pattern Gr
+ * @gb: subtraction value for Bayer pattern Gb
+ * @b: subtraction value for Bayer pattern B
+ */
+struct rppx1_bls_fixed_val {
+	__s32 r;
+	__s32 gr;
+	__s32 gb;
+	__s32 b;
+};
+
+/**
+ * struct rppx1_params_bls_config - Black Level Subtraction configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BLS)
+ * @enable_auto: 1 = use measured values, 0 = use fixed_val
+ * @en_windows: enabled measurement windows bitmask
+ * @bls_window1: measurement window 1
+ * @bls_window2: measurement window 2
+ * @bls_samples: log2 of the number of measured pixels per Bayer position
+ * @fixed_val: fixed subtraction values (24-bit + sign)
+ */
+struct rppx1_params_bls_config {
+	struct v4l2_isp_params_block_header header;
+	__u8 enable_auto;
+	__u8 en_windows;
+	struct rppx1_window bls_window1;
+	struct rppx1_window bls_window2;
+	__u8 bls_samples;
+	struct rppx1_bls_fixed_val fixed_val;
+};
+
+/**
+ * struct rppx1_dpcc_methods_config - DPCC methods set configuration
+ *
+ * This structure stores the configuration of one set of methods for the DPCC
+ * algorithm. Multiple methods can be selected in each set (independently for
+ * the Green and Red/Blue components) through the @method field, the result is
+ * the logical AND of all enabled methods. The remaining fields set thresholds
+ * and factors for each method.
+ *
+ * @method: method enable bits (RPPX1_DPCC_METHODS_SET_*)
+ * @line_thresh: line threshold (RPPX1_DPCC_LINE_THRESH_*)
+ * @line_mad_fac: line MAD factor (RPPX1_DPCC_LINE_MAD_FAC_*)
+ * @pg_fac: peak gradient factor (RPPX1_DPCC_PG_FAC_*)
+ * @rnd_thresh: rank neighbor difference threshold (RPPX1_DPCC_RND_THRESH_*)
+ * @rg_fac: rank gradient factor (RPPX1_DPCC_RG_FAC_*)
+ */
+struct rppx1_dpcc_methods_config {
+	__u32 method;
+	__u32 line_thresh;
+	__u32 line_mad_fac;
+	__u32 pg_fac;
+	__u32 rnd_thresh;
+	__u32 rg_fac;
+};
+
+/**
+ * struct rppx1_params_dpcc_config - Defect Pixel Cluster Correction configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DPCC)
+ * @mode: DPCC mode (RPPX1_DPCC_MODE_*)
+ * @output_mode: interpolation output mode (RPPX1_DPCC_OUTPUT_MODE_*)
+ * @set_use: methods sets selection (RPPX1_DPCC_SET_USE_*)
+ * @methods: methods sets configuration
+ * @ro_limits: rank order limits (RPPX1_DPCC_RO_LIMITS_*)
+ * @rnd_offs: differential rank offsets (RPPX1_DPCC_RND_OFFS_*)
+ */
+struct rppx1_params_dpcc_config {
+	struct v4l2_isp_params_block_header header;
+	__u32 mode;
+	__u32 output_mode;
+	__u32 set_use;
+	struct rppx1_dpcc_methods_config methods[RPPX1_DPCC_METHODS_MAX];
+	__u32 ro_limits;
+	__u32 rnd_offs;
+};
+
+/**
+ * struct rppx1_lin_curve - Linearization curve for one color channel
+ *
+ * The RPP-X1 linearization module supports 12/20/24-bit precision depending
+ * on hardware version. Values are provided at 24-bit precision; the driver
+ * truncates to the hardware capability.
+ *
+ * @gamma_y: curve y-axis values, each up to 24 bits
+ */
+struct rppx1_lin_curve {
+	__u32 gamma_y[RPPX1_LIN_SAMPLES_NUM];
+};
+
+/**
+ * struct rppx1_lin_curve_dx - Linearization curve x-axis (sampling points)
+ * increments.
+ *
+ * gamma_dx[0] is for the lower samples, so Bits 0:3 for sample 1, ... Bits
+ * 28:31 for sample 8
+ * gamma_dx[1] is for the higher samples, so Bits 0:3 for sample 9, ... Bits
+ * 28:31 for sample 16
+ *
+ * The reset values for both fields is 0xcccccccc. This means that each sample
+ * is 12 units away from the previous one on the x-axis.
+ *
+ * @gamma_dx: curve x-axis increments in 4-bit precision
+ */
+struct rppx1_lin_curve_dx {
+	__u32 gamma_dx[2];
+};
+
+/**
+ * struct rppx1_params_lin_config - Linearization (Sensor De-gamma) configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LIN)
+ * @curve_r: linearization curve for red channel
+ * @curve_g: linearization curve for green channel
+ * @curve_b: linearization curve for blue channel
+ * @xa_pnts: x axis increment definitions
+ */
+struct rppx1_params_lin_config {
+	struct v4l2_isp_params_block_header header;
+	struct rppx1_lin_curve curve_r;
+	struct rppx1_lin_curve curve_g;
+	struct rppx1_lin_curve curve_b;
+	struct rppx1_lin_curve_dx xa_pnts;
+};
+
+/**
+ * struct rppx1_params_lsc_config - Lens Shading Correction configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LSC)
+ * @r_data_tbl: sample table red
+ * @gr_data_tbl: sample table green (red)
+ * @gb_data_tbl: sample table green (blue)
+ * @b_data_tbl: sample table blue
+ * @x_grad_tbl: gradient table x
+ * @y_grad_tbl: gradient table y
+ * @x_size_tbl: size table x
+ * @y_size_tbl: size table y
+ * @config_width: reserved
+ * @config_height: reserved
+ */
+struct rppx1_params_lsc_config {
+	struct v4l2_isp_params_block_header header;
+	__u16 r_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+	__u16 gr_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+	__u16 gb_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+	__u16 b_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
+	__u16 x_grad_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
+	__u16 y_grad_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
+	__u16 x_size_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
+	__u16 y_size_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
+	__u16 config_width;
+	__u16 config_height;
+};
+
+/**
+ * struct rppx1_params_awb_gain_config  - AWB gain configuration
+ *
+ * RPP-X1 AWB gains are 18-bit with 12-bit fractional part (0x1000 = 1.0),
+ * giving a range of 0.0 to 64.0.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN)
+ * @gain_red: gain for red component, 18-bit (Q6.12)
+ * @gain_green_r: gain for green-in-red component, 18-bit (Q6.12)
+ * @gain_blue: gain for blue component, 18-bit (Q6.12)
+ * @gain_green_b: gain for green-in-blue component, 18-bit (Q6.12)
+ */
+struct rppx1_params_awb_gain_config {
+	struct v4l2_isp_params_block_header header;
+	__u32 gain_red;
+	__u32 gain_green_r;
+	__u32 gain_blue;
+	__u32 gain_green_b;
+};
+
+/**
+ * struct rppx1_params_flt_config - Filter (demosaic/denoise) configuration
+ *
+ * RPP-X1 thresholds are 18-bit and factors are 8-bit.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_FLT)
+ * @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_params_flt_config {
+	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;
+};
+
+/**
+ * struct rppx1_params_bdm_config - Bayer Demosaic configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BDM)
+ * @demosaic_th: threshold for texture detection, 16-bit
+ */
+struct rppx1_params_bdm_config {
+	struct v4l2_isp_params_block_header header;
+	__u16 demosaic_th;
+};
+
+/**
+ * struct rppx1_params_ctk_config - Color Correction (Cross-Talk) configuration
+ *
+ * RPP-X1 coefficients are 16-bit signed fixed-point (Q4.12).
+ * Range: -8.0 (0x8000) to +7.9996 (0x7FFF), 1.0 = 0x1000.
+ *
+ * RPP-X1 offsets are up to 24-bit + sign depending on hardware version.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_CTK)
+ * @coeff: 3x3 color correction matrix, Q4.12 signed
+ * @ct_offset: R, G, B offsets, up to 25-bit signed
+ */
+struct rppx1_params_ctk_config {
+	struct v4l2_isp_params_block_header header;
+	__u16 coeff[3][3];
+	__u32 ct_offset[3];
+};
+
+/**
+ * struct rppx1_params_goc_config - Gamma Out Correction configuration
+ *
+ * RPP-X1 gamma output values are up to 24-bit depending on hardware version.
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_GOC)
+ * @mode: gamma curve mode (0 = logarithmic, 1 = equidistant)
+ * @gamma_y: gamma out curve y-axis values, up to 24-bit
+ */
+struct rppx1_params_goc_config {
+	struct v4l2_isp_params_block_header header;
+	__u32 mode;
+	__u32 gamma_y[RPPX1_GAMMA_OUT_MAX_SAMPLES];
+};
+
+/**
+ * enum rppx1_dpf_gain_usage - DPF noise function gain usage mode
+ * @RPPX1_DPF_GAIN_USAGE_DISABLED: gain not used
+ * @RPPX1_DPF_GAIN_USAGE_NF_GAINS: use noise function gains
+ * @RPPX1_DPF_GAIN_USAGE_LSC_GAINS: use LSC gains
+ * @RPPX1_DPF_GAIN_USAGE_NF_LSC_GAINS: use noise function and LSC gains
+ * @RPPX1_DPF_GAIN_USAGE_AWB_GAINS: use AWB gains
+ * @RPPX1_DPF_GAIN_USAGE_AWB_LSC_GAINS: use AWB and LSC gains
+ */
+enum rppx1_dpf_gain_usage {
+	RPPX1_DPF_GAIN_USAGE_DISABLED,
+	RPPX1_DPF_GAIN_USAGE_NF_GAINS,
+	RPPX1_DPF_GAIN_USAGE_LSC_GAINS,
+	RPPX1_DPF_GAIN_USAGE_NF_LSC_GAINS,
+	RPPX1_DPF_GAIN_USAGE_AWB_GAINS,
+	RPPX1_DPF_GAIN_USAGE_AWB_LSC_GAINS,
+};
+
+/**
+ * enum rppx1_nll_scale_mode - DPF noise level lookup scale mode
+ * @RPPX1_NLL_SCALE_LINEAR: linear scaling
+ * @RPPX1_NLL_SCALE_LOGARITHMIC: logarithmic scaling
+ */
+enum rppx1_nll_scale_mode {
+	RPPX1_NLL_SCALE_LINEAR,
+	RPPX1_NLL_SCALE_LOGARITHMIC,
+};
+
+/**
+ * enum rppx1_dpf_rb_filtersize - DPF red/blue filter kernel size
+ * @RPPX1_DPF_RB_FILTERSIZE_13x9: 13x9 filter size
+ * @RPPX1_DPF_RB_FILTERSIZE_9x9: 9x9 filter size
+ */
+enum rppx1_dpf_rb_filtersize {
+	RPPX1_DPF_RB_FILTERSIZE_13x9,
+	RPPX1_DPF_RB_FILTERSIZE_9x9,
+};
+
+/**
+ * struct rppx1_dpf_gain - DPF noise function gain configuration
+ *
+ * @mode: gain usage 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_dpf_gain {
+	__u32 mode;
+	__u16 nf_r_gain;
+	__u16 nf_b_gain;
+	__u16 nf_gr_gain;
+	__u16 nf_gb_gain;
+};
+
+#define RPPX1_DPF_MAX_NLF_COEFFS			17
+#define RPPX1_DPF_MAX_SPATIAL_COEFFS			6
+
+/**
+ * struct rppx1_dpf_nll - DPF noise level lookup
+ *
+ * @coeff: noise level lookup coefficients
+ * @scale_mode: 0 = linear, 1 = logarithmic
+ */
+struct rppx1_dpf_nll {
+	__u16 coeff[RPPX1_DPF_MAX_NLF_COEFFS];
+	__u32 scale_mode;
+};
+
+/**
+ * struct rppx1_dpf_rb_flt - DPF red/blue filter configuration
+ *
+ * @fltsize: filter kernel size (0 = 13x9, 1 = 9x9)
+ * @spatial_coeff: spatial weight coefficients
+ * @r_enable: enable filter for red pixels
+ * @b_enable: enable filter for blue pixels
+ */
+struct rppx1_dpf_rb_flt {
+	__u32 fltsize;
+	__u8 spatial_coeff[RPPX1_DPF_MAX_SPATIAL_COEFFS];
+	__u8 r_enable;
+	__u8 b_enable;
+};
+
+/**
+ * struct rppx1_dpf_g_flt - DPF 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_dpf_g_flt {
+	__u8 spatial_coeff[RPPX1_DPF_MAX_SPATIAL_COEFFS];
+	__u8 gr_enable;
+	__u8 gb_enable;
+};
+
+/**
+ * struct rppx1_params_dpf_config - De-noising Pre-Filter configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DPF)
+ * @gain: noise function gain
+ * @g_flt: green filter configuration
+ * @rb_flt: red/blue filter configuration
+ * @nll: noise level lookup
+ */
+struct rppx1_params_dpf_config {
+	struct v4l2_isp_params_block_header header;
+	struct rppx1_dpf_gain gain;
+	struct rppx1_dpf_g_flt g_flt;
+	struct rppx1_dpf_rb_flt rb_flt;
+	struct rppx1_dpf_nll nll;
+};
+
+/**
+ * struct rppx1_params_dpf_strength_config - DPF strength configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH)
+ * @r: filter strength for RED
+ * @g: filter strength for GREEN
+ * @b: filter strength for BLUE
+ */
+struct rppx1_params_dpf_strength_config {
+	struct v4l2_isp_params_block_header header;
+	__u8 r;
+	__u8 g;
+	__u8 b;
+};
+
+/**
+ * enum rppx1_awb_mode_type - AWB measurement mode
+ * @RPPX1_AWB_MODE_MANUAL: manual white balance
+ * @RPPX1_AWB_MODE_RGB: RGB measurement mode
+ * @RPPX1_AWB_MODE_YCBCR: YCbCr measurement mode
+ */
+enum rppx1_awb_mode_type {
+	RPPX1_AWB_MODE_MANUAL,
+	RPPX1_AWB_MODE_RGB,
+	RPPX1_AWB_MODE_YCBCR,
+};
+
+/**
+ * struct rppx1_params_awb_meas_config - AWB measurement configuration
+ *
+ * RPP-X1 min_y, max_y, min_c, max_csum, awb_ref_cr, awb_ref_cb are up to
+ * 24-bit depending on hardware version (8/20/24-bit).
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS)
+ * @awb_wnd: measurement window
+ * @awb_mode: measurement mode (from enum rppx1_awb_mode_type)
+ * @max_y: upper pixel value limit, up to 24-bit
+ * @min_y: lower pixel value limit, up to 24-bit
+ * @max_csum: chrominance sum maximum, up to 24-bit
+ * @min_c: chrominance minimum, up to 24-bit
+ * @frames: number of frames for mean value calculation (0 = 1 frame)
+ * @awb_ref_cr: reference Cr for AWB regulation, up to 24-bit
+ * @awb_ref_cb: reference Cb for AWB regulation, up to 24-bit
+ * @enable_ymax_cmp: enable Y_MAX compare
+ */
+struct rppx1_params_awb_meas_config {
+	struct v4l2_isp_params_block_header header;
+	struct rppx1_window awb_wnd;
+	__u32 awb_mode;
+	__u32 max_y;
+	__u32 min_y;
+	__u32 max_csum;
+	__u32 min_c;
+	__u8 frames;
+	__u32 awb_ref_cr;
+	__u32 awb_ref_cb;
+	__u8 enable_ymax_cmp;
+};
+
+/**
+ * enum rppx1_histogram_mode - Histogram measurement mode
+ * @RPPX1_HISTOGRAM_MODE_DISABLE: histogram disabled
+ * @RPPX1_HISTOGRAM_MODE_RGB_COMBINED: combined RGB histogram
+ * @RPPX1_HISTOGRAM_MODE_R_HISTOGRAM: red channel histogram
+ * @RPPX1_HISTOGRAM_MODE_G_HISTOGRAM: green channel histogram
+ * @RPPX1_HISTOGRAM_MODE_B_HISTOGRAM: blue channel histogram
+ * @RPPX1_HISTOGRAM_MODE_Y_HISTOGRAM: luminance histogram
+ */
+enum rppx1_histogram_mode {
+	RPPX1_HISTOGRAM_MODE_DISABLE,
+	RPPX1_HISTOGRAM_MODE_RGB_COMBINED,
+	RPPX1_HISTOGRAM_MODE_R_HISTOGRAM,
+	RPPX1_HISTOGRAM_MODE_G_HISTOGRAM,
+	RPPX1_HISTOGRAM_MODE_B_HISTOGRAM,
+	RPPX1_HISTOGRAM_MODE_Y_HISTOGRAM,
+};
+
+#define RPPX1_HISTOGRAM_WEIGHT_GRIDS_SIZE		25
+
+/**
+ * struct rppx1_params_hst_config - Histogram measurement configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS)
+ * @mode: histogram mode (from enum rppx1_histogram_mode)
+ * @histogram_predivider: process every Nth pixel
+ * @meas_window: measurement window coordinates
+ * @hist_weight: weighting factors for sub-windows (5x5 grid)
+ */
+struct rppx1_params_hst_config {
+	struct v4l2_isp_params_block_header header;
+	__u32 mode;
+	__u8 histogram_predivider;
+	struct rppx1_window meas_window;
+	__u8 hist_weight[RPPX1_HISTOGRAM_WEIGHT_GRIDS_SIZE];
+};
+
+/**
+ * enum rppx1_exp_meas_mode - Exposure measurement mode
+ * @RPPX1_EXP_MEASURING_MODE_0: Y = 16 + 0.25R + 0.5G + 0.1094B
+ * @RPPX1_EXP_MEASURING_MODE_1: Y = (R + G + B) x (85/256)
+ */
+enum rppx1_exp_meas_mode {
+	RPPX1_EXP_MEASURING_MODE_0,
+	RPPX1_EXP_MEASURING_MODE_1,
+};
+
+/**
+ * struct rppx1_params_aec_config - Auto Exposure measurement configuration
+ *
+ * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS)
+ * @mode: exposure measure mode (from enum rppx1_exp_meas_mode)
+ * @autostop: 0 = continuous, 1 = stop after one frame
+ * @meas_window: measurement window coordinates
+ */
+struct rppx1_params_aec_config {
+	struct v4l2_isp_params_block_header header;
+	__u32 mode;
+	__u32 autostop;
+	struct rppx1_window meas_window;
+};
+
+/**
+ * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
+ */
+#define RPPX1_PARAMS_MAX_SIZE						\
+	(sizeof(struct rppx1_params_bls_config)			+	\
+	sizeof(struct rppx1_params_dpcc_config)			+	\
+	sizeof(struct rppx1_params_lin_config)			+	\
+	sizeof(struct rppx1_params_awb_gain_config)		+	\
+	sizeof(struct rppx1_params_flt_config)			+	\
+	sizeof(struct rppx1_params_bdm_config)			+	\
+	sizeof(struct rppx1_params_ctk_config)			+	\
+	sizeof(struct rppx1_params_goc_config)			+	\
+	sizeof(struct rppx1_params_dpf_config)			+	\
+	sizeof(struct rppx1_params_dpf_strength_config)		+	\
+	sizeof(struct rppx1_params_lsc_config)			+	\
+	sizeof(struct rppx1_params_awb_meas_config)		+	\
+	sizeof(struct rppx1_params_hst_config)			+	\
+	sizeof(struct rppx1_params_aec_config))
+
+/* ---------------------------------------------------------------------------
+ * Statistics Structures
+ *
+ * Native RPP-X1 precision. Fields use __u32 where the hardware provides
+ * wider-than-8-bit results.
+ */
+
+/**
+ * struct rppx1_awb_meas - AWB measured values
+ *
+ * @cnt: white pixel count
+ * @mean_y_or_g: mean Y (or G in RGB mode), up to 24-bit
+ * @mean_cb_or_b: mean Cb (or B in RGB mode), up to 24-bit
+ * @mean_cr_or_r: mean Cr (or R in RGB mode), up to 24-bit
+ */
+struct rppx1_awb_meas {
+	__u32 cnt;
+	__u32 mean_y_or_g;
+	__u32 mean_cb_or_b;
+	__u32 mean_cr_or_r;
+};
+
+/**
+ * struct rppx1_awb_stat - AWB statistics
+ *
+ * @awb_mean: measured AWB data
+ */
+struct rppx1_awb_stat {
+	struct rppx1_awb_meas awb_mean[RPPX1_AWB_MAX_GRID];
+};
+
+/**
+ * struct rppx1_bls_meas_val - BLS measured values
+ *
+ * RPP-X1 BLS statistics can be 8/20/24-bit depending on version.
+ *
+ * @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_meas_val {
+	__u32 meas_r;
+	__u32 meas_gr;
+	__u32 meas_gb;
+	__u32 meas_b;
+};
+
+/**
+ * struct rppx1_ae_stat - Auto Exposure statistics
+ *
+ * RPP-X1 exposure mean values are up to 20-bit depending on version.
+ * The image is divided into a 5x5 grid (25 blocks).
+ *
+ * @exp_mean: mean luminance values per block, up to 20-bit
+ * @bls_val: BLS measured values
+ */
+struct rppx1_ae_stat {
+	__u32 exp_mean[RPPX1_EXM_MEAN_MAX];
+	struct rppx1_bls_meas_val bls_val;
+};
+
+/**
+ * struct rppx1_hist_stat - Histogram statistics
+ *
+ * @hist_bins: 32 histogram bin counters, each 20-bit unsigned fixed point
+ *	       (bits 0-4 fractional, bits 5-19 integer)
+ */
+struct rppx1_hist_stat {
+	__u32 hist_bins[RPPX1_HIST_BIN_N_MAX];
+};
+
+/**
+ * struct rppx1_stat - RPP-X1 3A statistics
+ *
+ * @awb: auto white balance statistics
+ * @ae: auto exposure statistics
+ * @hist: histogram statistics
+ */
+struct rppx1_stat {
+	struct rppx1_awb_stat awb;
+	struct rppx1_ae_stat ae;
+	struct rppx1_hist_stat hist;
+};
+
+/**
+ * RPPX1_STAT_AWB - AWB measurement data available
+ * RPPX1_STAT_AUTOEXP - Auto exposure measurement data available
+ * RPPX1_STAT_HIST - Histogram measurement data available
+ */
+#define RPPX1_STAT_AWB			(1U << 0)
+#define RPPX1_STAT_AUTOEXP		(1U << 1)
+#define RPPX1_STAT_HIST			(1U << 2)
+
+/**
+ * struct rppx1_stat_buffer - RPP-X1 statistics metadata buffer
+ *
+ * @meas_type: bitmask of available measurements (RPPX1_STAT_*)
+ * @frame_id: frame identifier for synchronization
+ * @params: statistics data
+ */
+struct rppx1_stat_buffer {
+	__u32 meas_type;
+	__u32 frame_id;
+	struct rppx1_stat params;
+};
+
+#endif /* __UAPI_RPP_X1_CONFIG_H */

-- 
2.53.0


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

* [PATCH v7 03/18] media: rppx1: Add framework to support Dreamchip RPPX1 ISP
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 01/18] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 02/18] media: uapi: Add extensible param and stats blocks for RPPX1 Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 04/18] media: rcar-isp: Add support for ISPCORE Jai Luthra
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

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 functions provided by the RPPX1 are similar to what is exposed by
other ISP drivers already in tree (RkISP1 primarily), but the
implementation of them are different.

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(). This is intended to translate the
    parameter buffer to the register writes needed to be preformed on
    the RPPX1. 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>
---
 MAINTAINERS                                        |   7 +
 drivers/media/platform/Kconfig                     |   1 +
 drivers/media/platform/Makefile                    |   1 +
 drivers/media/platform/dreamchip/Kconfig           |   3 +
 drivers/media/platform/dreamchip/Makefile          |   6 +
 drivers/media/platform/dreamchip/rppx1/Kconfig     |  12 +
 drivers/media/platform/dreamchip/rppx1/Makefile    |  33 ++
 .../media/platform/dreamchip/rppx1/rpp_module.c    |  40 +++
 .../media/platform/dreamchip/rppx1/rpp_module.h    | 155 ++++++++++
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  72 +++++
 drivers/media/platform/dreamchip/rppx1/rpp_stats.c |  15 +
 drivers/media/platform/dreamchip/rppx1/rppx1.c     | 338 +++++++++++++++++++++
 drivers/media/platform/dreamchip/rppx1/rppx1.h     |  99 ++++++
 drivers/media/platform/dreamchip/rppx1/rppx1_acq.c | 147 +++++++++
 .../media/platform/dreamchip/rppx1/rppx1_awbg.c    |  30 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_bd.c  |  52 ++++
 .../media/platform/dreamchip/rppx1/rppx1_bdrgb.c   |  80 +++++
 drivers/media/platform/dreamchip/rppx1/rppx1_bls.c |  59 ++++
 drivers/media/platform/dreamchip/rppx1/rppx1_cac.c |  29 ++
 .../media/platform/dreamchip/rppx1/rppx1_ccor.c    | 106 +++++++
 drivers/media/platform/dreamchip/rppx1/rppx1_db.c  |  44 +++
 .../media/platform/dreamchip/rppx1/rppx1_dpcc.c    |  76 +++++
 drivers/media/platform/dreamchip/rppx1/rppx1_exm.c |  51 ++++
 drivers/media/platform/dreamchip/rppx1/rppx1_ga.c  |  49 +++
 .../media/platform/dreamchip/rppx1/rppx1_hist.c    |  76 +++++
 .../media/platform/dreamchip/rppx1/rppx1_hist256.c |  46 +++
 drivers/media/platform/dreamchip/rppx1/rppx1_is.c  |  42 +++
 drivers/media/platform/dreamchip/rppx1/rppx1_lin.c |  60 ++++
 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c |  68 +++++
 drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c |  48 +++
 .../media/platform/dreamchip/rppx1/rppx1_ltmmeas.c |  41 +++
 .../media/platform/dreamchip/rppx1/rppx1_outif.c   |  45 +++
 .../media/platform/dreamchip/rppx1/rppx1_outregs.c |  75 +++++
 .../media/platform/dreamchip/rppx1/rppx1_rmap.c    |  64 ++++
 .../platform/dreamchip/rppx1/rppx1_rmapmeas.c      |  47 +++
 .../media/platform/dreamchip/rppx1/rppx1_shrp.c    |  64 ++++
 .../media/platform/dreamchip/rppx1/rppx1_wbmeas.c  |  61 ++++
 .../media/platform/dreamchip/rppx1/rppx1_xyz2luv.c |  26 ++
 include/media/rppx1.h                              |  33 ++
 39 files changed, 2301 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index c3fe46d7c4bc43e960282537a7d89fd4c34080f7..9325268157121a892effed3174fdc50dd836daac 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7783,6 +7783,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 3f0b7bb68cc94086347239118d7a92b65734b785..3621b46e1e8d75241bc30a1a9736a1dd457be066 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 6d5f79ddfcc37a7d2168edff018475c3d73ff101..0cc4bfe0da326fac19c510b5eadcdbf3204758c2 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 0000000000000000000000000000000000000000..d177d4ee79ae17022328219f876100920324d140
--- /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 0000000000000000000000000000000000000000..ba47ba2d136e6e13c86211bbbedfa2f438e51f5c
--- /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 0000000000000000000000000000000000000000..0998a7d10bf28c9b14422716c51bebc3887da418
--- /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 0000000000000000000000000000000000000000..b2bd6b5d68bcf951988ef2d821ee1e97b155ab08
--- /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 0000000000000000000000000000000000000000..cd923b7ff5c1322dd225e256e1e534786de7ffe8
--- /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 0000000000000000000000000000000000000000..747ee4b928d0944a0f08091890fb6aa4090e5ee6
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -0,0 +1,155 @@
+/* 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;
+	struct rppx1_params_bls_config bls;
+	struct rppx1_params_dpcc_config dpcc;
+	struct rppx1_params_lin_config lin;
+	struct rppx1_params_lsc_config lsc;
+	struct rppx1_params_awb_gain_config awbg;
+	struct rppx1_params_flt_config flt;
+	struct rppx1_params_bdm_config bdm;
+	struct rppx1_params_ctk_config ctk;
+	struct rppx1_params_goc_config goc;
+	struct rppx1_params_dpf_config dpf;
+	struct rppx1_params_dpf_strength_config dpfs;
+	struct rppx1_params_awb_meas_config awbm;
+	struct rppx1_params_hst_config hst;
+	struct rppx1_params_aec_config aec;
+};
+
+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,
+			  struct rppx1_stat *stats);
+};
+
+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 0000000000000000000000000000000000000000..6d2a8d45056ae19c9c05b7e38c1aa013cbe2706b
--- /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_params_ ## data ## _config), \
+	}
+
+static const struct v4l2_isp_params_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 0000000000000000000000000000000000000000..b62e792b84a105baec904640ec606670b9bbbadd
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 Renesas Electronics Corp.
+ * Copyright 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include "rppx1.h"
+
+void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
+{
+	struct rppx1_stat_buffer *stats = buf;
+
+	stats->meas_type = 0;
+}
+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 0000000000000000000000000000000000000000..06e299f90aecaa5f6a75129cf5ce174c2f4197c9
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1.c
@@ -0,0 +1,338 @@
+// 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 rppx1 *rpp;
+	u32 reg;
+
+	/* Allocate library structure */
+	rpp = kzalloc(sizeof(*rpp), GFP_KERNEL);
+	if (!rpp)
+		return NULL;
+
+	rpp->base = base;
+
+	/* 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 0000000000000000000000000000000000000000..dcf43826d3083c56a7d4065bb3b28ccf70fea75d
--- /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 0000000000000000000000000000000000000000..45f619ccb684d96054028459b311fb37e6923f69
--- /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 0000000000000000000000000000000000000000..e20bc369ca8c6db3781ed95381024f0fa4c48dff
--- /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 0000000000000000000000000000000000000000..acbfbcd595915fbb36221bc3e6a63cfdc954409e
--- /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 0000000000000000000000000000000000000000..292f0b7bfd3fff6e4d9fd736e551e17ed3aa3d00
--- /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 0000000000000000000000000000000000000000..de7008befd8ea79f7bca974de7714399e8bf443c
--- /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 0000000000000000000000000000000000000000..5ed8c60982ba02f183015f80beff852e22a0edc1
--- /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 0000000000000000000000000000000000000000..4754b0bbce0a13678a91b2e40f001aed98ddabfc
--- /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 0000000000000000000000000000000000000000..5e233896cfc8b66e7d90770171b77a2fabc3cd9b
--- /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 0000000000000000000000000000000000000000..ae0b659764528efd49cd293358294cc0d06ed0d0
--- /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 0000000000000000000000000000000000000000..0c40300e13ad934c02106d804dd776990983792f
--- /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 0000000000000000000000000000000000000000..d6c7f951cf2972a8d633b7915818e26f8d0a1cf5
--- /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 0000000000000000000000000000000000000000..cab498ece5a8fac01e9e6c048b786f2aed829dd2
--- /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 0000000000000000000000000000000000000000..5b846b415a497427b25187b5e2058d4e22ae5916
--- /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 0000000000000000000000000000000000000000..3637a2e677ca42a0eaae6ea114621d7d2d24cc8a
--- /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 0000000000000000000000000000000000000000..e4b0a7be76656c4bc04408500c7ca60709bebf79
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
@@ -0,0 +1,60 @@
+// 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)))
+
+#define LIN_SAMPLES_NUM	17
+
+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 0000000000000000000000000000000000000000..e8acdf74495633790e2614a9985a47fa92df4eeb
--- /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 0000000000000000000000000000000000000000..693cf5ed16891de67d5c15da4dbee71533ec06ea
--- /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 0000000000000000000000000000000000000000..efc3d09db5ebea2b4ce195410fd8c6d07adfcc67
--- /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 0000000000000000000000000000000000000000..742c818449129fe92daedaf4ef4ede1c93f671d2
--- /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 0000000000000000000000000000000000000000..63d61e1dc447e6376e9a1c9ec21b71b6830c3452
--- /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 0000000000000000000000000000000000000000..f6773a452bd1e02885e509535eea1069bd00d79b
--- /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 0000000000000000000000000000000000000000..c04f92508f6d3e5978e1b0c173679efb67406019
--- /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 0000000000000000000000000000000000000000..5bec022e8f0517e84b5481c227b87cea8cf82682
--- /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 0000000000000000000000000000000000000000..3d197d914d0710128a61842ac3db54ddcce8c45e
--- /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 0000000000000000000000000000000000000000..73789c48c0574dbe5ca26fd47837dd9b8203d4fa
--- /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 0000000000000000000000000000000000000000..cb911f7bf010fbba549a00c9d00f5c7f950d3281
--- /dev/null
+++ b/include/media/rppx1.h
@@ -0,0 +1,33 @@
+/* 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);
+
+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__ */

-- 
2.53.0


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

* [PATCH v7 04/18] media: rcar-isp: Add support for ISPCORE
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (2 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 03/18] media: rppx1: Add framework to support Dreamchip RPPX1 ISP Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 05/18] media: rppx1: Add support for AWB measurement parameters and statistics Jai Luthra
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

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>
---
 drivers/media/platform/renesas/rcar-isp/Kconfig    |    2 +
 drivers/media/platform/renesas/rcar-isp/Makefile   |    2 +-
 drivers/media/platform/renesas/rcar-isp/core-io.c  | 1017 ++++++++++++++++++++
 drivers/media/platform/renesas/rcar-isp/core.c     |  826 ++++++++++++++++
 drivers/media/platform/renesas/rcar-isp/csisp.c    |   46 +-
 .../media/platform/renesas/rcar-isp/risp-core.h    |  170 ++++
 6 files changed, 2056 insertions(+), 7 deletions(-)

diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig
index 242f6a23851fcc62a536857e10b30f1d30dda3cc..bacc15c250fe815bb5bc98e2c33268cbc34e26a5 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 b542118c831e35eb59520726895c1ffa5edb1b6e..c0c80303682cd6180de9834e1b70e6624d692246 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 0000000000000000000000000000000000000000..685f14a3151fe838ecd24d4761d92dc978855834
--- /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_params_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 = sizeof(struct rppx1_stat_buffer),
+};
+
+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 0000000000000000000000000000000000000000..41ba82bf237a395bb01319966d1cb0021e1695ca
--- /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);
+
+	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 1eb29a0b774a98538d0a4da11a0df6259417ef5e..9d8571916c96a0f320dc81d6e0405c5ae27d86a9 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)
@@ -161,6 +160,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;
 
@@ -454,6 +454,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
  */
@@ -480,7 +495,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);
 }
@@ -544,14 +559,31 @@ static int risp_probe(struct platform_device *pdev)
 	if (ret)
 		goto error_notifier;
 
+	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_subdev;
+		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:
@@ -567,6 +599,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 0000000000000000000000000000000000000000..eab4f4eac466ae23f61d353809b1934e95f2aded
--- /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_job - 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.53.0


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

* [PATCH v7 05/18] media: rppx1: Add support for AWB measurement parameters and statistics
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (3 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 04/18] media: rcar-isp: Add support for ISPCORE Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 06/18] media: rppx1: Add support for AWB gain settings Jai Luthra
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to parse parameter blocks configuring for the
auto white balance measurement window and parameters. As well as
producing the measurements as part of a statistics buffer.

This is the first ISP algorithm added to the RPPX1 driver and exercises
both the parameter and statistics API provided by the base driver.

It uses the parameter writing interface which allows the framework user
to specify how (and when) the configuration are applied to the RPPX1.

Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |   4 +
 drivers/media/platform/dreamchip/rppx1/rpp_stats.c |   4 +
 .../media/platform/dreamchip/rppx1/rppx1_wbmeas.c  | 118 +++++++++++++++++++++
 3 files changed, 126 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 6d2a8d45056ae19c9c05b7e38c1aa013cbe2706b..868a5ce1620e185174b8fade8a9a697826b3fed5 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_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
+	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 };
 
 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_AWB_MEAS:
+			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 b62e792b84a105baec904640ec606670b9bbbadd..aac7692d2a61e919e32d7684af86f856ab5a22bb 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -11,5 +11,9 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
 	struct rppx1_stat_buffer *stats = buf;
 
 	stats->meas_type = 0;
+
+	if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS)
+		if (!rpp_module_call(&rpp->post.wbmeas, fill_stats, &stats->params))
+			stats->meas_type |= RPPX1_STAT_AWB;
 }
 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 3d197d914d0710128a61842ac3db54ddcce8c45e..e6a3b2eece1576a2daf0df4ca243d5a1e93ef662 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
@@ -56,6 +56,124 @@ 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_params_awb_meas_config *cfg = &block->awbm;
+	/*
+	 * The native params are 24-bit while the RPP can be 8, 20 or 24 bit.
+	 * Figure out how much we need to adjust the input parameters.
+	 */
+	const unsigned int shift = 24 - mod->info.wbmeas.colorbits;
+
+	/* 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->awb_wnd.h_offs);
+	write(priv, mod->base + AWB_MEAS_V_OFFS_REG,
+	      cfg->awb_wnd.v_offs);
+	write(priv, mod->base + AWB_MEAS_H_SIZE_REG,
+	      cfg->awb_wnd.h_size);
+	write(priv, mod->base + AWB_MEAS_V_SIZE_REG,
+	      cfg->awb_wnd.v_size);
+
+	/* Set number of frames to sample. */
+	write(priv, mod->base + AWB_MEAS_FRAMES_REG, cfg->frames);
+
+	if (cfg->awb_mode == RPPX1_AWB_MODE_YCBCR) {
+		write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+		      cfg->awb_ref_cb >> shift);
+		write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+		      cfg->awb_ref_cr >> shift);
+		write(priv, mod->base + AWB_MEAS_MAX_Y_REG,
+		      cfg->max_y >> shift);
+		write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+		      cfg->min_y >> shift);
+		write(priv, mod->base + AWB_MEAS_MAX_CSUM_REG,
+		      cfg->max_csum >> shift);
+		write(priv, mod->base + AWB_MEAS_MIN_C_REG,
+		      cfg->min_c >> shift);
+
+		/*
+		 * Match RkISP1 conversion, documented as
+		 *  Y = 16 + 0.2500 R + 0.5000 G + 0.1094 B
+		 *  Cb = 128 - 0.1406 R - 0.2969 G + 0.4375 B
+		 *  Cr = 128 + 0.4375 R - 0.3750 G - 0.0625 B
+		 *
+		 * Note map Y to G. Matrix is GBR, not RGB documented for RPPX1.
+		 */
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x0800);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x01c0);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0400);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0xfb40);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x0700);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0xfdc0);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0xfa00);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0xff00);
+		write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x0700);
+
+		write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00100000);
+		write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00800000);
+		write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00800000);
+
+		write(priv, mod->base + AWB_MEAS_PROP_REG,
+		      cfg->enable_ymax_cmp ? AWB_MEAS_PROP_YMAX : 0 |
+		      AWB_MEAS_PROP_AWB_MODE_ON);
+	} else {
+		write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+		      cfg->awb_ref_cb >> shift);
+		write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+		      cfg->awb_ref_cr >> shift);
+		write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+		      cfg->min_y >> shift);
+
+		/* Values from datasheet to map G to Y, B to Cb and R to Cr. */
+		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);
+
+		/* Values from datasheet. */
+		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);
+
+		write(priv, mod->base + AWB_MEAS_PROP_REG,
+		      AWB_MEAS_PROP_MEAS_MODE_RGB |
+		      AWB_MEAS_PROP_AWB_MODE_ON);
+	}
+
+	return 0;
+}
+
+static int rppx1_wbmeas_fill_stats(struct rpp_module *mod,
+				   struct rppx1_stat *stats)
+{
+	struct rppx1_awb_meas *meas = &stats->awb.awb_mean[0];
+
+	/* Return measurements at native hardware precision. */
+	meas->cnt = rpp_module_read(mod, AWB_MEAS_WHITE_CNT_REG);
+	meas->mean_y_or_g = rpp_module_read(mod, AWB_MEAS_MEAN_Y_G_REG);
+	meas->mean_cb_or_b = rpp_module_read(mod, AWB_MEAS_MEAN_CB_B_REG);
+	meas->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
 };

-- 
2.53.0


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

* [PATCH v7 06/18] media: rppx1: Add support for AWB gain settings
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (4 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 05/18] media: rppx1: Add support for AWB measurement parameters and statistics Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 07/18] media: rppx1: Add support for Auto Exposure Measurement Jai Luthra
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the AWB gains using the
parameter buffer format. This is the second function block inside the
RPPX1 to be enabled and 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>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  4 +++
 .../media/platform/dreamchip/rppx1/rppx1_awbg.c    | 32 ++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 868a5ce1620e185174b8fade8a9a697826b3fed5..de47be81133d067a85f63ba0eb3f8f5e1f0ba2d3 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_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
+	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 };
 
@@ -51,6 +52,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_AWB_GAIN:
+			module = &rpp->pre1.awbg;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS:
 			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 e20bc369ca8c6db3781ed95381024f0fa4c48dff..0be63cc8dfbec573da8dec921f08f7bcc1b95a82 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_params_awb_gain_config *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,
 };

-- 
2.53.0


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

* [PATCH v7 07/18] media: rppx1: Add support for Auto Exposure Measurement
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (5 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 06/18] media: rppx1: Add support for AWB gain settings Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH SQUASH v7 08/18] media: rppx1: exm: Expose coefficients, RGB mode and channel selection Jai Luthra
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the EXM configuration using the
parameter buffer format. 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>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  4 ++
 drivers/media/platform/dreamchip/rppx1/rpp_stats.c |  4 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_exm.c | 82 ++++++++++++++++++++++
 3 files changed, 90 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index de47be81133d067a85f63ba0eb3f8f5e1f0ba2d3..928e4fa196c0e4c0db6a23a89833359645a0941e 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -18,6 +18,7 @@ static const struct v4l2_isp_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
+	RPPX1_PARAMS_BLOCK_INFO(AEC_MEAS, aec),
 };
 
 int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
@@ -58,6 +59,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS:
 			module = &rpp->post.wbmeas;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS:
+			module = &rpp->pre1.exm;
+			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 aac7692d2a61e919e32d7684af86f856ab5a22bb..44edcbc7f2b644d7d569d46707b0b4b94ebe4144 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -15,5 +15,9 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
 	if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS)
 		if (!rpp_module_call(&rpp->post.wbmeas, fill_stats, &stats->params))
 			stats->meas_type |= RPPX1_STAT_AWB;
+
+	if (isc & RPPX1_IRQ_ID_PRE1_EXM)
+		if (!rpp_module_call(&rpp->pre1.exm, fill_stats, &stats->params))
+			stats->meas_type |= RPPX1_STAT_AUTOEXP;
 }
 EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
index 0c40300e13ad934c02106d804dd776990983792f..f756f7f882a124850a0908d9efa564443de01b2a 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
@@ -10,6 +10,7 @@
 #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
@@ -46,6 +47,87 @@ 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_params_aec_config *cfg = &block->aec;
+	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;
+	}
+
+	/* RGB bayer exposure measurement */
+	write(priv, mod->base + EXM_MODE_REG, 2);
+
+	write(priv, mod->base + EXM_CTRL_REG, EXM_CTRL_EXM_UPDATE_ENABLE |
+	      cfg->autostop ? EXM_CTRL_EXM_AUTOSTOP : 0);
+
+	/*
+	 * Select where to sample.
+	 * 0 - after input acquisition
+	 * 1 - after black level subtraction
+	 * 2 - after input linearization
+	 * 3 - after lens shade correction
+	 * 4 - after white balance gain stage
+	 * 5 - after defect pixel correction
+	 * 6 - after denoising
+	 */
+	write(priv, mod->base + EXM_CHANNEL_SEL_REG, 6);
+
+	if (cfg->mode == RPPX1_EXP_MEASURING_MODE_0) {
+		/* Coefficients for a BT.601 BAYER (from datasheet). */
+		write(priv, mod->base + EXM_COEFF_R_REG, 38);
+		write(priv, mod->base + EXM_COEFF_G_GR_REG, 75);
+		write(priv, mod->base + EXM_COEFF_B_REG, 15);
+		write(priv, mod->base + EXM_COEFF_GB_REG, 75);
+	} else {
+		/* Y = (R + Gr + B + Gb) / 4*/
+		write(priv, mod->base + EXM_COEFF_R_REG, 128);
+		write(priv, mod->base + EXM_COEFF_G_GR_REG, 128);
+		write(priv, mod->base + EXM_COEFF_B_REG, 128);
+		write(priv, mod->base + EXM_COEFF_GB_REG, 128);
+	}
+
+	/*
+	 * Adjust and set measurement window to hardware limitations,
+	 * - Offsets must be even.
+	 * - Width and height must be divisible by 10.
+	 */
+	h_offs = cfg->meas_window.h_offs & 0x1ffe;
+	v_offs = cfg->meas_window.v_offs & 0x1ffe;
+	h_size = (cfg->meas_window.h_size - 1) - ((cfg->meas_window.h_size - 1) % 10);
+	v_size = (cfg->meas_window.v_size - 1) - ((cfg->meas_window.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. */
+	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,
+				struct rppx1_stat *stats)
+{
+	/* Return measurements at native hardware precision. */
+	for (unsigned int i = 0; i < EXM_MEAN_REG_NUM; i++)
+		stats->ae.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,
 };

-- 
2.53.0


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

* [PATCH SQUASH v7 08/18] media: rppx1: exm: Expose coefficients, RGB mode and channel selection
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (6 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 07/18] media: rppx1: Add support for Auto Exposure Measurement Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 09/18] media: rppx1: Add support for Histogram Measurement Jai Luthra
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

Compared to RkISP, Dreamchip's RPP-X1's exposure measurement module
supports following extra features:

1. Measurement modes (bayer or RGB domain)
2. Programmable coefficients for RGB or Bayer channels
3. Choice for sampling point (channel selector) in the pipeline

Expose these features in the uAPI and support them in the driver.

Note: This commit's changes will be squashed into the relevant uAPI and
driver commits. It is separate for now to ease review and highlight the
differences.

Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 drivers/media/platform/dreamchip/rppx1/rppx1_exm.c | 47 ++++++++----------
 include/uapi/linux/media/dreamchip/rppx1-config.h  | 57 ++++++++++++++++++++--
 2 files changed, 73 insertions(+), 31 deletions(-)

diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
index f756f7f882a124850a0908d9efa564443de01b2a..839aa8a18b895c71305c44c317b7125882af3c97 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
@@ -14,7 +14,10 @@
 #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
@@ -61,37 +64,27 @@ rppx1_exm_fill_params(struct rpp_module *mod,
 		return 0;
 	}
 
-	/* RGB bayer exposure measurement */
-	write(priv, mod->base + EXM_MODE_REG, 2);
+	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.red);
+	write(priv, mod->base + EXM_COEFF_G_GR_REG, cfg->coeff.green_r);
+	write(priv, mod->base + EXM_COEFF_GB_REG, cfg->coeff.green_b);
+	write(priv, mod->base + EXM_COEFF_B_REG, cfg->coeff.blue);
 
 	write(priv, mod->base + EXM_CTRL_REG, EXM_CTRL_EXM_UPDATE_ENABLE |
 	      cfg->autostop ? EXM_CTRL_EXM_AUTOSTOP : 0);
 
-	/*
-	 * Select where to sample.
-	 * 0 - after input acquisition
-	 * 1 - after black level subtraction
-	 * 2 - after input linearization
-	 * 3 - after lens shade correction
-	 * 4 - after white balance gain stage
-	 * 5 - after defect pixel correction
-	 * 6 - after denoising
-	 */
-	write(priv, mod->base + EXM_CHANNEL_SEL_REG, 6);
-
-	if (cfg->mode == RPPX1_EXP_MEASURING_MODE_0) {
-		/* Coefficients for a BT.601 BAYER (from datasheet). */
-		write(priv, mod->base + EXM_COEFF_R_REG, 38);
-		write(priv, mod->base + EXM_COEFF_G_GR_REG, 75);
-		write(priv, mod->base + EXM_COEFF_B_REG, 15);
-		write(priv, mod->base + EXM_COEFF_GB_REG, 75);
-	} else {
-		/* Y = (R + Gr + B + Gb) / 4*/
-		write(priv, mod->base + EXM_COEFF_R_REG, 128);
-		write(priv, mod->base + EXM_COEFF_G_GR_REG, 128);
-		write(priv, mod->base + EXM_COEFF_B_REG, 128);
-		write(priv, mod->base + EXM_COEFF_GB_REG, 128);
-	}
+	/* 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,
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index b9083e6f32b15329333eb13491b50c0aea8d1a32..2adf5f9e083b89c0308a8728f8468f326ab87c48 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -537,6 +537,36 @@ struct rppx1_params_awb_meas_config {
 	__u8 enable_ymax_cmp;
 };
 
+/**
+ * enum rppx1_pre_meas_chan - Measurement point for PRE1/2 modules
+ * @RPPX1_PRE_MEASURE_AFTER_ACQ: after input acquisition
+ * @RPPX1_PRE_MEASURE_AFTER_BLS: after black level subtraction
+ * @RPPX1_PRE_MEASURE_AFTER_LIN: after sensor gamma linearization
+ * @RPPX1_PRE_MEASURE_AFTER_LSC: after lens shading correction
+ * @RPPX1_PRE_MEASURE_AFTER_AWBG: after auto white balance gains
+ * @RPPX1_PRE_MEASURE_AFTER_DPCC: after defect pixel correction
+ * @RPPX1_PRE_MEASURE_AFTER_DPF: after denoise pre-filter
+ */
+enum rppx1_pre_meas_chan {
+	RPPX1_PRE_MEASURE_AFTER_ACQ,
+	RPPX1_PRE_MEASURE_AFTER_BLS,
+	RPPX1_PRE_MEASURE_AFTER_LIN,
+	RPPX1_PRE_MEASURE_AFTER_LSC,
+	RPPX1_PRE_MEASURE_AFTER_AWBG,
+	RPPX1_PRE_MEASURE_AFTER_DPCC,
+	RPPX1_PRE_MEASURE_AFTER_DPF,
+};
+
+/**
+ * enum rppx1_post_meas_chan - Measurement point for POST modules
+ * @RPPX1_PRE_MEASURE_AFTER_AWBG: after auto white balance gains
+ * @RPPX1_PRE_MEASURE_AFTER_DEMOSAIC: after demosaicing
+ */
+enum rppx1_post_meas_chan {
+	RPPX1_POST_MEASURE_AFTER_AWBG = 4,
+	RPPX1_POST_MEASURE_AFTER_DEMOSAIC = 7,
+};
+
 /**
  * enum rppx1_histogram_mode - Histogram measurement mode
  * @RPPX1_HISTOGRAM_MODE_DISABLE: histogram disabled
@@ -574,14 +604,29 @@ struct rppx1_params_hst_config {
 	__u8 hist_weight[RPPX1_HISTOGRAM_WEIGHT_GRIDS_SIZE];
 };
 
+/**
+ * struct rppx1_aec_coeff - Coefficients for exposure measurement
+ *
+ * @red: Coefficient for weighting Red sample/channel (Q1.7)
+ * @green_r: Coefficient for weighting GreenRed bayer sample or Green channel (Q1.7)
+ * @green_b: Coefficient for weighting GreenBlue bayer sample (Q1.7)
+ * @blue: Coefficient for weighting Blue sample/channel (Q1.7)
+ */
+struct rppx1_aec_coeff {
+	__u8 red;
+	__u8 green_r;
+	__u8 green_b;
+	__u8 blue;
+};
+
 /**
  * enum rppx1_exp_meas_mode - Exposure measurement mode
- * @RPPX1_EXP_MEASURING_MODE_0: Y = 16 + 0.25R + 0.5G + 0.1094B
- * @RPPX1_EXP_MEASURING_MODE_1: Y = (R + G + B) x (85/256)
+ * @RPPX1_EXP_MEASURING_MODE_RGB: out_sample = coeff_r * R + coeff_gr * G + coeff_b * B
+ * @RPPX1_EXP_MEASURING_MODE_BAYER: out_sample = coeff_[r|gr|gb|b] * [R|Gr|Gb|B]
  */
 enum rppx1_exp_meas_mode {
-	RPPX1_EXP_MEASURING_MODE_0,
-	RPPX1_EXP_MEASURING_MODE_1,
+	RPPX1_EXP_MEASURING_MODE_RGB = 1,
+	RPPX1_EXP_MEASURING_MODE_BAYER,
 };
 
 /**
@@ -591,12 +636,16 @@ enum rppx1_exp_meas_mode {
  * @mode: exposure measure mode (from enum rppx1_exp_meas_mode)
  * @autostop: 0 = continuous, 1 = stop after one frame
  * @meas_window: measurement window coordinates
+ * @coeff: weighting coefficients for R/Gr/Gb/B
+ * @channel_sel: measurement point (see enum rppx1_[pre|post]_meas_chan)
  */
 struct rppx1_params_aec_config {
 	struct v4l2_isp_params_block_header header;
 	__u32 mode;
 	__u32 autostop;
 	struct rppx1_window meas_window;
+	struct rppx1_aec_coeff coeff;
+	__u8 channel_sel;
 };
 
 /**

-- 
2.53.0


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

* [PATCH v7 09/18] media: rppx1: Add support for Histogram Measurement
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (7 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH SQUASH v7 08/18] media: rppx1: exm: Expose coefficients, RGB mode and channel selection Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH SQUASH v7 10/18] media: rppx1: hist: Expose channel selection Jai Luthra
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the Histogram configuration
using the parameter buffer format. 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>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |   4 +
 drivers/media/platform/dreamchip/rppx1/rpp_stats.c |   4 +
 .../media/platform/dreamchip/rppx1/rppx1_hist.c    | 172 +++++++++++++++++++++
 3 files changed, 180 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 928e4fa196c0e4c0db6a23a89833359645a0941e..788d26f619d1a96d16e9dc499d2763366f70be0a 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -18,6 +18,7 @@ static const struct v4l2_isp_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
+	RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
 	RPPX1_PARAMS_BLOCK_INFO(AEC_MEAS, aec),
 };
 
@@ -59,6 +60,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS:
 			module = &rpp->post.wbmeas;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS:
+			module = &rpp->post.hist;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS:
 			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 44edcbc7f2b644d7d569d46707b0b4b94ebe4144..6f3be95026f60252d05370421bb2ec6ea090ce6f 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -16,6 +16,10 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
 		if (!rpp_module_call(&rpp->post.wbmeas, fill_stats, &stats->params))
 			stats->meas_type |= RPPX1_STAT_AWB;
 
+	if (isc & RPPX1_IRQ_ID_POST_HIST_MEAS)
+		if (!rpp_module_call(&rpp->post.hist, fill_stats, &stats->params))
+			stats->meas_type |= RPPX1_STAT_HIST;
+
 	if (isc & RPPX1_IRQ_ID_PRE1_EXM)
 		if (!rpp_module_call(&rpp->pre1.exm, fill_stats, &stats->params))
 			stats->meas_type |= RPPX1_STAT_AUTOEXP;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
index cab498ece5a8fac01e9e6c048b786f2aed829dd2..6287f9c401dfc8ee7f1c19acef1a3e82c5036ac7 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
@@ -71,6 +74,175 @@ 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_params_hst_config *cfg = &block->hst;
+	u32 h_offs, v_offs, h_size, v_size;
+	u8 mode, coeff[3];
+
+	/* 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;
+	}
+
+	/* Sample after demosaicing. */
+	write(priv, mod->base + HIST_CHANNEL_SEL_REG, 7);
+
+	/*
+	 * The RkISP1 histogram_predivider setting controls the pixel spacing
+	 * between each sample. On RPPX1 there is greater control as both line
+	 * and pixel spacing can be controlled.  The RkISP1 stepsize register is
+	 * documented as.
+	 *
+	 *  0, 1, 3: not allowed
+	 *  3: process every third input pixel
+	 *  4: process every fourth input pixel
+	 *  127: process every 127th pixel
+	 *
+	 * The output bins are 16 bit (FP16.4) so to not overflow a divider
+	 * calculated as would be needed.
+	 *
+	 *  count = mode == RGB_COMBINED ? 3 : 1
+	 *  factor = vsize * hsize  * count / 65536
+	 *
+	 * However the libcamera user of the RkISP documents the setting as
+	 * applying to both h and v direction at the same time and calculates
+	 * the divider as,
+	 *
+	 *  count = mode == RGB_COMBINED ? 3 : 1
+	 *  factor = ceil(sqrt(vsize * hsize  * count / 65536))
+	 *
+	 * Real world usage is better then bad documentation, do the same here
+	 * and apply the divider in both directions.
+	 *
+	 * The RPPX1 h-stepping is also configured differently. Internally
+	 * there is a 16-bit counter and for each input pixel h_step_inc is
+	 * added to it. Every time it overflows the input pixel is sampled.
+	 *
+	 *  h_step_inc = 2**16 => sample every pixel
+	 *  h_step_inc = 2**15 => sample every other pixel
+	 *
+	 * Gives us the conversion to RkISP1 parameters of.
+	 *
+	 *  h_step_inc = 65536 / divider
+	 */
+	write(priv, mod->base + HIST_SUBSAMPLING_REG,
+	      HIST_SUBSAMPLING_V_STEPSIZE(cfg->histogram_predivider) |
+	      HIST_SUBSAMPLING_H_STEP_INC(0x10000 / cfg->histogram_predivider));
+
+	/*
+	 * Adjust and set measurement window to hardware limitations,
+	 * - Offsets must be even.
+	 * - Width and height must be divisible by 10.
+	 */
+	h_offs = cfg->meas_window.h_offs & 0x1ffe;
+	v_offs = cfg->meas_window.v_offs & 0x1ffe;
+	h_size = cfg->meas_window.h_size - cfg->meas_window.h_size % 10;
+	v_size = cfg->meas_window.v_size - cfg->meas_window.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. */
+	write(priv, mod->base + HIST_LAST_MEAS_LINE_REG,
+	      v_offs + v_size + 1);
+
+	/* NOTE: Keep the default full sample range. */
+
+	/* Set measurement window weights. */
+	write(priv, mod->base + HIST_WEIGHT_00TO30_REG,
+	      RPPX1_HIST_WEIGHT(cfg->hist_weight[0], cfg->hist_weight[1],
+				cfg->hist_weight[2], cfg->hist_weight[3]));
+	write(priv, mod->base + HIST_WEIGHT_40TO21_REG,
+	      RPPX1_HIST_WEIGHT(cfg->hist_weight[4], cfg->hist_weight[5],
+				cfg->hist_weight[6], cfg->hist_weight[7]));
+	write(priv, mod->base + HIST_WEIGHT_31TO12_REG,
+	      RPPX1_HIST_WEIGHT(cfg->hist_weight[8], cfg->hist_weight[9],
+				cfg->hist_weight[10], cfg->hist_weight[11]));
+	write(priv, mod->base + HIST_WEIGHT_22TO03_REG,
+	      RPPX1_HIST_WEIGHT(cfg->hist_weight[12], cfg->hist_weight[13],
+				cfg->hist_weight[14], cfg->hist_weight[15]));
+	write(priv, mod->base + HIST_WEIGHT_13TO43_REG,
+	      RPPX1_HIST_WEIGHT(cfg->hist_weight[16], cfg->hist_weight[17],
+				cfg->hist_weight[18], cfg->hist_weight[19]));
+	write(priv, mod->base + HIST_WEIGHT_04TO34_REG,
+	      RPPX1_HIST_WEIGHT(cfg->hist_weight[20], cfg->hist_weight[21],
+				cfg->hist_weight[22], cfg->hist_weight[23]));
+	write(priv, mod->base + HIST_WEIGHT_44_REG,
+	      RPPX1_HIST_WEIGHT(cfg->hist_weight[24], 0, 0, 0));
+
+	/* Translate RkISP1 modes. */
+	mode = HIST_MODE_HIST_MODE_YRGB;
+	switch (cfg->mode) {
+	case RPPX1_HISTOGRAM_MODE_RGB_COMBINED:
+		/* L = R + G + B */
+		coeff[0] = 0x80;
+		coeff[1] = 0x80;
+		coeff[2] = 0x80;
+		break;
+	case RPPX1_HISTOGRAM_MODE_R_HISTOGRAM:
+		/* L = R */
+		coeff[0] = 0x80;
+		coeff[1] = 0x00;
+		coeff[2] = 0x00;
+		break;
+	case RPPX1_HISTOGRAM_MODE_G_HISTOGRAM:
+		/* L = G */
+		coeff[0] = 0x00;
+		coeff[1] = 0x80;
+		coeff[2] = 0x00;
+		break;
+	case RPPX1_HISTOGRAM_MODE_B_HISTOGRAM:
+		coeff[0] = 0x00;
+		coeff[1] = 0x00;
+		coeff[2] = 0x80;
+		break;
+	case RPPX1_HISTOGRAM_MODE_Y_HISTOGRAM:
+		/* Coefficients for a BT.601 (from datasheet). */
+		coeff[0] = 38;
+		coeff[1] = 75;
+		coeff[2] = 15;
+		break;
+	default:
+		mode = HIST_MODE_HIST_MODE_DISABLE;
+		coeff[0] = 0x00;
+		coeff[1] = 0x00;
+		coeff[2] = 0x00;
+		break;
+	}
+
+	write(priv, mod->base + HIST_MODE_REG, mode);
+	write(priv, mod->base + HIST_COEFF_R_REG, coeff[0]);
+	write(priv, mod->base + HIST_COEFF_G_REG, coeff[1]);
+	write(priv, mod->base + HIST_COEFF_B_REG, coeff[2]);
+
+	write(priv, mod->base + HIST_FORCED_UPDATE_REG, 1);
+
+	return 0;
+}
+
+static int rppx1_hist_fill_stats(struct rpp_module *mod,
+				 struct rppx1_stat *stats)
+{
+	for (unsigned int i = 0; i < HIST_BIN_REG_NUM; i++)
+		stats->hist.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,
 };

-- 
2.53.0


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

* [PATCH SQUASH v7 10/18] media: rppx1: hist: Expose channel selection
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (8 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 09/18] media: rppx1: Add support for Histogram Measurement Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 11/18] media: rppx1: Add support for Black Level Subtraction Jai Luthra
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

The module supports selecting the sampling point (channel selector) in
the pipeline for histogram measurements.

Expose it in the uAPI and add support in the driver.

Note: This commit's changes will be squashed into the relevant uAPI and
driver commits. It is separate for now to ease review and highlight the
differences.

Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 drivers/media/platform/dreamchip/rppx1/rppx1_hist.c | 5 +++--
 include/uapi/linux/media/dreamchip/rppx1-config.h   | 2 ++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
index 6287f9c401dfc8ee7f1c19acef1a3e82c5036ac7..76642d2ec0959969e10a92be49c3b0995a0ea5b4 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
@@ -94,8 +94,9 @@ static int rppx1_hist_fill_params(struct rpp_module *mod,
 		return 0;
 	}
 
-	/* Sample after demosaicing. */
-	write(priv, mod->base + HIST_CHANNEL_SEL_REG, 7);
+	/* Select sample point */
+	write(priv, mod->base + HIST_CHANNEL_SEL_REG,
+	      cfg->channel_sel & HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK);
 
 	/*
 	 * The RkISP1 histogram_predivider setting controls the pixel spacing
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index 2adf5f9e083b89c0308a8728f8468f326ab87c48..b247312cfde41510c6d678cb6482a3b22b9036b5 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -593,6 +593,7 @@ enum rppx1_histogram_mode {
  * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS)
  * @mode: histogram mode (from enum rppx1_histogram_mode)
  * @histogram_predivider: process every Nth pixel
+ * @channel_sel: measurement point (see enum rppx1_[pre|post]_meas_chan)
  * @meas_window: measurement window coordinates
  * @hist_weight: weighting factors for sub-windows (5x5 grid)
  */
@@ -600,6 +601,7 @@ struct rppx1_params_hst_config {
 	struct v4l2_isp_params_block_header header;
 	__u32 mode;
 	__u8 histogram_predivider;
+	__u8 channel_sel;
 	struct rppx1_window meas_window;
 	__u8 hist_weight[RPPX1_HISTOGRAM_WEIGHT_GRIDS_SIZE];
 };

-- 
2.53.0


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

* [PATCH v7 11/18] media: rppx1: Add support for Black Level Subtraction
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (9 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH SQUASH v7 10/18] media: rppx1: hist: Expose channel selection Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 12/18] media: rppx1: Add support for Color Correction Matrix Jai Luthra
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the Black Level Subtraction
(BLS) configuration using the parameter buffer format. 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>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |   4 +
 drivers/media/platform/dreamchip/rppx1/rpp_stats.c |   5 +-
 drivers/media/platform/dreamchip/rppx1/rppx1_bls.c | 110 +++++++++++++++++++++
 3 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 788d26f619d1a96d16e9dc499d2763366f70be0a..c218a509098991943a5333f1df061d1aa5ec53d6 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_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
+	RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 	RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
@@ -54,6 +55,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:
+			module = &rpp->pre1.bls;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN:
 			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 6f3be95026f60252d05370421bb2ec6ea090ce6f..aa07de4c6892b4da0511f4854a06621cbfef3afa 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -20,8 +20,11 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
 		if (!rpp_module_call(&rpp->post.hist, fill_stats, &stats->params))
 			stats->meas_type |= RPPX1_STAT_HIST;
 
-	if (isc & RPPX1_IRQ_ID_PRE1_EXM)
+	if (isc & RPPX1_IRQ_ID_PRE1_EXM) {
 		if (!rpp_module_call(&rpp->pre1.exm, fill_stats, &stats->params))
 			stats->meas_type |= RPPX1_STAT_AUTOEXP;
+
+		rpp_module_call(&rpp->pre1.bls, fill_stats, &stats->params);
+	}
 }
 EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
index de7008befd8ea79f7bca974de7714399e8bf443c..1e5153f0ba3600c0c9ead0be4b8c0feb4c5fd9ad 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
 
@@ -54,6 +55,115 @@ 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_params_bls_config *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->enable_auto) {
+		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 native params are 24-bit + 1 signed bit, while the RPP
+		 * can be 12, 20 or 24 bit + 1 signed bit. Figure out how much
+		 * we need to adjust the input parameters.
+		 */
+		const unsigned int shift = 24 - mod->info.bls.colorbits;
+
+		write(priv, mod->base + swapped[0], cfg->fixed_val.r >> shift);
+		write(priv, mod->base + swapped[1], cfg->fixed_val.gr >> shift);
+		write(priv, mod->base + swapped[2], cfg->fixed_val.gb >> shift);
+		write(priv, mod->base + swapped[3], cfg->fixed_val.b >> shift);
+	} else {
+		write(priv, mod->base + BLS_SAMPLES_REG, cfg->bls_samples);
+
+		if (cfg->en_windows & BIT(0)) {
+			write(priv, mod->base + BLS_H1_START_REG, cfg->bls_window1.h_offs);
+			write(priv, mod->base + BLS_H1_STOP_REG, cfg->bls_window1.h_size);
+			write(priv, mod->base + BLS_V1_START_REG, cfg->bls_window1.v_offs);
+			write(priv, mod->base + BLS_V1_STOP_REG, cfg->bls_window1.v_size);
+			ctrl |= BLS_CTRL_BLS_WIN1;
+		}
+
+		if (cfg->en_windows & BIT(1)) {
+			write(priv, mod->base + BLS_H2_START_REG, cfg->bls_window2.h_offs);
+			write(priv, mod->base + BLS_H2_STOP_REG, cfg->bls_window2.h_size);
+			write(priv, mod->base + BLS_V2_START_REG, cfg->bls_window2.v_offs);
+			write(priv, mod->base + BLS_V2_STOP_REG, cfg->bls_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,
+				struct rppx1_stat *stats)
+{
+	struct rppx1_bls_meas_val *bls = &stats->ae.bls_val;
+
+	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);
+
+	/* Return measurements at native hardware precision. */
+	bls->meas_r = rpp_module_read(mod, swapped[0]);
+	bls->meas_gr = rpp_module_read(mod, swapped[1]);
+	bls->meas_gb = rpp_module_read(mod, swapped[2]);
+	bls->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
 };

-- 
2.53.0


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

* [PATCH v7 12/18] media: rppx1: Add support for Color Correction Matrix
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (10 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 11/18] media: rppx1: Add support for Black Level Subtraction Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 13/18] media: rppx1: Add support for Lens Shade Correction Jai Luthra
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the Color Correction Matrix
(CTK) configuration using the parameter buffer format. 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>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  4 ++
 .../media/platform/dreamchip/rppx1/rppx1_ccor.c    | 67 ++++++++++++++++++++++
 2 files changed, 71 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index c218a509098991943a5333f1df061d1aa5ec53d6..3a8848114bbba670071a7b0871955d82eec76b3f 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -18,6 +18,7 @@ static const struct v4l2_isp_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
 	RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
+	RPPX1_PARAMS_BLOCK_INFO(CTK, ctk),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 	RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
 	RPPX1_PARAMS_BLOCK_INFO(AEC_MEAS, aec),
@@ -61,6 +62,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN:
 			module = &rpp->pre1.awbg;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_CTK:
+			module = &rpp->post.ccor;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS:
 			module = &rpp->post.wbmeas;
 			break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
index 4754b0bbce0a13678a91b2e40f001aed98ddabfc..f614f3fbf41608ab917fa32b23c3a8123fa7d442 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
@@ -68,9 +68,76 @@ 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_params_ctk_config *cfg = &block->ctk;
+
+	/* 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.
+	 *
+	 * The native params are 24-bit + sign while the RPP can be 12, 20 or
+	 * 24 bit + sign. Figure out how much we need to adjust the input
+	 * parameters.
+	 */
+	const unsigned int shift = 24 - mod->info.wbmeas.colorbits;
+
+	write(priv, mod->base + CCOR_OFFSET_R_REG, cfg->ct_offset[0] >> shift);
+	write(priv, mod->base + CCOR_OFFSET_G_REG, cfg->ct_offset[1] >> shift);
+	write(priv, mod->base + CCOR_OFFSET_B_REG, cfg->ct_offset[2] >> shift);
+
+	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,

-- 
2.53.0


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

* [PATCH v7 13/18] media: rppx1: Add support for Lens Shade Correction
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (11 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 12/18] media: rppx1: Add support for Color Correction Matrix Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH SQUASH v7 14/18] media: rppx1: lsc: Make full lens grid programmable Jai Luthra
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the Lens Shade Correction (LSC)
configuration using the parameter buffer format. 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.

The parameters buffer only describes one quadrant of the lens, this is
due to the RkISP1 hardware only allowing one quadrant to be programmed
to hardware, which was copied for RPPX1. The hardware then extrapolates
this to the other three quadrants of the lens. For RPP all four
quadrants are individually programmable.

To compensate for the driver need to extrapolate all four quadrants from
the parameters buffer information.

Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |   4 +
 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c | 126 +++++++++++++++++++++
 2 files changed, 130 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 3a8848114bbba670071a7b0871955d82eec76b3f..386c36fe0e19fa52d53691d77405d31d844c5445 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -19,6 +19,7 @@ rppx1_ext_params_blocks_info[] = {
 	RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
 	RPPX1_PARAMS_BLOCK_INFO(CTK, ctk),
+	RPPX1_PARAMS_BLOCK_INFO(LSC, lsc),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 	RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
 	RPPX1_PARAMS_BLOCK_INFO(AEC_MEAS, aec),
@@ -65,6 +66,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_CTK:
 			module = &rpp->post.ccor;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_LSC:
+			module = &rpp->pre1.lsc;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS:
 			module = &rpp->post.wbmeas;
 			break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
index e8acdf74495633790e2614a9985a47fa92df4eeb..4cba2d075bec6390ecc5bffb25eeba443213f52e 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,128 @@ 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_params_lsc_config *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_tbl[i];
+		const __u16 *gr = cfg->gr_data_tbl[i];
+		const __u16 *b = cfg->b_data_tbl[i];
+		const __u16 *gb = cfg->gb_data_tbl[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.
+	 *
+	 * The RPP ISP can describe each quarter of the lens individually, this
+	 * differs from the Rk1ISP which can only describe one quarter of lens
+	 * with software and then extrapolates the other three.
+	 *
+	 * To adjust for this extrapolate the three missing quadrants using
+	 * software for the RPP ISP.
+	 */
+
+	v = cfg->x_grad_tbl;
+	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[7], v[6]));
+	write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+	write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+	write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+	v = cfg->y_grad_tbl;
+	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[7], v[6]));
+	write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+	write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+	write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+	v = cfg->x_size_tbl;
+	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[7], v[6]));
+	write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+	write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+	write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+	v = cfg->y_size_tbl;
+	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[7], v[6]));
+	write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
+	write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
+	write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+
+	/* 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,
 };

-- 
2.53.0


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

* [PATCH SQUASH v7 14/18] media: rppx1: lsc: Make full lens grid programmable
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (12 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 13/18] media: rppx1: Add support for Lens Shade Correction Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 15/18] media: rppx1: Add support for Gamma Correction Jai Luthra
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

RPP-X1 supports programming the full grid of the lens instead of just
one quadrant.

Expose it in the uAPI and add support in the driver.

Note: This commit's changes will be squashed into the relevant uAPI and
driver commits. It is separate for now to ease review and highlight the
differences.

Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c | 39 +++++++++-------------
 include/uapi/linux/media/dreamchip/rppx1-config.h  |  2 +-
 2 files changed, 17 insertions(+), 24 deletions(-)

diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
index 4cba2d075bec6390ecc5bffb25eeba443213f52e..c548d42727cc601f5559567a29597ad8443b7244 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
@@ -133,13 +133,6 @@ rppx1_lsc_fill_params(struct rpp_module *mod,
 
 	/*
 	 * Program X- and Y- sizes, and gradients.
-	 *
-	 * The RPP ISP can describe each quarter of the lens individually, this
-	 * differs from the Rk1ISP which can only describe one quarter of lens
-	 * with software and then extrapolates the other three.
-	 *
-	 * To adjust for this extrapolate the three missing quadrants using
-	 * software for the RPP ISP.
 	 */
 
 	v = cfg->x_grad_tbl;
@@ -147,40 +140,40 @@ rppx1_lsc_fill_params(struct rpp_module *mod,
 	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[7], v[6]));
-	write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
-	write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
-	write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+	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_tbl;
 	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[7], v[6]));
-	write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
-	write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
-	write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+	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_size_tbl;
 	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[7], v[6]));
-	write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
-	write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
-	write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+	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_size_tbl;
 	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[7], v[6]));
-	write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[5], v[4]));
-	write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[3], v[2]));
-	write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[1], v[0]));
+	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);
diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h
index b247312cfde41510c6d678cb6482a3b22b9036b5..e55c1f4818ef8f2ecf533340da65b0e4b667f7e0 100644
--- a/include/uapi/linux/media/dreamchip/rppx1-config.h
+++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
@@ -25,7 +25,7 @@
 #define RPPX1_GAMMA_OUT_MAX_SAMPLES			17
 
 /* Lens Shade Correction */
-#define RPPX1_LSC_SECTORS_TBL_SIZE			8
+#define RPPX1_LSC_SECTORS_TBL_SIZE			16
 #define RPPX1_LSC_SAMPLES_MAX				17
 
 /* Histogram */

-- 
2.53.0


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

* [PATCH v7 15/18] media: rppx1: Add support for Gamma Correction
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (13 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH SQUASH v7 14/18] media: rppx1: lsc: Make full lens grid programmable Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 16/18] media: rppx1: Add support for Bayer Demosaicing Jai Luthra
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the Gamma Correction
configuration using the parameter buffer format. 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>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  4 +++
 drivers/media/platform/dreamchip/rppx1/rppx1_ga.c  | 34 ++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 386c36fe0e19fa52d53691d77405d31d844c5445..9969517eb1902d1002ea3c7b26a6ca15fa974fa6 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -19,6 +19,7 @@ rppx1_ext_params_blocks_info[] = {
 	RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
 	RPPX1_PARAMS_BLOCK_INFO(CTK, ctk),
+	RPPX1_PARAMS_BLOCK_INFO(GOC, goc),
 	RPPX1_PARAMS_BLOCK_INFO(LSC, lsc),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 	RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
@@ -66,6 +67,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_CTK:
 			module = &rpp->post.ccor;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_GOC:
+			module = &rpp->hv.ga;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_LSC:
 			module = &rpp->pre1.lsc;
 			break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
index d6c7f951cf2972a8d633b7915818e26f8d0a1cf5..18b527569c8ecf9ccfd5f92f67e51371a068af8e 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
@@ -43,7 +43,41 @@ 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_params_goc_config *cfg = &block->goc;
+
+	/* 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;
+	}
+
+	write(priv, mod->base + GAMMA_OUT_MODE_REG,
+	      cfg->mode ? GAMMA_OUT_ENABLE_GAMMA_OUT_EN : 0);
+
+	/*
+	 * The native params are 24-bit while the RPP can be 12 or 24 bit.
+	 * Figure out how much we need to adjust the input values.
+	 */
+	const unsigned int shift = 24 - mod->info.ga.colorbits;
+
+	for (unsigned int i = 0; i < RPPX1_GAMMA_OUT_MAX_SAMPLES; i++)
+		write(priv, mod->base + GAMMA_OUT_Y_REG(i),
+		      cfg->gamma_y[i] >> shift);
+
+	/* 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,
 };

-- 
2.53.0


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

* [PATCH v7 16/18] media: rppx1: Add support for Bayer Demosaicing
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (14 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 15/18] media: rppx1: Add support for Gamma Correction Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 17/18] media: rppx1: Add support for Bilateral Denoising Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 18/18] media: rppx1: Add support for Sensor (Gamma) Linearization Jai Luthra
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the Bayer Demosaicing
configuration using the parameter buffer format. 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>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  7 ++
 drivers/media/platform/dreamchip/rppx1/rppx1_db.c  | 82 ++++++++++++++++++++++
 2 files changed, 89 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 9969517eb1902d1002ea3c7b26a6ca15fa974fa6..07ce9d22265a444038bb8bdc91c129a47cb94e0d 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_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
 	RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
+	RPPX1_PARAMS_BLOCK_INFO(FLT, flt),
+	RPPX1_PARAMS_BLOCK_INFO(BDM, bdm),
 	RPPX1_PARAMS_BLOCK_INFO(CTK, ctk),
 	RPPX1_PARAMS_BLOCK_INFO(GOC, goc),
 	RPPX1_PARAMS_BLOCK_INFO(LSC, lsc),
@@ -64,6 +66,11 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN:
 			module = &rpp->pre1.awbg;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_FLT:
+		case RPPX1_PARAMS_BLOCK_TYPE_BDM:
+			/* Both types handled by the same block. */
+			module = &rpp->post.db;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_CTK:
 			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 5e233896cfc8b66e7d90770171b77a2fabc3cd9b..c9f6d2d0dc57eec1adc11bef4fca4e1a52afb0e6 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_flt(struct rpp_module *mod,
+			 const union rppx1_params_block *block,
+			 rppx1_reg_write write, void *priv)
+{
+	const struct rppx1_params_flt_config *cfg = &block->flt;
+
+	/* 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_bdm(struct rpp_module *mod,
+			 const union rppx1_params_block *block,
+			 rppx1_reg_write write, void *priv)
+{
+	const struct rppx1_params_bdm_config *cfg = &block->bdm;
+
+	/* 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(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_FLT:
+		return rppx1_db_fill_params_flt(mod, block, write, priv);
+	case RPPX1_PARAMS_BLOCK_TYPE_BDM:
+		return rppx1_db_fill_params_bdm(mod, block, write, priv);
+	}
+
+	return -EINVAL;
+}
+
 const struct rpp_module_ops rppx1_db_ops = {
 	.probe = rppx1_db_probe,
+	.fill_params = rppx1_db_fill_params,
 };

-- 
2.53.0


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

* [PATCH v7 17/18] media: rppx1: Add support for Bilateral Denoising
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (15 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 16/18] media: rppx1: Add support for Bayer Demosaicing Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  2026-04-10  9:05 ` [PATCH v7 18/18] media: rppx1: Add support for Sensor (Gamma) Linearization Jai Luthra
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

Extend the RPPX1 driver to allow setting the Bilateral Denoising
configuration using the parameter buffer format. 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.

Compared to RkISP, RPP-X1 has dropped the hardware bit AWB_GAIN_COMP.
Luckily it's behavior is easy to emulate in software, so we have kept
the uAPI same.

Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |   7 +
 drivers/media/platform/dreamchip/rppx1/rppx1_bd.c  | 150 +++++++++++++++++++++
 2 files changed, 157 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 07ce9d22265a444038bb8bdc91c129a47cb94e0d..2847647dcf42d7975dee446c8e29c87828db746c 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(BDM, bdm),
 	RPPX1_PARAMS_BLOCK_INFO(CTK, ctk),
 	RPPX1_PARAMS_BLOCK_INFO(GOC, goc),
+	RPPX1_PARAMS_BLOCK_INFO(DPF, dpf),
+	RPPX1_PARAMS_BLOCK_INFO(DPF_STRENGTH, dpf_strength),
 	RPPX1_PARAMS_BLOCK_INFO(LSC, lsc),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 	RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
@@ -77,6 +79,11 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_GOC:
 			module = &rpp->hv.ga;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_DPF:
+		case RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH:
+			/* Both types handled by the same block. */
+			module = &rpp->pre1.bd;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_LSC:
 			module = &rpp->pre1.lsc;
 			break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
index acbfbcd595915fbb36221bc3e6a63cfdc954409e..0d629c3752905687dc67d1cd90ec6d6fd3249e93 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
@@ -47,6 +47,156 @@ static int rppx1_bd_probe(struct rpp_module *mod)
 	return 0;
 }
 
+static int
+rppx1_bd_fill_params_main(struct rpp_module *mod,
+			  const union rppx1_params_block *block,
+			  rppx1_reg_write write, void *priv)
+{
+	const struct rppx1_params_dpf_config *cfg = &block->dpf;
+	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;
+	}
+
+	/*
+	 * 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_DPF_GAIN_USAGE_NF_GAINS:
+		awb_gain_comp = true;
+		isp_dpf_mode = DPF_MODE_USE_NF_GAIN;
+		break;
+	case RPPX1_DPF_GAIN_USAGE_LSC_GAINS:
+		isp_dpf_mode = DPF_MODE_LSC_GAIN_COMP;
+		break;
+	case RPPX1_DPF_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_DPF_GAIN_USAGE_AWB_GAINS:
+		awb_gain_comp = true;
+		isp_dpf_mode = 0;
+		break;
+	case RPPX1_DPF_GAIN_USAGE_AWB_LSC_GAINS:
+		awb_gain_comp = true;
+		isp_dpf_mode = DPF_MODE_LSC_GAIN_COMP;
+		break;
+	case RPPX1_DPF_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_NLL_SCALE_LINEAR)
+		isp_dpf_mode |= DPF_MODE_NLL_SEGMENTATION;
+	if (cfg->rb_flt.fltsize == RPPX1_DPF_RB_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_DPF_MAX_NLF_COEFFS; 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;
+}
+
+static int
+rppx1_bd_fill_params_strength(struct rpp_module *mod,
+			      const union rppx1_params_block *block,
+			      rppx1_reg_write write, void *priv)
+{
+	const struct rppx1_params_dpf_strength_config *cfg = &block->dpfs;
+
+	/* 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->r);
+	write(priv, mod->base + DPF_STRENGTH_G_REG, cfg->g);
+	write(priv, mod->base + DPF_STRENGTH_B_REG, cfg->b);
+
+	return 0;
+}
+
+static int
+rppx1_bd_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_DPF:
+		return rppx1_bd_fill_params_main(mod, block, write, priv);
+	case RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH:
+		return rppx1_bd_fill_params_strength(mod, block, write, priv);
+	}
+
+	return -EINVAL;
+}
+
 const struct rpp_module_ops rppx1_bd_ops = {
 	.probe = rppx1_bd_probe,
+	.fill_params = rppx1_bd_fill_params,
 };

-- 
2.53.0


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

* [PATCH v7 18/18] media: rppx1: Add support for Sensor (Gamma) Linearization
  2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
                   ` (16 preceding siblings ...)
  2026-04-10  9:05 ` [PATCH v7 17/18] media: rppx1: Add support for Bilateral Denoising Jai Luthra
@ 2026-04-10  9:05 ` Jai Luthra
  17 siblings, 0 replies; 21+ messages in thread
From: Jai Luthra @ 2026-04-10  9:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto
  Cc: linux-media, linux-kernel, linux-renesas-soc, Laurent Pinchart,
	Jacopo Mondi, Marek Vasut, Jai Luthra

Extend the RPPX1 driver to allow setting the Sensor (Gamma)
Linearization configuration using the parameter buffer format. 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+renesas@ideasonboard.com>
---
 .../media/platform/dreamchip/rppx1/rpp_params.c    |  4 +++
 drivers/media/platform/dreamchip/rppx1/rppx1_lin.c | 31 ++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 2847647dcf42d7975dee446c8e29c87828db746c..696688856540a88e28ec6ccaa9cd436c64cc9edc 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -17,6 +17,7 @@
 static const struct v4l2_isp_params_block_type_info
 rppx1_ext_params_blocks_info[] = {
 	RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
+	RPPX1_PARAMS_BLOCK_INFO(LIN, lin),
 	RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
 	RPPX1_PARAMS_BLOCK_INFO(FLT, flt),
 	RPPX1_PARAMS_BLOCK_INFO(BDM, bdm),
@@ -65,6 +66,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
 		case RPPX1_PARAMS_BLOCK_TYPE_BLS:
 			module = &rpp->pre1.bls;
 			break;
+		case RPPX1_PARAMS_BLOCK_TYPE_LIN:
+			module = &rpp->pre1.lin;
+			break;
 		case RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN:
 			module = &rpp->pre1.awbg;
 			break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
index e4b0a7be76656c4bc04408500c7ca60709bebf79..24c005ba9b9287504113bbea83f8a076f88df423 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
@@ -54,7 +54,38 @@ 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_params_lin_config *cfg = &block->lin;
+	const unsigned int shift = 24 - mod->info.lin.colorbits;
+
+	if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		write(priv, mod->base + LIN_ENABLE_REG, 0);
+		return 0;
+	}
+
+	write(priv, mod->base + LIN_DX_LO_REG, cfg->xa_pnts.gamma_dx[0]);
+	write(priv, mod->base + LIN_DX_HI_REG, cfg->xa_pnts.gamma_dx[1]);
+
+	for (unsigned int i = 0; i < LIN_SAMPLES_NUM; i++) {
+		write(priv, mod->base + LIN_R_Y_REG(i),
+		      cfg->curve_r.gamma_y[i] >> shift);
+		write(priv, mod->base + LIN_G_Y_REG(i),
+		      cfg->curve_g.gamma_y[i] >> shift);
+		write(priv, mod->base + LIN_B_Y_REG(i),
+		      cfg->curve_b.gamma_y[i] >> shift);
+	}
+
+	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,
 };

-- 
2.53.0


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

* Re: [PATCH v7 01/18] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats
  2026-04-10  9:05 ` [PATCH v7 01/18] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Jai Luthra
@ 2026-04-10 11:15   ` Jacopo Mondi
  0 siblings, 0 replies; 21+ messages in thread
From: Jacopo Mondi @ 2026-04-10 11:15 UTC (permalink / raw)
  To: Jai Luthra
  Cc: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto, linux-media, linux-kernel,
	linux-renesas-soc, Laurent Pinchart, Jacopo Mondi, Marek Vasut

Hi Jai

On Fri, Apr 10, 2026 at 02:35:36PM +0530, Jai Luthra wrote:
> 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>
> ---
>  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 a2b650f4ec3c32a4883521f34fb51eed13c71d76..cd3f4a86e27f22a0108ad2932cba755295af9a98 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 eda4492e40dc14a90a230601d8e23b0e13845d34..1f78b5378b3bde31a5ec464a6a609fac94e6d0d6 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 */
> +

nice.

However you'll also need a bit of documentation.
See Documentation/admin-guide/media/mali-c55.rst and
Documentation/userspace-api/media/v4l/metafmt-arm-mali-c55.rst for
reference and the commits that introduced them.

They can come in a separate patch which should be part of this series.

For this one:
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Thanks
  j

>  #ifdef __KERNEL__
>  /*
>   * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when
>
> --
> 2.53.0
>

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

* Re: [PATCH v7 02/18] media: uapi: Add extensible param and stats blocks for RPPX1
  2026-04-10  9:05 ` [PATCH v7 02/18] media: uapi: Add extensible param and stats blocks for RPPX1 Jai Luthra
@ 2026-04-10 16:10   ` Jacopo Mondi
  0 siblings, 0 replies; 21+ messages in thread
From: Jacopo Mondi @ 2026-04-10 16:10 UTC (permalink / raw)
  To: Jai Luthra
  Cc: Mauro Carvalho Chehab, Niklas Söderlund, Geert Uytterhoeven,
	Magnus Damm, Kuninori Morimoto, linux-media, linux-kernel,
	linux-renesas-soc, Laurent Pinchart, Jacopo Mondi, Marek Vasut

Hi Jai

On Fri, Apr 10, 2026 at 02:35:37PM +0530, Jai Luthra wrote:
> Define the userspace API for the Dreamchip RPP-X1 ISP extensible
> parameters and statistics. The RPP-X1 is functionally similar to the
> RkISP1 already supported upstream, but operates at higher bit depths (up
> to 24-bit precision in many blocks) and exposes additional configuration
> options. This warrants a dedicated uAPI rather than reusing the RkISP1
> definitions.
>
> The parameter blocks follow the V4L2 extensible parameters framework
> using struct v4l2_isp_params_block_header, with each ISP functional
> block represented as a tagged configuration structure. The statistics
> buffer provides AWB, auto-exposure and histogram measurement results at
> native RPP-X1 precision.
>
> Not all functional blocks present on the RPP-X1 hardware are included
> yet, but the format is extensible and new blocks can be added without
> breaking existing userspace.
>
> Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> ---
>  include/uapi/linux/media/dreamchip/rppx1-config.h | 728 ++++++++++++++++++++++
>  1 file changed, 728 insertions(+)
>
> 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 0000000000000000000000000000000000000000..b9083e6f32b15329333eb13491b50c0aea8d1a32
> --- /dev/null
> +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h
> @@ -0,0 +1,728 @@
> +/* 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>
> +
> +/*
> + * Defect Pixel Cluster Correction
> + */
> +#define RPPX1_DPCC_METHODS_MAX				3
> +
> +/* Linearization (Sensor De-gamma) */
> +#define RPPX1_LIN_SAMPLES_NUM				17
> +
> +/* Gamma Out */
> +#define RPPX1_GAMMA_OUT_MAX_SAMPLES			17
> +
> +/* Lens Shade Correction */
> +#define RPPX1_LSC_SECTORS_TBL_SIZE			8
> +#define RPPX1_LSC_SAMPLES_MAX				17
> +
> +/* Histogram */
> +#define RPPX1_HIST_BIN_N_MAX				32
> +
> +/* Exposure Measurement */
> +#define RPPX1_EXM_MEAN_MAX				25
> +
> +/* AWB Measurement */
> +#define RPPX1_AWB_MAX_GRID				1
> +
> +/* Color Correction Matrix */
> +#define RPPX1_CTK_COEFF_MAX				0x8000
> +#define RPPX1_CTK_OFFSET_MAX				0x800000
> +
> +/* Filter */
> +#define RPPX1_BDM_MAX_TH				0xffff
> +
> +/**
> + * enum rppx1_params_block_type - RPP-X1 extensible params block types
> + *
> + * @RPPX1_PARAMS_BLOCK_TYPE_BLS: Black Level Subtraction
> + * @RPPX1_PARAMS_BLOCK_TYPE_DPCC: Defect Pixel Cluster Correction
> + * @RPPX1_PARAMS_BLOCK_TYPE_LIN: Linearization (Sensor De-gamma)
> + * @RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN: Auto White Balance Gains
> + * @RPPX1_PARAMS_BLOCK_TYPE_FLT: ISP Filtering
> + * @RPPX1_PARAMS_BLOCK_TYPE_BDM: Bayer Demosaic
> + * @RPPX1_PARAMS_BLOCK_TYPE_CTK: Color Correction (Cross-Talk)
> + * @RPPX1_PARAMS_BLOCK_TYPE_GOC: Gamma Out Correction
> + * @RPPX1_PARAMS_BLOCK_TYPE_DPF: De-noise Pre-Filter
> + * @RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH: De-noise Pre-Filter Strength
> + * @RPPX1_PARAMS_BLOCK_TYPE_LSC: Lens Shading Correction
> + * @RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS: AWB Measurement Configuration
> + * @RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS: Histogram Measurement Configuration
> + * @RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS: Auto Exposure Measurement Configuration
> + */
> +enum rppx1_params_block_type {
> +	RPPX1_PARAMS_BLOCK_TYPE_BLS,
> +	RPPX1_PARAMS_BLOCK_TYPE_DPCC,
> +	RPPX1_PARAMS_BLOCK_TYPE_LIN,
> +	RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN,
> +	RPPX1_PARAMS_BLOCK_TYPE_FLT,
> +	RPPX1_PARAMS_BLOCK_TYPE_BDM,
> +	RPPX1_PARAMS_BLOCK_TYPE_CTK,
> +	RPPX1_PARAMS_BLOCK_TYPE_GOC,
> +	RPPX1_PARAMS_BLOCK_TYPE_DPF,
> +	RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH,
> +	RPPX1_PARAMS_BLOCK_TYPE_LSC,
> +	RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS,
> +	RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS,
> +	RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS,
> +};

Let me start with a review of some of the blocks, we'll go through
them one by one.

> +
> +/**
> + * struct rppx1_window - Measurement window
> + *
> + * @h_offs: horizontal offset from the left of the frame in pixels

I think it's relevant saying these are 14 bits values

> + * @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;
> +};
> +
> +/**
> + * struct rppx1_bls_fixed_val - BLS fixed subtraction values
> + *
> + * Fixed black level values subtracted from sensor data per Bayer channel.
> + * Negative values result in addition. Each value is a 24-bit + sign
> + * (25-bit signed) fixed-point number stored in a __s32.

I think these should be described as

"Each value is is stored as a signed 2's complement representation
ranging from -2^24 to 2^24-1."

As the fixed point representation in 2's complement allows to
represent a negative number with an integer I think the type of the
fields should be __u32.

> + *
> + * RPP-X1 supports 12/20/24-bit + sign depending on hardware version.

The ISP reports this through the "bls_version" register field.

I would introduce an enumeration for this and reference it here.
More on this below.

> + * Userspace should provide values at full 24-bit precision; the driver
> + * truncates to match the hardware.

If you're looking at "bls_version" I didn't find where it is said that
it impacts the fixed values, I only read it impacts the measured
values. Have I missed that ?

> + *
> + * @r: subtraction value for Bayer pattern R
> + * @gr: subtraction value for Bayer pattern Gr
> + * @gb: subtraction value for Bayer pattern Gb
> + * @b: subtraction value for Bayer pattern B

The manual describes the values as "A", "B", "C" and "D".

These values are matched with the Bayer components according to the
cropping configuration on the input port. The pipeline should
carefully crop to the macro-pixel boundary so that A B C and D
correspond to the sensor's native Bayer ordering.

Also, I wouldn't say "subtraction" but simply "Fixed black level for
channel ..."

> + */
> +struct rppx1_bls_fixed_val {
> +	__s32 r;
> +	__s32 gr;
> +	__s32 gb;
> +	__s32 b;
> +};
> +
> +/**
> + * struct rppx1_params_bls_config - Black Level Subtraction configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BLS)
> + * @enable_auto: 1 = use measured values, 0 = use fixed_val

I would call this "bls_mode" and create and enum for it

enum rppx1_bls_mode {
        RPPX1_BLS_MODE_FIXED,
        RPPX1_BLS_MODE_MEAS,
};

> + * @en_windows: enabled measurement windows bitmask

For this one as well

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,
};

> + * @bls_window1: measurement window 1
> + * @bls_window2: measurement window 2
> + * @bls_samples: log2 of the number of measured pixels per Bayer position
> + * @fixed_val: fixed subtraction values (24-bit + sign)

Let's defer the field length description to the documentation of
rppx1_bls_fixed_val.

Also, I would document these as "fixed black level values"

> + */
> +struct rppx1_params_bls_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u8 enable_auto;
> +	__u8 en_windows;

I was about to complaint that you're missing the enable bit but we can
use the block header maybe ?

> +	struct rppx1_window bls_window1;
> +	struct rppx1_window bls_window2;
> +	__u8 bls_samples;
> +	struct rppx1_bls_fixed_val fixed_val;
> +};
> +
> +/**
> + * struct rppx1_dpcc_methods_config - DPCC methods set configuration
> + *
> + * This structure stores the configuration of one set of methods for the DPCC
> + * algorithm. Multiple methods can be selected in each set (independently for
> + * the Green and Red/Blue components) through the @method field, the result is
> + * the logical AND of all enabled methods. The remaining fields set thresholds
> + * and factors for each method.
> + *
> + * @method: method enable bits (RPPX1_DPCC_METHODS_SET_*)
> + * @line_thresh: line threshold (RPPX1_DPCC_LINE_THRESH_*)
> + * @line_mad_fac: line MAD factor (RPPX1_DPCC_LINE_MAD_FAC_*)
> + * @pg_fac: peak gradient factor (RPPX1_DPCC_PG_FAC_*)
> + * @rnd_thresh: rank neighbor difference threshold (RPPX1_DPCC_RND_THRESH_*)
> + * @rg_fac: rank gradient factor (RPPX1_DPCC_RG_FAC_*)
> + */
> +struct rppx1_dpcc_methods_config {
> +	__u32 method;

Shoulf we define each bit ?

#define RPPX1_DPCC_METHODS_RG_RB_EN             (1 << 12)
#define RPPX1_DPCC_METHODS_RND_RB_EN            (1 << 11)
#define RPPX1_DPCC_METHODS_RO_RB_EN             (1 << 10)
#define RPPX1_DPCC_METHODS_LC_RB_EN             (1 << 9)
#define RPPX1_DPCC_METHODS_PG_RB_EN             (1 << 8)
#define RPPX1_DPCC_METHODS_RG_G_EN              (1 << 4)
#define RPPX1_DPCC_METHODS_RND_G_EN             (1 << 3)
#define RPPX1_DPCC_METHODS_RO_G_EN              (1 << 2)
#define RPPX1_DPCC_METHODS_LC_G_EN              (1 << 1)
#define RPPX1_DPCC_METHODS_PG_G_EN              (1 << 0)

> +	__u32 line_thresh;
> +	__u32 line_mad_fac;

For these and other fields you can define bitshits like the RkISP1
uAPI header does

> +	__u32 pg_fac;
> +	__u32 rnd_thresh;
> +	__u32 rg_fac;
> +};
> +
> +/**
> + * struct rppx1_params_dpcc_config - Defect Pixel Cluster Correction configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DPCC)
> + * @mode: DPCC mode (RPPX1_DPCC_MODE_*)
> + * @output_mode: interpolation output mode (RPPX1_DPCC_OUTPUT_MODE_*)
> + * @set_use: methods sets selection (RPPX1_DPCC_SET_USE_*)
> + * @methods: methods sets configuration
> + * @ro_limits: rank order limits (RPPX1_DPCC_RO_LIMITS_*)
> + * @rnd_offs: differential rank offsets (RPPX1_DPCC_RND_OFFS_*)
> + */
> +struct rppx1_params_dpcc_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u32 mode;

Here as well it might be useful to define

#define RPPX1_DPCC_MODE_STAGE1_EN               (1 << 2)
#define RPPX1_DPCC_MODE_GRAYSCALE               (1 << 1)
#define RPPX1_DPPC_MODE_ENABLE                  (1 << 0)

> +	__u32 output_mode;
> +	__u32 set_use;

The same applies to these two registers

> +	struct rppx1_dpcc_methods_config methods[RPPX1_DPCC_METHODS_MAX];
> +	__u32 ro_limits;
> +	__u32 rnd_offs;
> +};

DPPC reports a version number that allows to identify the bit width.
Should it be communicated to userspace through statistics ?

> +
> +/**
> + * struct rppx1_lin_curve - Linearization curve for one color channel
> + *
> + * The RPP-X1 linearization module supports 12/20/24-bit precision depending
> + * on hardware version. Values are provided at 24-bit precision; the driver

The hardware version should be reported through stats with proper
defines

Did you get what the difference is between:
0x006: 24 bit version
0x009: 24 bit, 4 bit dxi

It seems to me dxi samples are 4 bits, aren't they ?

> + *
> + * @gamma_y: curve y-axis values, each up to 24 bits
> + */
> +struct rppx1_lin_curve {
> +	__u32 gamma_y[RPPX1_LIN_SAMPLES_NUM];
> +};
> +
> +/**
> + * struct rppx1_lin_curve_dx - Linearization curve x-axis (sampling points)
> + * increments.
> + *
> + * gamma_dx[0] is for the lower samples, so Bits 0:3 for sample 1, ... Bits
> + * 28:31 for sample 8
> + * gamma_dx[1] is for the higher samples, so Bits 0:3 for sample 9, ... Bits
> + * 28:31 for sample 16
> + *
> + * The reset values for both fields is 0xcccccccc. This means that each sample
> + * is 12 units away from the previous one on the x-axis.
> + *
> + * @gamma_dx: curve x-axis increments in 4-bit precision
> + */
> +struct rppx1_lin_curve_dx {
> +	__u32 gamma_dx[2];
> +};
> +
> +/**
> + * struct rppx1_params_lin_config - Linearization (Sensor De-gamma) configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LIN)
> + * @curve_r: linearization curve for red channel
> + * @curve_g: linearization curve for green channel
> + * @curve_b: linearization curve for blue channel
> + * @xa_pnts: x axis increment definitions
> + */
> +struct rppx1_params_lin_config {
> +	struct v4l2_isp_params_block_header header;
> +	struct rppx1_lin_curve curve_r;
> +	struct rppx1_lin_curve curve_g;
> +	struct rppx1_lin_curve curve_b;
> +	struct rppx1_lin_curve_dx xa_pnts;
> +};

Ack, I'll stop here for the time being :)

> +
> +/**
> + * struct rppx1_params_lsc_config - Lens Shading Correction configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LSC)
> + * @r_data_tbl: sample table red
> + * @gr_data_tbl: sample table green (red)
> + * @gb_data_tbl: sample table green (blue)
> + * @b_data_tbl: sample table blue
> + * @x_grad_tbl: gradient table x
> + * @y_grad_tbl: gradient table y
> + * @x_size_tbl: size table x
> + * @y_size_tbl: size table y
> + * @config_width: reserved
> + * @config_height: reserved
> + */
> +struct rppx1_params_lsc_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u16 r_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
> +	__u16 gr_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
> +	__u16 gb_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
> +	__u16 b_data_tbl[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX];
> +	__u16 x_grad_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
> +	__u16 y_grad_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
> +	__u16 x_size_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
> +	__u16 y_size_tbl[RPPX1_LSC_SECTORS_TBL_SIZE];
> +	__u16 config_width;
> +	__u16 config_height;
> +};
> +
> +/**
> + * struct rppx1_params_awb_gain_config  - AWB gain configuration
> + *
> + * RPP-X1 AWB gains are 18-bit with 12-bit fractional part (0x1000 = 1.0),
> + * giving a range of 0.0 to 64.0.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN)
> + * @gain_red: gain for red component, 18-bit (Q6.12)
> + * @gain_green_r: gain for green-in-red component, 18-bit (Q6.12)
> + * @gain_blue: gain for blue component, 18-bit (Q6.12)
> + * @gain_green_b: gain for green-in-blue component, 18-bit (Q6.12)
> + */
> +struct rppx1_params_awb_gain_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u32 gain_red;
> +	__u32 gain_green_r;
> +	__u32 gain_blue;
> +	__u32 gain_green_b;
> +};
> +
> +/**
> + * struct rppx1_params_flt_config - Filter (demosaic/denoise) configuration
> + *
> + * RPP-X1 thresholds are 18-bit and factors are 8-bit.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_FLT)
> + * @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_params_flt_config {
> +	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;
> +};
> +
> +/**
> + * struct rppx1_params_bdm_config - Bayer Demosaic configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BDM)
> + * @demosaic_th: threshold for texture detection, 16-bit
> + */
> +struct rppx1_params_bdm_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u16 demosaic_th;
> +};
> +
> +/**
> + * struct rppx1_params_ctk_config - Color Correction (Cross-Talk) configuration
> + *
> + * RPP-X1 coefficients are 16-bit signed fixed-point (Q4.12).
> + * Range: -8.0 (0x8000) to +7.9996 (0x7FFF), 1.0 = 0x1000.
> + *
> + * RPP-X1 offsets are up to 24-bit + sign depending on hardware version.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_CTK)
> + * @coeff: 3x3 color correction matrix, Q4.12 signed
> + * @ct_offset: R, G, B offsets, up to 25-bit signed
> + */
> +struct rppx1_params_ctk_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u16 coeff[3][3];
> +	__u32 ct_offset[3];
> +};
> +
> +/**
> + * struct rppx1_params_goc_config - Gamma Out Correction configuration
> + *
> + * RPP-X1 gamma output values are up to 24-bit depending on hardware version.
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_GOC)
> + * @mode: gamma curve mode (0 = logarithmic, 1 = equidistant)
> + * @gamma_y: gamma out curve y-axis values, up to 24-bit
> + */
> +struct rppx1_params_goc_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u32 mode;
> +	__u32 gamma_y[RPPX1_GAMMA_OUT_MAX_SAMPLES];
> +};
> +
> +/**
> + * enum rppx1_dpf_gain_usage - DPF noise function gain usage mode
> + * @RPPX1_DPF_GAIN_USAGE_DISABLED: gain not used
> + * @RPPX1_DPF_GAIN_USAGE_NF_GAINS: use noise function gains
> + * @RPPX1_DPF_GAIN_USAGE_LSC_GAINS: use LSC gains
> + * @RPPX1_DPF_GAIN_USAGE_NF_LSC_GAINS: use noise function and LSC gains
> + * @RPPX1_DPF_GAIN_USAGE_AWB_GAINS: use AWB gains
> + * @RPPX1_DPF_GAIN_USAGE_AWB_LSC_GAINS: use AWB and LSC gains
> + */
> +enum rppx1_dpf_gain_usage {
> +	RPPX1_DPF_GAIN_USAGE_DISABLED,
> +	RPPX1_DPF_GAIN_USAGE_NF_GAINS,
> +	RPPX1_DPF_GAIN_USAGE_LSC_GAINS,
> +	RPPX1_DPF_GAIN_USAGE_NF_LSC_GAINS,
> +	RPPX1_DPF_GAIN_USAGE_AWB_GAINS,
> +	RPPX1_DPF_GAIN_USAGE_AWB_LSC_GAINS,
> +};
> +
> +/**
> + * enum rppx1_nll_scale_mode - DPF noise level lookup scale mode
> + * @RPPX1_NLL_SCALE_LINEAR: linear scaling
> + * @RPPX1_NLL_SCALE_LOGARITHMIC: logarithmic scaling
> + */
> +enum rppx1_nll_scale_mode {
> +	RPPX1_NLL_SCALE_LINEAR,
> +	RPPX1_NLL_SCALE_LOGARITHMIC,
> +};
> +
> +/**
> + * enum rppx1_dpf_rb_filtersize - DPF red/blue filter kernel size
> + * @RPPX1_DPF_RB_FILTERSIZE_13x9: 13x9 filter size
> + * @RPPX1_DPF_RB_FILTERSIZE_9x9: 9x9 filter size
> + */
> +enum rppx1_dpf_rb_filtersize {
> +	RPPX1_DPF_RB_FILTERSIZE_13x9,
> +	RPPX1_DPF_RB_FILTERSIZE_9x9,
> +};
> +
> +/**
> + * struct rppx1_dpf_gain - DPF noise function gain configuration
> + *
> + * @mode: gain usage 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_dpf_gain {
> +	__u32 mode;
> +	__u16 nf_r_gain;
> +	__u16 nf_b_gain;
> +	__u16 nf_gr_gain;
> +	__u16 nf_gb_gain;
> +};
> +
> +#define RPPX1_DPF_MAX_NLF_COEFFS			17
> +#define RPPX1_DPF_MAX_SPATIAL_COEFFS			6
> +
> +/**
> + * struct rppx1_dpf_nll - DPF noise level lookup
> + *
> + * @coeff: noise level lookup coefficients
> + * @scale_mode: 0 = linear, 1 = logarithmic
> + */
> +struct rppx1_dpf_nll {
> +	__u16 coeff[RPPX1_DPF_MAX_NLF_COEFFS];
> +	__u32 scale_mode;
> +};
> +
> +/**
> + * struct rppx1_dpf_rb_flt - DPF red/blue filter configuration
> + *
> + * @fltsize: filter kernel size (0 = 13x9, 1 = 9x9)
> + * @spatial_coeff: spatial weight coefficients
> + * @r_enable: enable filter for red pixels
> + * @b_enable: enable filter for blue pixels
> + */
> +struct rppx1_dpf_rb_flt {
> +	__u32 fltsize;
> +	__u8 spatial_coeff[RPPX1_DPF_MAX_SPATIAL_COEFFS];
> +	__u8 r_enable;
> +	__u8 b_enable;
> +};
> +
> +/**
> + * struct rppx1_dpf_g_flt - DPF 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_dpf_g_flt {
> +	__u8 spatial_coeff[RPPX1_DPF_MAX_SPATIAL_COEFFS];
> +	__u8 gr_enable;
> +	__u8 gb_enable;
> +};
> +
> +/**
> + * struct rppx1_params_dpf_config - De-noising Pre-Filter configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DPF)
> + * @gain: noise function gain
> + * @g_flt: green filter configuration
> + * @rb_flt: red/blue filter configuration
> + * @nll: noise level lookup
> + */
> +struct rppx1_params_dpf_config {
> +	struct v4l2_isp_params_block_header header;
> +	struct rppx1_dpf_gain gain;
> +	struct rppx1_dpf_g_flt g_flt;
> +	struct rppx1_dpf_rb_flt rb_flt;
> +	struct rppx1_dpf_nll nll;
> +};
> +
> +/**
> + * struct rppx1_params_dpf_strength_config - DPF strength configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH)
> + * @r: filter strength for RED
> + * @g: filter strength for GREEN
> + * @b: filter strength for BLUE
> + */
> +struct rppx1_params_dpf_strength_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u8 r;
> +	__u8 g;
> +	__u8 b;
> +};
> +
> +/**
> + * enum rppx1_awb_mode_type - AWB measurement mode
> + * @RPPX1_AWB_MODE_MANUAL: manual white balance
> + * @RPPX1_AWB_MODE_RGB: RGB measurement mode
> + * @RPPX1_AWB_MODE_YCBCR: YCbCr measurement mode
> + */
> +enum rppx1_awb_mode_type {
> +	RPPX1_AWB_MODE_MANUAL,
> +	RPPX1_AWB_MODE_RGB,
> +	RPPX1_AWB_MODE_YCBCR,
> +};
> +
> +/**
> + * struct rppx1_params_awb_meas_config - AWB measurement configuration
> + *
> + * RPP-X1 min_y, max_y, min_c, max_csum, awb_ref_cr, awb_ref_cb are up to
> + * 24-bit depending on hardware version (8/20/24-bit).
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AWB_MEAS)
> + * @awb_wnd: measurement window
> + * @awb_mode: measurement mode (from enum rppx1_awb_mode_type)
> + * @max_y: upper pixel value limit, up to 24-bit
> + * @min_y: lower pixel value limit, up to 24-bit
> + * @max_csum: chrominance sum maximum, up to 24-bit
> + * @min_c: chrominance minimum, up to 24-bit
> + * @frames: number of frames for mean value calculation (0 = 1 frame)
> + * @awb_ref_cr: reference Cr for AWB regulation, up to 24-bit
> + * @awb_ref_cb: reference Cb for AWB regulation, up to 24-bit
> + * @enable_ymax_cmp: enable Y_MAX compare
> + */
> +struct rppx1_params_awb_meas_config {
> +	struct v4l2_isp_params_block_header header;
> +	struct rppx1_window awb_wnd;
> +	__u32 awb_mode;
> +	__u32 max_y;
> +	__u32 min_y;
> +	__u32 max_csum;
> +	__u32 min_c;
> +	__u8 frames;
> +	__u32 awb_ref_cr;
> +	__u32 awb_ref_cb;
> +	__u8 enable_ymax_cmp;
> +};
> +
> +/**
> + * enum rppx1_histogram_mode - Histogram measurement mode
> + * @RPPX1_HISTOGRAM_MODE_DISABLE: histogram disabled
> + * @RPPX1_HISTOGRAM_MODE_RGB_COMBINED: combined RGB histogram
> + * @RPPX1_HISTOGRAM_MODE_R_HISTOGRAM: red channel histogram
> + * @RPPX1_HISTOGRAM_MODE_G_HISTOGRAM: green channel histogram
> + * @RPPX1_HISTOGRAM_MODE_B_HISTOGRAM: blue channel histogram
> + * @RPPX1_HISTOGRAM_MODE_Y_HISTOGRAM: luminance histogram
> + */
> +enum rppx1_histogram_mode {
> +	RPPX1_HISTOGRAM_MODE_DISABLE,
> +	RPPX1_HISTOGRAM_MODE_RGB_COMBINED,
> +	RPPX1_HISTOGRAM_MODE_R_HISTOGRAM,
> +	RPPX1_HISTOGRAM_MODE_G_HISTOGRAM,
> +	RPPX1_HISTOGRAM_MODE_B_HISTOGRAM,
> +	RPPX1_HISTOGRAM_MODE_Y_HISTOGRAM,
> +};
> +
> +#define RPPX1_HISTOGRAM_WEIGHT_GRIDS_SIZE		25
> +
> +/**
> + * struct rppx1_params_hst_config - Histogram measurement configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_HST_MEAS)
> + * @mode: histogram mode (from enum rppx1_histogram_mode)
> + * @histogram_predivider: process every Nth pixel
> + * @meas_window: measurement window coordinates
> + * @hist_weight: weighting factors for sub-windows (5x5 grid)
> + */
> +struct rppx1_params_hst_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u32 mode;
> +	__u8 histogram_predivider;
> +	struct rppx1_window meas_window;
> +	__u8 hist_weight[RPPX1_HISTOGRAM_WEIGHT_GRIDS_SIZE];
> +};
> +
> +/**
> + * enum rppx1_exp_meas_mode - Exposure measurement mode
> + * @RPPX1_EXP_MEASURING_MODE_0: Y = 16 + 0.25R + 0.5G + 0.1094B
> + * @RPPX1_EXP_MEASURING_MODE_1: Y = (R + G + B) x (85/256)
> + */
> +enum rppx1_exp_meas_mode {
> +	RPPX1_EXP_MEASURING_MODE_0,
> +	RPPX1_EXP_MEASURING_MODE_1,
> +};
> +
> +/**
> + * struct rppx1_params_aec_config - Auto Exposure measurement configuration
> + *
> + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AEC_MEAS)
> + * @mode: exposure measure mode (from enum rppx1_exp_meas_mode)
> + * @autostop: 0 = continuous, 1 = stop after one frame
> + * @meas_window: measurement window coordinates
> + */
> +struct rppx1_params_aec_config {
> +	struct v4l2_isp_params_block_header header;
> +	__u32 mode;
> +	__u32 autostop;
> +	struct rppx1_window meas_window;
> +};
> +
> +/**
> + * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks
> + */
> +#define RPPX1_PARAMS_MAX_SIZE						\
> +	(sizeof(struct rppx1_params_bls_config)			+	\
> +	sizeof(struct rppx1_params_dpcc_config)			+	\
> +	sizeof(struct rppx1_params_lin_config)			+	\
> +	sizeof(struct rppx1_params_awb_gain_config)		+	\
> +	sizeof(struct rppx1_params_flt_config)			+	\
> +	sizeof(struct rppx1_params_bdm_config)			+	\
> +	sizeof(struct rppx1_params_ctk_config)			+	\
> +	sizeof(struct rppx1_params_goc_config)			+	\
> +	sizeof(struct rppx1_params_dpf_config)			+	\
> +	sizeof(struct rppx1_params_dpf_strength_config)		+	\
> +	sizeof(struct rppx1_params_lsc_config)			+	\
> +	sizeof(struct rppx1_params_awb_meas_config)		+	\
> +	sizeof(struct rppx1_params_hst_config)			+	\
> +	sizeof(struct rppx1_params_aec_config))
> +
> +/* ---------------------------------------------------------------------------
> + * Statistics Structures
> + *
> + * Native RPP-X1 precision. Fields use __u32 where the hardware provides
> + * wider-than-8-bit results.
> + */
> +
> +/**
> + * struct rppx1_awb_meas - AWB measured values
> + *
> + * @cnt: white pixel count
> + * @mean_y_or_g: mean Y (or G in RGB mode), up to 24-bit
> + * @mean_cb_or_b: mean Cb (or B in RGB mode), up to 24-bit
> + * @mean_cr_or_r: mean Cr (or R in RGB mode), up to 24-bit
> + */
> +struct rppx1_awb_meas {
> +	__u32 cnt;
> +	__u32 mean_y_or_g;
> +	__u32 mean_cb_or_b;
> +	__u32 mean_cr_or_r;
> +};
> +
> +/**
> + * struct rppx1_awb_stat - AWB statistics
> + *
> + * @awb_mean: measured AWB data
> + */
> +struct rppx1_awb_stat {
> +	struct rppx1_awb_meas awb_mean[RPPX1_AWB_MAX_GRID];
> +};
> +
> +/**
> + * struct rppx1_bls_meas_val - BLS measured values
> + *
> + * RPP-X1 BLS statistics can be 8/20/24-bit depending on version.
> + *
> + * @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_meas_val {
> +	__u32 meas_r;
> +	__u32 meas_gr;
> +	__u32 meas_gb;
> +	__u32 meas_b;
> +};

You can also report the value of "bls_version" which tells you
the measured black levels number of significant bits.

One thing which might be worth testing is if the measured values are
reported unconditionally or only if RPPX1_BLS_MODE_MEAS is selected.

In this case it should be specified these values are only valid in
case RPPX1_BLS_MODE_MEAS is in use.

> +
> +/**
> + * struct rppx1_ae_stat - Auto Exposure statistics
> + *
> + * RPP-X1 exposure mean values are up to 20-bit depending on version.
> + * The image is divided into a 5x5 grid (25 blocks).
> + *
> + * @exp_mean: mean luminance values per block, up to 20-bit
> + * @bls_val: BLS measured values
> + */
> +struct rppx1_ae_stat {
> +	__u32 exp_mean[RPPX1_EXM_MEAN_MAX];
> +	struct rppx1_bls_meas_val bls_val;

Curious to know why you report this together with AE stats

> +};
> +
> +/**
> + * struct rppx1_hist_stat - Histogram statistics
> + *
> + * @hist_bins: 32 histogram bin counters, each 20-bit unsigned fixed point
> + *	       (bits 0-4 fractional, bits 5-19 integer)
> + */
> +struct rppx1_hist_stat {
> +	__u32 hist_bins[RPPX1_HIST_BIN_N_MAX];
> +};
> +
> +/**
> + * struct rppx1_stat - RPP-X1 3A statistics
> + *
> + * @awb: auto white balance statistics
> + * @ae: auto exposure statistics
> + * @hist: histogram statistics
> + */
> +struct rppx1_stat {
> +	struct rppx1_awb_stat awb;
> +	struct rppx1_ae_stat ae;
> +	struct rppx1_hist_stat hist;
> +};
> +
> +/**
> + * RPPX1_STAT_AWB - AWB measurement data available
> + * RPPX1_STAT_AUTOEXP - Auto exposure measurement data available
> + * RPPX1_STAT_HIST - Histogram measurement data available
> + */
> +#define RPPX1_STAT_AWB			(1U << 0)
> +#define RPPX1_STAT_AUTOEXP		(1U << 1)
> +#define RPPX1_STAT_HIST			(1U << 2)
> +
> +/**
> + * struct rppx1_stat_buffer - RPP-X1 statistics metadata buffer
> + *
> + * @meas_type: bitmask of available measurements (RPPX1_STAT_*)
> + * @frame_id: frame identifier for synchronization
> + * @params: statistics data
> + */
> +struct rppx1_stat_buffer {
> +	__u32 meas_type;
> +	__u32 frame_id;
> +	struct rppx1_stat params;
> +};
> +
> +#endif /* __UAPI_RPP_X1_CONFIG_H */
>
> --
> 2.53.0
>

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

end of thread, other threads:[~2026-04-10 16:10 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-10  9:05 [PATCH v7 00/18] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Jai Luthra
2026-04-10  9:05 ` [PATCH v7 01/18] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Jai Luthra
2026-04-10 11:15   ` Jacopo Mondi
2026-04-10  9:05 ` [PATCH v7 02/18] media: uapi: Add extensible param and stats blocks for RPPX1 Jai Luthra
2026-04-10 16:10   ` Jacopo Mondi
2026-04-10  9:05 ` [PATCH v7 03/18] media: rppx1: Add framework to support Dreamchip RPPX1 ISP Jai Luthra
2026-04-10  9:05 ` [PATCH v7 04/18] media: rcar-isp: Add support for ISPCORE Jai Luthra
2026-04-10  9:05 ` [PATCH v7 05/18] media: rppx1: Add support for AWB measurement parameters and statistics Jai Luthra
2026-04-10  9:05 ` [PATCH v7 06/18] media: rppx1: Add support for AWB gain settings Jai Luthra
2026-04-10  9:05 ` [PATCH v7 07/18] media: rppx1: Add support for Auto Exposure Measurement Jai Luthra
2026-04-10  9:05 ` [PATCH SQUASH v7 08/18] media: rppx1: exm: Expose coefficients, RGB mode and channel selection Jai Luthra
2026-04-10  9:05 ` [PATCH v7 09/18] media: rppx1: Add support for Histogram Measurement Jai Luthra
2026-04-10  9:05 ` [PATCH SQUASH v7 10/18] media: rppx1: hist: Expose channel selection Jai Luthra
2026-04-10  9:05 ` [PATCH v7 11/18] media: rppx1: Add support for Black Level Subtraction Jai Luthra
2026-04-10  9:05 ` [PATCH v7 12/18] media: rppx1: Add support for Color Correction Matrix Jai Luthra
2026-04-10  9:05 ` [PATCH v7 13/18] media: rppx1: Add support for Lens Shade Correction Jai Luthra
2026-04-10  9:05 ` [PATCH SQUASH v7 14/18] media: rppx1: lsc: Make full lens grid programmable Jai Luthra
2026-04-10  9:05 ` [PATCH v7 15/18] media: rppx1: Add support for Gamma Correction Jai Luthra
2026-04-10  9:05 ` [PATCH v7 16/18] media: rppx1: Add support for Bayer Demosaicing Jai Luthra
2026-04-10  9:05 ` [PATCH v7 17/18] media: rppx1: Add support for Bilateral Denoising Jai Luthra
2026-04-10  9:05 ` [PATCH v7 18/18] media: rppx1: Add support for Sensor (Gamma) Linearization Jai Luthra

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