Linux-Rockchip Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] media: rockchip: rkisp2: Add driver for ISP on Rk3588
@ 2026-08-24  9:03 Paul Elder
  2026-08-24  9:03 ` [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings Paul Elder
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Paul Elder @ 2026-08-24  9:03 UTC (permalink / raw)
  To: laurent.pinchart
  Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, mehdi.djait,
	linux-media, linux-arm-kernel, linux-rockchip, linux-kernel

Hello everyone!

This patch series adds a new rkisp2 driver that aims to support all
Rockchip 2.x and 3.x series ISPs. Here we add support for the version on
the RK3588, which as far as I'm aware is a 3.0.

Patches 1 and 2 add dt bindings and dt nodes; patches 3~5 add the actual
driver.

Notable changes from the RFC version:
- fixed a couple potential page fault storms
- use extensible stats (previously only params were extensible)
- no more subdev pad crops; all cropping has been moved to parameter
  buffers so that they can be configured synchronously
- the DMA RX sink pads and the main/self path source pads on the ISP
  subdev have been split into individual pads
- an unused sink pad has been allocated on the ISP subdev for future
  inline mode support (to prevent uapi from changing)

This series is based on the latest version (v7.2-rc1) (at the time of
posting) of media-commiters/next [0], with some patches on top for
testing on the Orange Pi CM5 Base with an imx708. I have a branch [1]
that integrates this series on top of said patches and
media-committers/next.

Corresponding libcamera patches+branch will be coming soon (within
minutes), but since I cannot send and link these simultanously, you will
have to search the libcamera mailing list :)

[0] https://gitlab.freedesktop.org/linux-media/media-committers/-/tree/4900cad020c0580dfb1be27776ff10a4ef110cfa
[1] https://git.ideasonboard.com/epaul/linux/src/branch/epaul/v7.2-rc1/rk3588/rkisp2/upstream-v2

Paul Elder (3):
  media: dt-bindings: Add rockchip rkisp2 bindings
  media: rkisp2: Add parameters output video node
  media: rkisp2: Add statistics capture video node

Xu Hongfei (2):
  arm64: dts: rockchip: add ISP nodes to rk3588
  media: rockchip: rkisp2: Add rkisp2 driver

 .../bindings/media/rockchip-isp2.yaml         |  125 +
 .../userspace-api/media/v4l/meta-formats.rst  |    1 +
 .../media/v4l/metafmt-rkisp2.rst              |   88 +
 arch/arm64/boot/dts/rockchip/rk3588-base.dtsi |   57 +
 drivers/media/platform/rockchip/Kconfig       |    1 +
 drivers/media/platform/rockchip/Makefile      |    1 +
 .../media/platform/rockchip/rkisp2/Kconfig    |   19 +
 .../media/platform/rockchip/rkisp2/Makefile   |   14 +
 .../platform/rockchip/rkisp2/rkisp2-capture.c | 1540 ++++++++
 .../platform/rockchip/rkisp2/rkisp2-common.c  |  180 +
 .../platform/rockchip/rkisp2/rkisp2-common.h  |  619 +++
 .../platform/rockchip/rkisp2/rkisp2-debug.c   |  234 ++
 .../platform/rockchip/rkisp2/rkisp2-dev.c     |  371 ++
 .../platform/rockchip/rkisp2/rkisp2-dmarx.c   |  706 ++++
 .../platform/rockchip/rkisp2/rkisp2-isp.c     |  839 ++++
 .../platform/rockchip/rkisp2/rkisp2-params.c  | 1161 ++++++
 .../rockchip/rkisp2/rkisp2-regs-v2x.h         | 2625 +++++++++++++
 .../rockchip/rkisp2/rkisp2-regs-v3x.h         | 3428 +++++++++++++++++
 .../platform/rockchip/rkisp2/rkisp2-regs.h    | 1736 +++++++++
 .../platform/rockchip/rkisp2/rkisp2-resizer.c |  404 ++
 .../platform/rockchip/rkisp2/rkisp2-stats.c   |  508 +++
 drivers/media/v4l2-core/v4l2-ioctl.c          |    2 +
 include/uapi/linux/rkisp1-config.h            |    1 +
 include/uapi/linux/rkisp2-config.h            |  605 +++
 include/uapi/linux/videodev2.h                |    4 +
 25 files changed, 15269 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp2.yaml
 create mode 100644 Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst
 create mode 100644 drivers/media/platform/rockchip/rkisp2/Kconfig
 create mode 100644 drivers/media/platform/rockchip/rkisp2/Makefile
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-capture.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-common.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-dmarx.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-regs.h
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-resizer.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c
 create mode 100644 include/uapi/linux/rkisp2-config.h

-- 
2.47.2


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings
  2026-08-24  9:03 [PATCH v2 0/5] media: rockchip: rkisp2: Add driver for ISP on Rk3588 Paul Elder
@ 2026-08-24  9:03 ` Paul Elder
  2026-08-24 10:52   ` Krzysztof Kozlowski
  2026-08-24  9:03 ` [PATCH v2 2/5] arm64: dts: rockchip: add ISP nodes to rk3588 Paul Elder
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Paul Elder @ 2026-08-24  9:03 UTC (permalink / raw)
  To: laurent.pinchart
  Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, mehdi.djait,
	linux-media, linux-arm-kernel, linux-rockchip, linux-kernel,
	Heiko Stuebner

Add documentation for the Rockchip rkisp2 bindings. This is meant to
support multiple versions of Rockchip ISPs going forward, including the
2.x series and 3.x series. The current version only adds the compatible
for the RK3588, which is a 3.0.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
No change in v2
---
 .../bindings/media/rockchip-isp2.yaml         | 125 ++++++++++++++++++
 1 file changed, 125 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp2.yaml

diff --git a/Documentation/devicetree/bindings/media/rockchip-isp2.yaml b/Documentation/devicetree/bindings/media/rockchip-isp2.yaml
new file mode 100644
index 000000000000..3cc7eda4d376
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip-isp2.yaml
@@ -0,0 +1,125 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/rockchip-isp2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SoC Image Signal Processing unit v2
+
+maintainers:
+  - Paul Elder <paul.elder@ideasonboard.com>
+
+description: |
+  Rockchip ISP2 is the Camera interface for the Rockchip series of SoCs which
+  contains image processing, scaling, and compression functions.
+
+properties:
+  compatible:
+    enum:
+      - rockchip,rk3588-isp
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 2
+    maxItems: 2
+
+  interrupt-names:
+    items:
+      - const: isp_irq
+      - const: mi_irq
+
+  clocks:
+    minItems: 3
+    items:
+      - description: ISP AXI clock (aclk)
+      - description: ISP AHB clock (hclk)
+      - description: ISP core clock (isp)
+      # for rk3588
+      - description: ISP core clock (marvin)
+      - description: ISP core clock (vicap)
+
+  clock-names:
+    minItems: 3
+    items:
+      - const: aclk
+      - const: hclk
+      - const: clk_core
+      # for rk3588
+      - const: clk_core_marvin
+      - const: clk_core_vicap
+
+  iommus:
+    maxItems: 1
+
+  power-domains:
+    items:
+      - description: ISP power domain
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/$defs/port-base
+        unevaluatedProperties: false
+        description: connection point for VICAP in inline mode
+
+        properties:
+          endpoint:
+            $ref: video-interfaces.yaml#
+            unevaluatedProperties: false
+
+    required:
+      - port@0
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - power-domains
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: rockchip,rk3588-isp
+    then:
+      properties:
+        clocks:
+          minItems: 5
+        clock-names:
+          minItems: 5
+
+additionalProperties: false
+
+examples:
+  - |
+
+    #include <dt-bindings/clock/rockchip,rk3588-cru.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/rk3588-power.h>
+
+    parent0: parent {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        isp0: isp@fdcb0000 {
+            compatible = "rockchip,rk3588-isp";
+            reg = <0x0 0xfdcb0000 0x0 0x7f00>;
+            interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH 0>,
+                         <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH 0>;
+            interrupt-names = "isp_irq", "mi_irq";
+            clocks = <&cru ACLK_ISP0>, <&cru HCLK_ISP0>,
+                     <&cru CLK_ISP0_CORE>, <&cru CLK_ISP0_CORE_MARVIN>,
+                     <&cru CLK_ISP0_CORE_VICAP>;
+            clock-names = "aclk", "hclk", "clk_core",
+                        "clk_core_marvin", "clk_core_vicap";
+            power-domains = <&power RK3588_PD_VI>;
+            iommus = <&isp0_mmu>;
+        };
+    };
-- 
2.47.2


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* [PATCH v2 2/5] arm64: dts: rockchip: add ISP nodes to rk3588
  2026-08-24  9:03 [PATCH v2 0/5] media: rockchip: rkisp2: Add driver for ISP on Rk3588 Paul Elder
  2026-08-24  9:03 ` [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings Paul Elder
@ 2026-08-24  9:03 ` Paul Elder
  2026-08-24 10:52   ` Krzysztof Kozlowski
  2026-08-24  9:04 ` [PATCH v2 4/5] media: rkisp2: Add parameters output video node Paul Elder
  2026-08-24  9:04 ` [PATCH v2 5/5] media: rkisp2: Add statistics capture " Paul Elder
  3 siblings, 1 reply; 9+ messages in thread
From: Paul Elder @ 2026-08-24  9:03 UTC (permalink / raw)
  To: laurent.pinchart
  Cc: Xu Hongfei, michael.riesch, stefan.klug, mehdi.djait, linux-media,
	linux-arm-kernel, linux-rockchip, linux-kernel, Paul Elder,
	Heiko Stuebner

From: Xu Hongfei <xuhf@rock-chips.com>

Add device tree nodes for the ISP and their iommus on the RK3588.

Signed-off-by: Xu Hongfei <xuhf@rock-chips.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
No change in v2
---
 arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index fc1fdbfd3162..22b78465a823 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -3590,6 +3590,63 @@ gpio4: gpio@fec50000 {
 			#interrupt-cells = <2>;
 		};
 	};
+
+	isp0: isp@fdcb0000 {
+		compatible = "rockchip,rk3588-isp";
+		reg = <0x0 0xfdcb0000 0x0 0x7f00>;
+		interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH 0>;
+		interrupt-names = "isp_irq", "mi_irq";
+		clocks = <&cru ACLK_ISP0>, <&cru HCLK_ISP0>,
+			 <&cru CLK_ISP0_CORE>, <&cru CLK_ISP0_CORE_MARVIN>,
+			 <&cru CLK_ISP0_CORE_VICAP>;
+		clock-names = "aclk", "hclk", "clk_core",
+			      "clk_core_marvin", "clk_core_vicap";
+		power-domains = <&power RK3588_PD_VI>;
+		iommus = <&isp0_mmu>;
+		status = "disabled";
+	};
+
+	isp0_mmu: iommu@fdcb7f00 {
+		compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
+		reg = <0x0 0xfdcb7f00 0x0 0x100>;
+		interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH 0>;
+		clocks = <&cru ACLK_ISP0>, <&cru HCLK_ISP0>;
+		clock-names = "aclk", "iface";
+		power-domains = <&power RK3588_PD_VI>;
+		#iommu-cells = <0>;
+		rockchip,disable-mmu-reset;
+		status = "disabled";
+	};
+
+	isp1: isp@fdcc0000 {
+		compatible = "rockchip,rk3588-isp";
+		reg = <0x0 0xfdcc0000 0x0 0x7f00>;
+		interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH 0>;
+		interrupt-names = "isp_irq", "mi_irq";
+		clocks = <&cru ACLK_ISP1>, <&cru HCLK_ISP1>,
+			 <&cru CLK_ISP1_CORE>, <&cru CLK_ISP1_CORE_MARVIN>,
+			 <&cru CLK_ISP1_CORE_VICAP>;
+		clock-names = "aclk", "hclk", "clk_core",
+			      "clk_core_marvin", "clk_core_vicap";
+		power-domains = <&power RK3588_PD_ISP1>;
+		iommus = <&isp1_mmu>;
+		status = "disabled";
+	};
+
+	isp1_mmu: iommu@fdcc7f00 {
+		compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
+		reg = <0x0 0xfdcc7f00 0x0 0x100>;
+		interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH 0>;
+		clocks = <&cru ACLK_ISP1>, <&cru HCLK_ISP1>;
+		clock-names = "aclk", "iface";
+		power-domains = <&power RK3588_PD_ISP1>;
+		#iommu-cells = <0>;
+		rockchip,disable-mmu-reset;
+		status = "disabled";
+	};
 };
 
 #include "rk3588-base-pinctrl.dtsi"
-- 
2.47.2


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* [PATCH v2 4/5] media: rkisp2: Add parameters output video node
  2026-08-24  9:03 [PATCH v2 0/5] media: rockchip: rkisp2: Add driver for ISP on Rk3588 Paul Elder
  2026-08-24  9:03 ` [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings Paul Elder
  2026-08-24  9:03 ` [PATCH v2 2/5] arm64: dts: rockchip: add ISP nodes to rk3588 Paul Elder
@ 2026-08-24  9:04 ` Paul Elder
  2026-08-24  9:04 ` [PATCH v2 5/5] media: rkisp2: Add statistics capture " Paul Elder
  3 siblings, 0 replies; 9+ messages in thread
From: Paul Elder @ 2026-08-24  9:04 UTC (permalink / raw)
  To: laurent.pinchart
  Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, mehdi.djait,
	linux-media, linux-arm-kernel, linux-rockchip, linux-kernel

Implement support for setting parameters on the ISP by queueing
parameter buffers to rkisp2.

Support for the following parameters is added:
- BLS (black level subtraction)
- AWB gains (color gains)
- CSM (color space conversion)
- CCM (color correction matrix)
- GOC (gamma out correction)
- LSC (lens shading correction)
- Crop

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
Changes in v2:
- enable bls
- add crop
---
 .../userspace-api/media/v4l/meta-formats.rst  |   1 +
 .../media/v4l/metafmt-rkisp2.rst              |  70 ++
 .../media/platform/rockchip/rkisp2/Makefile   |   4 +-
 .../platform/rockchip/rkisp2/rkisp2-common.h  |  98 ++
 .../platform/rockchip/rkisp2/rkisp2-dev.c     |  17 +
 .../platform/rockchip/rkisp2/rkisp2-isp.c     |  13 +-
 .../platform/rockchip/rkisp2/rkisp2-params.c  | 923 ++++++++++++++++++
 .../rockchip/rkisp2/rkisp2-regs-v3x.h         |  31 +
 .../platform/rockchip/rkisp2/rkisp2-resizer.c | 404 ++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c          |   1 +
 include/uapi/linux/rkisp2-config.h            | 343 +++++++
 include/uapi/linux/videodev2.h                |   3 +
 12 files changed, 1906 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-resizer.c

diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst
index b5455229837e..6809e3d9fc22 100644
--- a/Documentation/userspace-api/media/v4l/meta-formats.rst
+++ b/Documentation/userspace-api/media/v4l/meta-formats.rst
@@ -21,6 +21,7 @@ These formats are used for the :ref:`metadata` interface only.
     metafmt-pisp-be
     metafmt-pisp-fe
     metafmt-rkisp1
+    metafmt-rkisp2
     metafmt-rppx1
     metafmt-uvc
     metafmt-uvc-msxu-1-5
diff --git a/Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst b/Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst
new file mode 100644
index 000000000000..e0ca6cbed642
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst
@@ -0,0 +1,70 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _v4l2-meta-fmt-rkisp2-params:
+
+************************************
+V4L2_META_FMT_RKISP2_PARAMS ('RK2P')
+************************************
+
+Configuration Parameters
+========================
+
+The configuration of the RkISP1 ISP is performed by userspace by providing
+parameters for the ISP to the driver using the :c:type:`v4l2_meta_format`
+interface.
+
+The configuration parameters are passed to the :ref:`rkisp2_params
+<rkisp2_params>` metadata output video node, using the
+:c:type:`v4l2_meta_format` interface. Rather than a single struct containing
+sub-structs for each configurable area of the ISP, parameters for the Mali-C55
+use the v4l2-isp parameters system, through which groups of parameters are
+defined as distinct structs or "blocks" which may be added to the data member
+of :c:type:`v4l2_isp_params_buffer`. Userspace is responsible for populating
+the data member with the blocks that need to be configured by the driver. Each
+block-specific struct embeds :c:type:`v4l2_isp_params_block_header` as its
+first member and userspace must populate the type member with a value from
+:c:type:`rkisp2_params_block_type`.
+
+.. code-block:: c
+
+	struct v4l2_isp_params_buffer *params =
+		(struct v4l2_isp_params_buffer *)buffer;
+
+	params->version = V4L2_ISP_PARAMS_VERSION_V1;
+	params->data_size = 0;
+
+	void *data = (void *)params->data;
+
+	struct rkisp2_params_awb_gains *gains =
+		(struct rkisp2_params_awb_gains *)data;
+
+	gains->header.type = RKISP2_PARAMS_BLOCK_AWB_GAINS;
+	gains->header.flags |= V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+	gains->header.size = sizeof(struct rkisp2_params_awb_gains);
+
+        gains->gains[0].gb = 256;
+        gains->gains[0].b = 256;
+        gains->gains[0].r = 256;
+        gains->gains[0].gr = 256;
+
+	data += sizeof(struct rkisp2_params_awb_gains);
+	params->data_size += sizeof(struct rkisp2_params_awb_gains);
+
+	struct rkisp2_params_bls *bls = (struct rkisp2_params_bls *)data;
+
+	bls->header.type = RKISP2_PARAMS_BLOCK_BLS;
+	bls->header.flags |= V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+	bls->header.size = sizeof(struct rkisp2_params_bls);
+
+        bls->enable_auto = 0;
+        bls->bls_fixed_val.a = 256;
+        bls->bls_fixed_val.b = 256;
+        bls->bls_fixed_val.c = 256;
+        bls->bls_fixed_val.d = 256;
+
+	params->data_size += sizeof(struct rkisp2_params_bls);
+
+rkisp2 uAPI data types
+======================
+
+.. kernel-doc:: include/uapi/linux/rkisp2-config.h
diff --git a/drivers/media/platform/rockchip/rkisp2/Makefile b/drivers/media/platform/rockchip/rkisp2/Makefile
index 9a9d7b5233c4..2d41b4ab8f7c 100644
--- a/drivers/media/platform/rockchip/rkisp2/Makefile
+++ b/drivers/media/platform/rockchip/rkisp2/Makefile
@@ -4,7 +4,9 @@ rockchip-isp2-y := rkisp2-capture.o \
            rkisp2-common.o \
            rkisp2-dev.o \
            rkisp2-dmarx.o \
-           rkisp2-isp.o
+           rkisp2-isp.o \
+           rkisp2-params.o \
+	   rkisp2-resizer.o
 
 rockchip-isp2-$(CONFIG_DEBUG_FS) += rkisp2-debug.o
 
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
index 596f6ca8f755..0479a24fa7f9 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
@@ -22,6 +22,7 @@
 #include <media/media-entity.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-isp.h>
 #include <media/videobuf2-v4l2.h>
 
 #include "rkisp2-regs.h"
@@ -101,6 +102,7 @@ enum rkisp2_isp_pad {
 	RKISP2_ISP_PAD_SINK_VIDEO_DMA_2,
 	RKISP2_ISP_PAD_SINK_VIDEO_DMA_MAX,
 	RKISP2_ISP_PAD_SINK_VIDEO_CIF = RKISP2_ISP_PAD_SINK_VIDEO_DMA_MAX,
+	RKISP2_ISP_PAD_SINK_PARAMS,
 	RKISP2_ISP_PAD_SOURCE_VIDEO_MAIN,
 	RKISP2_ISP_PAD_SOURCE_VIDEO_SELF,
 	RKISP2_ISP_PAD_MAX
@@ -196,6 +198,26 @@ struct rkisp2_buffer {
 	dma_addr_t buff_addr[VIDEO_MAX_PLANES];
 };
 
+/*
+ * struct rkisp2_params_buffer - A container for the vb2 buffers used by the
+ *				 params video device
+ *
+ * @vb:		vb2 buffer
+ * @queue:	entry of the buffer in the queue
+ * @cfg:	scratch buffer used for caching the ISP configuration parameters
+ */
+struct rkisp2_params_buffer {
+	struct vb2_v4l2_buffer vb;
+	struct list_head queue;
+	struct v4l2_isp_params_buffer *cfg;
+};
+
+static inline struct rkisp2_params_buffer *
+to_rkisp2_params_buffer(struct vb2_v4l2_buffer *vbuf)
+{
+	return container_of(vbuf, struct rkisp2_params_buffer, vb);
+}
+
 /*
  * struct rkisp2_dummy_buffer - A buffer to write the next frame to in case
  *				there are no vb2 buffers available.
@@ -298,6 +320,61 @@ struct rkisp2_capture {
 	} pix;
 };
 
+/*
+ * struct rkisp2_params - ISP input parameters device
+ *
+ * @vnode:			video node
+ * @rkisp2:			pointer to the rkisp2 device
+ * @buf_lock:			locks the buffer list 'params'
+ * @params:			queue of rkisp2_buffer
+ * @raw_type:			the bayer pattern on the isp video sink pad
+ */
+struct rkisp2_params {
+	struct rkisp2_vdev_node vnode;
+	struct rkisp2_device *rkisp2;
+
+	spinlock_t buf_lock; /* locks the buffers list 'params' */
+	struct list_head params;
+
+	enum rkisp2_fmt_raw_pat_type raw_type;
+};
+
+/*
+ * struct rkisp2_resizer - ISP resizer device
+ *
+ * This struct stores information relevant to the configuration of the resizer
+ * and dual crop blocks. There is no associated subdev because hardware-wise
+ * this resides in the ISP block, but we configure it via parameters buffers to
+ * enable synchronous crop rectangle configuration. The configuration
+ * information is populated at s_stream time and does not change.
+ *
+ * @rkisp2:			Pointer to the rkisp2 device
+ * @id:				id of the capture, one of RKISP2_SELFPATH, RKISP2_MAINPATH
+ * @config:			The register set for the resizer and dual crop
+ * @sink_size:			The size of the sink format of the ISP subdev sink pad
+ * @source_size:		The size of the pad-specific source format of the ISP subdev sink pad
+ * @source_format:		The mbus format of the ISP subdev source pad
+ * @source_fmt_info:		A pointer to the v4l2_format_info of the pixel
+ *
+ *
+ * @sink_size is the ISP sink size (corresponding to the output of IS). It will
+ * be used to compare the crop rectangle to to check if dual crop needs to be
+ * enabled or not.
+ *
+ * @source_size is the output of the combined dual crop + resizer.
+ */
+struct rkisp2_resizer {
+	struct rkisp2_device *rkisp2;
+	enum rkisp2_stream_id id;
+	const struct rkisp2_resizer_config *config;
+
+	struct v4l2_rect sink_size;
+	struct v4l2_rect source_size;
+
+	struct v4l2_mbus_framefmt source_format;
+	const struct v4l2_format_info *source_fmt_info;
+};
+
 struct rkisp2_debug {
 	struct dentry *debugfs_dir;
 	unsigned long data_loss;
@@ -330,6 +407,7 @@ struct rkisp2_debug {
  * @resizer_devs:  resizer sub-devices
  * @capture_devs:  capture devices
  * @dmarx:	   ISP memory read device
+ * @params:	   ISP parameters metadata output device
  * @pipe:	   media pipeline
  * @stream_lock:   serializes {start/stop}_streaming callbacks between the capture devices.
  * @debug:	   debug params to be exposed on debugfs
@@ -349,8 +427,10 @@ struct rkisp2_device {
 	struct v4l2_async_notifier notifier;
 	struct v4l2_subdev *source;
 	struct rkisp2_isp isp;
+	struct rkisp2_resizer resizer_devs[2];
 	struct rkisp2_capture capture_devs[2];
 	struct rkisp2_dmarx dmarx;
+	struct rkisp2_params params;
 	struct media_pipeline pipe;
 	struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */
 	struct rkisp2_debug debug;
@@ -440,6 +520,7 @@ irqreturn_t rkisp2_isp_isr(int irq, void *ctx);
 irqreturn_t rkisp2_capture_isr(int irq, void *ctx);
 irqreturn_t rkisp2_mipi_isr(int irq, void *ctx);
 void rkisp2_dmarx_isr(struct rkisp2_device *rkisp2, u32 status);
+void rkisp2_params_isr(struct rkisp2_params *params);
 
 /* register/unregisters functions of the entities */
 int rkisp2_capture_devs_register(struct rkisp2_device *rkisp2);
@@ -450,6 +531,23 @@ void rkisp2_isp_unregister(struct rkisp2_device *rkisp2);
 int rkisp2_dmarx_register(struct rkisp2_device *rkisp2);
 void rkisp2_dmarx_unregister(struct rkisp2_device *rkisp2);
 
+int rkisp2_params_register(struct rkisp2_device *rkisp2);
+void rkisp2_params_unregister(struct rkisp2_device *rkisp2);
+void rkisp2_params_pre_configure(struct rkisp2_params *params,
+				 enum rkisp2_fmt_raw_pat_type bayer_pat,
+				 const struct v4l2_mbus_framefmt *sink_frm,
+				 const struct v4l2_mbus_framefmt *mp_src_frm,
+				 const struct v4l2_mbus_framefmt *sp_src_frm);
+void rkisp2_params_post_configure(struct rkisp2_params *params);
+
+void rkisp2_resizer_configure(struct rkisp2_resizer *rsz,
+			      struct v4l2_rect *crop);
+void rkisp2_resizer_pre_configure(struct rkisp2_device *rkisp2,
+				  const struct v4l2_mbus_framefmt *sink_frm,
+				  const struct v4l2_mbus_framefmt *mp_src_frm,
+				  const struct v4l2_mbus_framefmt *sp_src_frm);
+void rkisp2_resizer_devs_init(struct rkisp2_device *rkisp2);
+
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 void rkisp2_debug_init(struct rkisp2_device *rkisp2);
 void rkisp2_debug_cleanup(struct rkisp2_device *rkisp2);
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
index 3c7edd2e55a3..6fc0b6044121 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
@@ -122,11 +122,22 @@ static int rkisp2_create_links(struct rkisp2_device *rkisp2)
 			return ret;
 	}
 
+	/* params links */
+	ret = media_create_pad_link(&rkisp2->params.vnode.vdev.entity, 0,
+				    &rkisp2->isp.sd.entity,
+				    RKISP2_ISP_PAD_SINK_PARAMS,
+				    MEDIA_LNK_FL_ENABLED |
+				    MEDIA_LNK_FL_IMMUTABLE);
+	if (ret)
+		return ret;
+
+
 	return 0;
 }
 
 static void rkisp2_entities_unregister(struct rkisp2_device *rkisp2)
 {
+	rkisp2_params_unregister(rkisp2);
 	rkisp2_dmarx_unregister(rkisp2);
 	rkisp2_capture_devs_unregister(rkisp2);
 	rkisp2_isp_unregister(rkisp2);
@@ -140,6 +151,8 @@ static int rkisp2_entities_register(struct rkisp2_device *rkisp2)
 	if (ret)
 		goto error;
 
+	rkisp2_resizer_devs_init(rkisp2);
+
 	ret = rkisp2_capture_devs_register(rkisp2);
 	if (ret)
 		goto error;
@@ -148,6 +161,10 @@ static int rkisp2_entities_register(struct rkisp2_device *rkisp2)
 	if (ret)
 		goto error;
 
+	ret = rkisp2_params_register(rkisp2);
+	if (ret)
+		goto error;
+
 	ret = rkisp2_create_links(rkisp2);
 	if (ret)
 		goto error;
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
index c1c03bc71a8f..00336c5e99f3 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
@@ -93,6 +93,7 @@ static int rkisp2_config_isp(struct rkisp2_isp *isp,
 	const struct rkisp2_mbus_info *sink_fmt;
 	const struct rkisp2_mbus_info *mp_src_fmt;
 	const struct v4l2_mbus_framefmt *mp_src_frm;
+	const struct v4l2_mbus_framefmt *sp_src_frm;
 	const struct v4l2_mbus_framefmt *sink_frm;
 
 	/* All the video sink formats and crops are kept equal */
@@ -150,6 +151,11 @@ static int rkisp2_config_isp(struct rkisp2_isp *isp,
 		    RKISP2_CIF_ISP_PIC_SIZE_ERROR;
 	rkisp2_write(rkisp2, RKISP2_CIF_ISP_IMSC, irq_mask);
 
+	sp_src_frm = v4l2_subdev_state_get_format(sd_state,
+					       RKISP2_ISP_PAD_SOURCE_VIDEO_SELF);
+	rkisp2_params_pre_configure(&rkisp2->params, sink_fmt->bayer_pat,
+				    sink_frm, mp_src_frm, sp_src_frm);
+
 	isp->sink_fmt = sink_fmt;
 
 	return 0;
@@ -278,6 +284,8 @@ static int rkisp2_isp_start(struct rkisp2_isp *isp,
 	       RKISP2_CIF_ISP_CTRL_ISP_INFORM_ENABLE | RKISP2_CIF_ISP_CTRL_ISP_CFG_UPD_PERMANENT;
 	rkisp2_write(rkisp2, RKISP2_CIF_ISP_CTRL, val);
 
+	rkisp2_params_post_configure(&rkisp2->params);
+
 	return 0;
 }
 
@@ -727,6 +735,7 @@ int rkisp2_isp_register(struct rkisp2_device *rkisp2)
 	pads[RKISP2_ISP_PAD_SINK_VIDEO_DMA_1].flags = MEDIA_PAD_FL_SINK;
 	pads[RKISP2_ISP_PAD_SINK_VIDEO_DMA_2].flags = MEDIA_PAD_FL_SINK;
 	pads[RKISP2_ISP_PAD_SINK_VIDEO_CIF].flags = MEDIA_PAD_FL_SINK;
+	pads[RKISP2_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
 	pads[RKISP2_ISP_PAD_SOURCE_VIDEO_MAIN].flags = MEDIA_PAD_FL_SOURCE;
 	pads[RKISP2_ISP_PAD_SOURCE_VIDEO_SELF].flags = MEDIA_PAD_FL_SOURCE;
 
@@ -817,8 +826,10 @@ irqreturn_t rkisp2_isp_isr(int irq, void *ctx)
 		rkisp2->debug.data_loss++;
 	}
 
-	if (status & RKISP2_CIF_ISP_FRAME)
+	if (status & RKISP2_CIF_ISP_FRAME) {
 		rkisp2->debug.complete_frames++;
+		rkisp2_params_isr(&rkisp2->params);
+	}
 
 	rkisp2_write(rkisp2, RKISP2_CIF_ISP_ICR, status);
 
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
new file mode 100644
index 000000000000..2fac73709ae4
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
@@ -0,0 +1,923 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+/*
+ * Rockchip ISP2 Driver - Params subdevice
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2026 Ideas on Board Oy.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/build_bug.h>
+#include <linux/math.h>
+#include <linux/string.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
+#include <media/v4l2-rect.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "rkisp2-common.h"
+
+#define RKISP2_PARAMS_DEV_NAME	RKISP2_DRIVER_NAME "_params"
+
+#define RKISP2_PARAMS_BLOCK_INFO(block, data) \
+	[RKISP2_PARAMS_BLOCK_ ## block] = { \
+		.size = sizeof(struct rkisp2_params_ ## data ), \
+	}
+
+#define RKISP2_PARAMS_BLOCK_HANDLER_INFO(block, handler_postfix, prio_postfix) \
+	[RKISP2_PARAMS_BLOCK_ ## block] = { \
+		.handler = rkisp2_params_ ## handler_postfix,\
+		.priority = RKISP2_PARAMS_CONFIG_PRIO_ ## prio_postfix, \
+	}
+
+union rkisp2_params_block {
+	const struct v4l2_isp_params_block_header *header;
+	const struct rkisp2_params_bls *bls;
+	const struct rkisp2_params_awb_gains *awb_gains;
+	const struct rkisp2_params_csm *csm;
+	const struct rkisp2_params_ccm *ccm;
+	const struct rkisp2_params_goc *goc;
+	const struct rkisp2_params_lsc *lsc;
+	const struct rkisp2_params_crop *crop;
+	const __u8 *data;
+};
+
+static void rkisp2_params_bls(struct rkisp2_params *params,
+			      union rkisp2_params_block block);
+static void rkisp2_params_awb_gains(struct rkisp2_params *params,
+				    union rkisp2_params_block block);
+static void rkisp2_params_csm(struct rkisp2_params *params,
+			      union rkisp2_params_block block);
+static void rkisp2_params_ccm(struct rkisp2_params *params,
+			      union rkisp2_params_block block);
+static void rkisp2_params_goc(struct rkisp2_params *params,
+			      union rkisp2_params_block block);
+static void rkisp2_params_lsc(struct rkisp2_params *params,
+			      union rkisp2_params_block block);
+static void rkisp2_params_crop(struct rkisp2_params *params,
+			       union rkisp2_params_block block);
+
+typedef void (*rkisp2_params_handler)(struct rkisp2_params *params,
+				      const union rkisp2_params_block block);
+
+enum rkisp2_params_configure_priority {
+	RKISP2_PARAMS_CONFIG_PRIO_NONE = 0,
+	RKISP2_PARAMS_CONFIG_PRIO_PRE,
+	RKISP2_PARAMS_CONFIG_PRIO_POST,
+};
+
+struct rkisp2_params_block_handler_info {
+	rkisp2_params_handler handler;
+	enum rkisp2_params_configure_priority priority;
+};
+
+static const struct rkisp2_params_block_handler_info
+rkisp2_params_handlers[] = {
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(BLS, bls, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(AWB_GAINS, awb_gains, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(CSM, csm, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(CCM, ccm, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(GOC, goc, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(LSC, lsc, POST),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(CROP, crop, PRE),
+};
+
+static const struct v4l2_isp_params_block_type_info
+rkisp2_params_block_types_info[] = {
+	RKISP2_PARAMS_BLOCK_INFO(BLS, bls),
+	RKISP2_PARAMS_BLOCK_INFO(AWB_GAINS, awb_gains),
+	RKISP2_PARAMS_BLOCK_INFO(CSM, csm),
+	RKISP2_PARAMS_BLOCK_INFO(CCM, ccm),
+	RKISP2_PARAMS_BLOCK_INFO(GOC, goc),
+	RKISP2_PARAMS_BLOCK_INFO(LSC, lsc),
+	RKISP2_PARAMS_BLOCK_INFO(CROP, crop),
+};
+
+static_assert(ARRAY_SIZE(rkisp2_params_handlers) ==
+	      ARRAY_SIZE(rkisp2_params_block_types_info));
+
+static inline void
+rkisp2_param_set_bits(struct rkisp2_params *params, u32 reg, u32 bit_mask)
+{
+	u32 val;
+
+	val = rkisp2_read(params->rkisp2, reg);
+	rkisp2_write(params->rkisp2, reg, val | bit_mask);
+}
+
+static inline void
+rkisp2_param_clear_bits(struct rkisp2_params *params, u32 reg, u32 bit_mask)
+{
+	u32 val;
+
+	val = rkisp2_read(params->rkisp2, reg);
+	rkisp2_write(params->rkisp2, reg, val & ~bit_mask);
+}
+
+static void rkisp2_params_bls(struct rkisp2_params *params,
+			      union rkisp2_params_block block)
+{
+	const struct rkisp2_params_bls *arg = block.bls;
+	u32 control;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ISP_BLS_CTRL, ISP_BLS_ENA | ISP_BLS_BLS1_EN);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	control = ISP_BLS_ENA;
+
+	if (!arg->enable_auto) {
+		/* TODO plumb BLS1 */
+
+		rkisp2_write(params->rkisp2, ISP_BLS_A_FIXED, arg->bls_fixed_val.a);
+		rkisp2_write(params->rkisp2, ISP_BLS_B_FIXED, arg->bls_fixed_val.b);
+		rkisp2_write(params->rkisp2, ISP_BLS_C_FIXED, arg->bls_fixed_val.c);
+		rkisp2_write(params->rkisp2, ISP_BLS_D_FIXED, arg->bls_fixed_val.d);
+
+		/* Set fixed mode */
+		rkisp2_write(params->rkisp2, ISP_BLS_CTRL, control);
+		return;
+	}
+
+	if (arg->enabled_windows & BIT(1)) {
+		rkisp2_write(params->rkisp2, ISP_BLS_H2_START,
+			     arg->bls_window2.h_offs);
+		rkisp2_write(params->rkisp2, ISP_BLS_H2_STOP,
+			     arg->bls_window2.h_size);
+		rkisp2_write(params->rkisp2, ISP_BLS_V2_START,
+			     arg->bls_window2.v_offs);
+		rkisp2_write(params->rkisp2, ISP_BLS_V2_STOP,
+			     arg->bls_window2.v_size);
+		control |= ISP_BLS_WINDOW_2;
+	}
+
+	if (arg->enabled_windows & BIT(0)) {
+		rkisp2_write(params->rkisp2, ISP_BLS_H1_START,
+			     arg->bls_window1.h_offs);
+		rkisp2_write(params->rkisp2, ISP_BLS_H1_STOP,
+			     arg->bls_window1.h_size);
+		rkisp2_write(params->rkisp2, ISP_BLS_V1_START,
+			     arg->bls_window1.v_offs);
+		rkisp2_write(params->rkisp2, ISP_BLS_V1_STOP,
+			     arg->bls_window1.v_size);
+		control |= ISP_BLS_WINDOW_1;
+	}
+
+	rkisp2_write(params->rkisp2, ISP_BLS_SAMPLES,
+		     arg->bls_samples);
+
+	control |= ISP_BLS_MODE_MEASURED;
+	rkisp2_write(params->rkisp2, ISP_BLS_CTRL, control);
+}
+
+static void rkisp2_params_awb_gains(struct rkisp2_params *params,
+				    union rkisp2_params_block block)
+{
+	const struct rkisp2_params_awb_gains *arg = block.awb_gains;
+	unsigned int i;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, RKISP2_CIF_ISP_CTRL,
+					RKISP2_CIF_ISP_CTRL_ISP_AWB_ENA);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(arg->gains); i++) {
+		rkisp2_write(params->rkisp2, ISP21_AWB_GAIN0_G + i * 8,
+			     arg->gains[i].gr << 16 | arg->gains[i].gb);
+		rkisp2_write(params->rkisp2, ISP21_AWB_GAIN0_RB + i * 8,
+			     arg->gains[i].r << 16 | arg->gains[i].b);
+	}
+
+	rkisp2_param_set_bits(params, RKISP2_CIF_ISP_CTRL,
+			      RKISP2_CIF_ISP_CTRL_ISP_AWB_ENA);
+}
+
+static void rkisp2_params_csm_reset(struct rkisp2_params *params)
+{
+	/* Write back the default values. */
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_0, 0x80);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_1, 0);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_2, 0);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_3, 0);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_4, 0x80);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_5, 0);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_6, 0);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_7, 0);
+	rkisp2_write(params->rkisp2, RKISP2_CIF_ISP_CC_COEFF_8, 0x80);
+}
+
+static void rkisp2_params_csm(struct rkisp2_params *params,
+			      union rkisp2_params_block block)
+{
+	const struct rkisp2_params_csm *arg = block.csm;
+	unsigned int i, j, k = 0;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_params_csm_reset(params);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	for (i = 0; i < 3; i++)
+		for (j = 0; j < 3; j++)
+			rkisp2_write(params->rkisp2,
+				     RKISP2_CIF_ISP_CC_COEFF_0 + 4 * k++,
+				     arg->coeff[i][j]);
+}
+
+static void rkisp2_params_ccm(struct rkisp2_params *params,
+			      union rkisp2_params_block block)
+{
+	const struct rkisp2_params_ccm *arg = block.ccm;
+	unsigned int i;
+	u32 control = 0;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ISP_CCM_CTRL, ISP_CCM_EN);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	for (i = 0; i < 3; i++) {
+		rkisp2_write(params->rkisp2, ISP_CCM_COEFF0_R + 8 * i,
+			     ISP3X_CCM_COEFF(arg->coeff[i][0], arg->coeff[i][1]));
+		rkisp2_write(params->rkisp2, ISP_CCM_COEFF1_R + 8 * i,
+			     ISP3X_CCM_COEFF(arg->coeff[i][2], arg->offset[i]));
+	}
+
+	rkisp2_write(params->rkisp2, ISP_CCM_COEFF0_Y,
+		     ISP3X_CCM_COEFF(arg->y_coeff[0], arg->y_coeff[1]));
+	rkisp2_write(params->rkisp2, ISP_CCM_COEFF1_Y, arg->y_coeff[2]);
+
+	for (i = 0; i < 8; i++)
+		rkisp2_write(params->rkisp2, ISP_CCM_ALP_Y0 + 4 * i,
+			     ISP3X_CCM_COEFF(arg->alp[2 * i], arg->alp[2 * i + 1]));
+	rkisp2_write(params->rkisp2, ISP_CCM_ALP_Y8, arg->alp[16]);
+
+	rkisp2_write(params->rkisp2, ISP_CCM_BOUND_BIT, arg->inflection_point);
+
+	if (!arg->high_y_alpha_adj_en)
+		control = ISP3X_CCM_HIGHY_ADJ_DIS;
+	control |= ISP_CCM_EN;
+	rkisp2_write(params->rkisp2, ISP_CCM_CTRL, control);
+}
+
+static void rkisp2_params_goc(struct rkisp2_params *params,
+			      union rkisp2_params_block block)
+{
+	const struct rkisp2_params_goc *arg = block.goc;
+	unsigned int i;
+	u32 control = 0;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ISP3X_GAMMA_OUT_CTRL,
+					ISP3X_GAMMA_OUT_CTRL_EN);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	for (i = 0; i < RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS >> 1; i++)
+		rkisp2_write(params->rkisp2,
+			     ISP3X_GAMMA_OUT_Y0 + 4 * i,
+			     ISP3X_GAMMA_OUT_SAMPLE(arg->gamma_y[2 * i],
+						  arg->gamma_y[2 * i + 1]));
+
+	rkisp2_write(params->rkisp2, ISP3X_GAMMA_OUT_Y24,
+		     arg->gamma_y[RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS - 1]);
+
+	rkisp2_write(params->rkisp2, ISP3X_GAMMA_OUT_OFFSET, arg->offset);
+
+	if (arg->mode == RKISP2_ISP_GOC_MODE_EQUIDISTANT)
+		control = ISP3X_GAMMA_OUT_CTRL_MODE_EQUIDISTANT;
+	if (arg->mode == RKISP2_ISP_GOC_SEGMENTS_48)
+		control |= ISP3X_GAMMA_OUT_CTRL_SEGMENTS_48;
+	control |= ISP3X_GAMMA_OUT_CTRL_EN;
+	rkisp2_write(params->rkisp2, ISP3X_GAMMA_OUT_CTRL, control);
+}
+
+static void rkisp2_params_lsc(struct rkisp2_params *params,
+			      union rkisp2_params_block block)
+{
+	const struct rkisp2_params_lsc *arg = block.lsc;
+	struct rkisp2_device *rkisp2 = params->rkisp2;
+	u32 sram_addr;
+	u32 data;
+	unsigned int i, j, table_i;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ISP3X_LSC_CTRL, ISP3X_LSC_CTRL_EN);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	if (arg->set_active_table_when == RKISP2_ISP_LSC_SET_ACTIVE_TABLE_BEFORE)
+		rkisp2_write(rkisp2, ISP3X_LSC_TABLE_SEL, arg->active_table ? 1 : 0);
+
+	/*
+	 * - No need to disable the lsc before writing the table 
+	 * - Table 0 starts at 0, table 1 starts at 153.
+	 * - TABLE_SEL selects which table is active, but programming the tables
+	 *   is done by just writing to the right address.
+	 * - The address automatically increments, so no need to increment it.
+	 */
+
+	for (table_i = 0; table_i < 2; table_i++) {
+		if (!arg->write_table[table_i])
+			continue;
+
+		sram_addr = table_i * 153;
+
+		rkisp2_write(rkisp2, ISP3X_LSC_R_TABLE_ADDR, sram_addr);
+		rkisp2_write(rkisp2, ISP3X_LSC_GR_TABLE_ADDR, sram_addr);
+		rkisp2_write(rkisp2, ISP3X_LSC_B_TABLE_ADDR, sram_addr);
+		rkisp2_write(rkisp2, ISP3X_LSC_GB_TABLE_ADDR, sram_addr);
+
+		/* Program data tables (table size is 9 * 17 = 153) */
+		for (i = 0; i < RKISP2_ISP_LSC_SAMPLES_MAX; i++) {
+			const __u16 *r_row = arg->r_data_tbl[table_i][i];
+			const __u16 *gr_row = arg->gr_data_tbl[table_i][i];
+			const __u16 *gb_row = arg->gb_data_tbl[table_i][i];
+			const __u16 *b_row = arg->b_data_tbl[table_i][i];
+
+			/*
+			 * 17 sectors with 2 values in one DWORD = 9
+			 * DWORDs (2nd value of last DWORD unused)
+			 */
+			for (j = 0; j < RKISP2_ISP_LSC_SAMPLES_MAX / 2; j++) {
+				rkisp2_write(rkisp2, ISP3X_LSC_R_TABLE_DATA,
+					     ISP3X_LSC_TABLE_DATA(r_row[2 * j], r_row[2 * j + 1]));
+				rkisp2_write(rkisp2, ISP3X_LSC_GR_TABLE_DATA,
+					     ISP3X_LSC_TABLE_DATA(gr_row[2 * j], gr_row[2 * j + 1]));
+				rkisp2_write(rkisp2, ISP3X_LSC_GB_TABLE_DATA,
+					     ISP3X_LSC_TABLE_DATA(gb_row[2 * j], gb_row[2 * j + 1]));
+				rkisp2_write(rkisp2, ISP3X_LSC_B_TABLE_DATA,
+					     ISP3X_LSC_TABLE_DATA(b_row[2 * j], b_row[2 * j + 1]));
+			}
+
+			rkisp2_write(rkisp2, ISP3X_LSC_R_TABLE_DATA,
+				     ISP3X_LSC_TABLE_DATA(r_row[2 * j], 0));
+			rkisp2_write(rkisp2, ISP3X_LSC_GR_TABLE_DATA,
+				     ISP3X_LSC_TABLE_DATA(gr_row[2 * j], 0));
+			rkisp2_write(rkisp2, ISP3X_LSC_GB_TABLE_DATA,
+				     ISP3X_LSC_TABLE_DATA(gb_row[2 * j], 0));
+			rkisp2_write(rkisp2, ISP3X_LSC_B_TABLE_DATA,
+				     ISP3X_LSC_TABLE_DATA(b_row[2 * j], 0));
+
+		}
+	}
+
+	/* Program grid sizes and interpolation gradients */
+	for (i = 0; i < RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX / 2; i++) {
+		/* program x size tables */
+		data = ISP3X_LSC_SECT_SIZE(arg->x_sizes[i * 2],
+					   arg->x_sizes[i * 2 + 1]);
+		rkisp2_write(rkisp2, ISP3X_LSC_XSIZE(i), data);
+
+		/* program x grad tables */
+		data = ISP3X_LSC_GRAD_SIZE(arg->x_grads[i * 2],
+					   arg->x_grads[i * 2 + 1]);
+		rkisp2_write(rkisp2, ISP3X_LSC_XGRAD(i), data);
+
+		/* program y size tables */
+		data = ISP3X_LSC_SECT_SIZE(arg->y_sizes[i * 2],
+					   arg->y_sizes[i * 2 + 1]);
+		rkisp2_write(rkisp2, ISP3X_LSC_YSIZE(i), data);
+
+		/* program y grad tables */
+		data = ISP3X_LSC_GRAD_SIZE(arg->y_grads[i * 2],
+					   arg->y_grads[i * 2 + 1]);
+		rkisp2_write(rkisp2, ISP3X_LSC_YGRAD(i), data);
+	}
+
+	rkisp2_write(rkisp2, ISP3X_LSC_TABLE_SEL, arg->active_table ? 1 : 0);
+
+	data = 0;
+	if (arg->window_mode)
+		data |= ISP3X_LSC_SECTOR_16X16;
+	/* TODO plumb the rest of the ctrl fields */
+	data |= ISP3X_LSC_CTRL_EN;
+
+	rkisp2_param_set_bits(params, ISP3X_LSC_CTRL, data);
+}
+
+static void __rkisp2_params_crop(struct rkisp2_params *params,
+				 const struct rkisp2_params_crop *arg,
+				 __u16 flags)
+{
+	unsigned int i;
+
+	/*
+	 * Based on the params provided by userspace and the ISP stream
+	 * configurations and validation, determine the input and output
+	 * rectangles. Then delegate to the resizer routines about how to
+	 * configure the dual crop + resizer.
+	 *
+	 * This handler has a different pattern from the other params handlers
+	 * because the crop + resizer is always active (depending on the stream
+	 * configuration), and the hardware configuration is only adjusted
+	 * based on the parameters.
+	 */
+
+	if (!(flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE) &&
+	    !(flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE))
+		return;
+
+	for (i = 0; i < 2; i++) {
+		struct rkisp2_resizer *rsz = &params->rkisp2->resizer_devs[i];
+
+		const struct rkisp2_isp_window *window =
+			(i == 0 ? &arg->mp_crop : &arg->sp_crop);
+		u16 en_mask =
+			(i == 0 ? RKISP2_ISP_CROP_ENABLE_MAIN :
+			 	  RKISP2_ISP_CROP_ENABLE_SELF);
+
+		/*
+		 * This is input of the combined dual crop + resizer. When crop
+		 * is disabled (either by crop_en flags or by disabling the
+		 * entire block), the input size will instead be set to equal
+		 * ISP sink size (corresponding to output of IS). They are
+		 * clamped to the min/max sizes.
+		 */
+		struct v4l2_rect crop = {
+			.left = window->h_offs,
+			.top = window->v_offs,
+			.width = window->h_size,
+			.height = window->v_size
+		};
+
+		/*
+		 * Resizer (and dual crop if applicable) always needs to be
+		 * handled to do format conversion and resizing to achieve the
+		 * requested format type and size for the stream.
+		 */
+		if (!(arg->crop_en & en_mask) ||
+		    (flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) {
+			crop = rsz->sink_size;
+		}
+
+		/*
+		 * Resizer does not work in raw mode but dual crop does, so
+		 * make crop = source_size if we are in raw mode. If there is
+		 * no dual crop then whatever.
+		 *
+		 * TODO Use image stabilizer to do the correction in the case
+		 * that there is no dual crop. This logic should be in the
+		 * resizer code.
+		 */
+		if (!v4l2_is_format_yuv(rsz->source_fmt_info)) {
+			/* Match the crop size to the source size first */
+			if (crop.width != rsz->source_size.width || crop.height != rsz->source_size.height) {
+				crop.width = rsz->source_size.width;
+				crop.height = rsz->source_size.height;
+			}
+		}
+
+		/*
+		 * Out-of-bounds rectangles just produces garbage data and
+		 * doesn't break the hardware so don't correct for it.
+		 */
+
+		/*
+		 * TODO Invalidate invalid stream configurations in rkisp2_params_vb2_buf_prepare
+		 *
+		 * We should validate configurations that would cause
+		 * memory-related or hardware-breaking errors in buf_prepare
+		 * (like crop != source_size on raw mode). Other non-breaking
+		 * errors we can just forward the parameters to hardware as-is
+		 * and let userspace deal with the consequences. They shouldn't
+		 * be setting bad parameters (as long as it doesn't break the
+		 * system).
+		 */
+
+		rkisp2_resizer_configure(rsz, &crop);
+	}
+}
+
+static void rkisp2_params_crop(struct rkisp2_params *params,
+			       union rkisp2_params_block block)
+{
+	const struct rkisp2_params_crop *arg = block.crop;
+	__rkisp2_params_crop(params, arg, block.header->flags);
+}
+
+static void rkisp2_params_crop_init(struct rkisp2_params *params)
+{
+	struct rkisp2_device *rkisp2 = params->rkisp2;
+
+	/* Create an initial crop configuration to configure */
+	struct rkisp2_params_crop arg = {
+		.header.type = RKISP2_PARAMS_BLOCK_CROP,
+		.header.flags = 0,
+		.header.size = sizeof(struct rkisp2_params_crop),
+		.crop_en = RKISP2_ISP_CROP_ENABLE_MAIN | RKISP2_ISP_CROP_ENABLE_SELF,
+
+		.mp_crop.h_offs = 0,
+		.mp_crop.v_offs = 0,
+		.mp_crop.h_size = rkisp2->resizer_devs[0].source_size.width,
+		.mp_crop.v_size = rkisp2->resizer_devs[0].source_size.height,
+
+		.sp_crop.h_offs = 0,
+		.sp_crop.v_offs = 0,
+		.sp_crop.h_size = rkisp2->resizer_devs[1].source_size.width,
+		.sp_crop.v_size = rkisp2->resizer_devs[1].source_size.height
+	};
+
+	dev_dbg(rkisp2->dev,
+		"%s: set mp crop to %dx%d\n",
+		__func__, arg.mp_crop.h_size, arg.mp_crop.v_size);
+
+	__rkisp2_params_crop(params, &arg, V4L2_ISP_PARAMS_FL_BLOCK_ENABLE);
+}
+
+static void rkisp2_params_configure(struct rkisp2_params *params,
+				    struct rkisp2_params_buffer *buf,
+				    enum rkisp2_params_configure_priority prio)
+{
+	struct v4l2_isp_params_buffer *cfg;
+	const struct rkisp2_params_block_handler_info *info;
+	size_t block_offset = 0;
+	size_t max_offset;
+
+	buf->vb.sequence = params->rkisp2->isp.frame_sequence + 1;
+	cfg = buf->cfg;
+
+	max_offset = cfg->data_size;
+
+	/* Walk the list of parameter blocks and process them. */
+	while (max_offset && block_offset < max_offset) {
+		union rkisp2_params_block block;
+
+		/* \todo Check if we want to avoid this copy */
+		block.data = &cfg->data[block_offset];
+
+		block_offset += block.header->size;
+
+		info = &rkisp2_params_handlers[block.header->type];
+
+		if (prio != RKISP2_PARAMS_CONFIG_PRIO_NONE &&
+		    prio != info->priority)
+			continue;
+
+		info->handler(params, block);
+	}
+
+	/* update shadow register immediately */
+	rkisp2_param_set_bits(params, RKISP2_CIF_ISP_CTRL,
+			      RKISP2_CIF_ISP_CTRL_ISP_CFG_UPD);
+
+	if (prio == RKISP2_PARAMS_CONFIG_PRIO_NONE)
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+void rkisp2_params_isr(struct rkisp2_params *params)
+{
+	struct rkisp2_params_buffer *buf;
+
+	spin_lock(&params->buf_lock);
+	buf = list_first_entry_or_null(&params->params,
+				       struct rkisp2_params_buffer, queue);
+	if (buf)
+		list_del(&buf->queue);
+	spin_unlock(&params->buf_lock);
+
+	if (!buf)
+		return;
+
+	rkisp2_params_configure(params, buf, RKISP2_PARAMS_CONFIG_PRIO_NONE);
+}
+
+void rkisp2_params_pre_configure(struct rkisp2_params *params,
+				 enum rkisp2_fmt_raw_pat_type bayer_pat,
+				 const struct v4l2_mbus_framefmt *sink_frm,
+				 const struct v4l2_mbus_framefmt *mp_src_frm,
+				 const struct v4l2_mbus_framefmt *sp_src_frm)
+{
+	struct rkisp2_device *rkisp2 = params->rkisp2;
+	struct rkisp2_params_buffer *buf;
+
+	/* Save configuration information that params requires */
+
+	params->raw_type = bayer_pat;
+
+	rkisp2_resizer_pre_configure(rkisp2, sink_frm, mp_src_frm, sp_src_frm);
+
+	spin_lock_irq(&params->buf_lock);
+	buf = list_first_entry_or_null(&params->params,
+				       struct rkisp2_params_buffer, queue);
+	spin_unlock_irq(&params->buf_lock);
+
+	if (!buf) {
+		/*
+		 * Initialize crop in the event that are are no pre-queued
+		 * parameter buffers
+		 */
+		rkisp2_params_crop_init(params);
+		return;
+	}
+
+	rkisp2_params_configure(params, buf, RKISP2_PARAMS_CONFIG_PRIO_PRE);
+}
+
+void rkisp2_params_post_configure(struct rkisp2_params *params)
+{
+	struct rkisp2_params_buffer *buf;
+
+	spin_lock_irq(&params->buf_lock);
+	buf = list_first_entry_or_null(&params->params,
+				       struct rkisp2_params_buffer, queue);
+	if (buf)
+		list_del(&buf->queue);
+	spin_unlock_irq(&params->buf_lock);
+
+	if (!buf)
+		return;
+
+	rkisp2_params_configure(params, buf, RKISP2_PARAMS_CONFIG_PRIO_POST);
+}
+
+/*** V4L2 ***/
+
+static int rkisp2_params_enum_fmt_meta_out(struct file *file, void *fh,
+					   struct v4l2_fmtdesc *f)
+{
+	if (f->index)
+		return -EINVAL;
+
+	if (f->mbus_code && f->mbus_code != MEDIA_BUS_FMT_METADATA_FIXED)
+		return -EINVAL;
+
+	f->pixelformat = V4L2_META_FMT_RKISP2_PARAMS;
+
+	return 0;
+}
+
+static int rkisp2_params_g_fmt_meta_out(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+
+	static const struct v4l2_meta_format mfmt = {
+		.dataformat = V4L2_META_FMT_RKISP2_PARAMS,
+		.buffersize = v4l2_isp_buffer_size(RKISP2_PARAMS_MAX_SIZE),
+	};
+
+	f->fmt.meta = mfmt;
+
+	return 0;
+}
+
+static int rkisp2_params_querycap(struct file *file,
+				  void *priv, struct v4l2_capability *cap)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	strscpy(cap->driver, RKISP2_DRIVER_NAME, sizeof(cap->driver));
+	strscpy(cap->card, vdev->name, sizeof(cap->card));
+	strscpy(cap->bus_info, RKISP2_BUS_INFO, sizeof(cap->bus_info));
+
+	return 0;
+}
+
+/* ISP params video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp2_params_ioctl = {
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_enum_fmt_meta_out = rkisp2_params_enum_fmt_meta_out,
+	.vidioc_g_fmt_meta_out = rkisp2_params_g_fmt_meta_out,
+	.vidioc_s_fmt_meta_out = rkisp2_params_g_fmt_meta_out,
+	.vidioc_try_fmt_meta_out = rkisp2_params_g_fmt_meta_out,
+	.vidioc_querycap = rkisp2_params_querycap,
+};
+
+static int rkisp2_params_vb2_queue_setup(struct vb2_queue *vq,
+					 unsigned int *num_buffers,
+					 unsigned int *num_planes,
+					 unsigned int sizes[],
+					 struct device *alloc_devs[])
+{
+	/* \todo num_buffers? */
+
+	*num_planes = 1;
+
+	sizes[0] = v4l2_isp_buffer_size(RKISP2_PARAMS_MAX_SIZE);
+
+	return 0;
+}
+
+static int rkisp2_params_vb2_buf_init(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+
+	params_buf->cfg = kvmalloc(v4l2_isp_buffer_size(RKISP2_PARAMS_MAX_SIZE),
+				   GFP_KERNEL);
+	if (!params_buf->cfg)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void rkisp2_params_vb2_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+
+	kvfree(params_buf->cfg);
+	params_buf->cfg = NULL;
+}
+
+static void rkisp2_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct rkisp2_params *params = vq->drv_priv;
+
+	spin_lock_irq(&params->buf_lock);
+	list_add_tail(&params_buf->queue, &params->params);
+	spin_unlock_irq(&params->buf_lock);
+}
+
+static int rkisp2_params_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+	struct rkisp2_params *params = vb->vb2_queue->drv_priv;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rkisp2_params_buffer *params_buf = to_rkisp2_params_buffer(vbuf);
+	struct v4l2_isp_params_buffer *cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+	size_t payload_size = vb2_get_plane_payload(vb, 0);
+	int ret;
+
+	ret = v4l2_isp_params_validate_buffer_size(params->rkisp2->dev, vb,
+						   v4l2_isp_buffer_size(RKISP2_PARAMS_MAX_SIZE));
+	if (ret)
+		return ret;
+
+	/*
+	 * Copy the parameters buffer to the internal scratch buffer to avoid
+	 * userspace modifying the buffer content while the driver processes it.
+	 */
+	memcpy(params_buf->cfg, cfg, payload_size);
+
+	return v4l2_isp_params_validate_buffer(params->rkisp2->dev, vb, cfg,
+				rkisp2_params_block_types_info,
+				ARRAY_SIZE(rkisp2_params_block_types_info));
+}
+
+static void rkisp2_params_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct rkisp2_params *params = vq->drv_priv;
+	struct rkisp2_params_buffer *buf;
+	LIST_HEAD(tmp_list);
+
+	/*
+	 * we first move the buffers into a local list 'tmp_list'
+	 * and then we can iterate it and call vb2_buffer_done
+	 * without holding the lock
+	 */
+	spin_lock_irq(&params->buf_lock);
+	list_splice_init(&params->params, &tmp_list);
+	spin_unlock_irq(&params->buf_lock);
+
+	list_for_each_entry(buf, &tmp_list, queue)
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops rkisp2_params_vb2_ops = {
+	.queue_setup = rkisp2_params_vb2_queue_setup,
+	.buf_init = rkisp2_params_vb2_buf_init,
+	.buf_cleanup = rkisp2_params_vb2_buf_cleanup,
+	.buf_queue = rkisp2_params_vb2_buf_queue,
+	.buf_prepare = rkisp2_params_vb2_buf_prepare,
+	.stop_streaming = rkisp2_params_vb2_stop_streaming,
+};
+
+static const struct v4l2_file_operations rkisp2_params_fops = {
+	.mmap = vb2_fop_mmap,
+	.unlocked_ioctl = video_ioctl2,
+	.poll = vb2_fop_poll,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release
+};
+
+static int rkisp2_params_link_validate(struct media_link *link)
+{
+	/* \todo implement this */
+	return 0;
+}
+
+static const struct media_entity_operations rkisp2_params_media_ops = {
+	.link_validate = rkisp2_params_link_validate,
+};
+
+static int rkisp2_params_init_vb2_queue(struct vb2_queue *q,
+					struct rkisp2_params *params)
+{
+	struct rkisp2_vdev_node *node;
+
+	node = container_of(q, struct rkisp2_vdev_node, buf_queue);
+
+	q->type = V4L2_BUF_TYPE_META_OUTPUT;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = params;
+	q->ops = &rkisp2_params_vb2_ops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->buf_struct_size = sizeof(struct rkisp2_params_buffer);
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &node->vlock;
+
+	return vb2_queue_init(q);
+}
+
+int rkisp2_params_register(struct rkisp2_device *rkisp2)
+{
+	struct rkisp2_params *params = &rkisp2->params;
+	struct rkisp2_vdev_node *node = &params->vnode;
+	struct video_device *vdev = &node->vdev;
+	int ret;
+
+	params->rkisp2 = rkisp2;
+	mutex_init(&node->vlock);
+	INIT_LIST_HEAD(&params->params);
+	spin_lock_init(&params->buf_lock);
+
+	strscpy(vdev->name, RKISP2_PARAMS_DEV_NAME, sizeof(vdev->name));
+
+	video_set_drvdata(vdev, params);
+	vdev->ioctl_ops = &rkisp2_params_ioctl;
+	vdev->fops = &rkisp2_params_fops;
+	vdev->release = video_device_release_empty;
+	/*
+	 * Provide a mutex to v4l2 core. It will be used
+	 * to protect all fops and v4l2 ioctls.
+	 */
+	vdev->lock = &node->vlock;
+	vdev->v4l2_dev = &rkisp2->v4l2_dev;
+	vdev->queue = &node->buf_queue;
+	vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
+	vdev->entity.ops = &rkisp2_params_media_ops;
+	vdev->vfl_dir = VFL_DIR_TX;
+	ret = rkisp2_params_init_vb2_queue(vdev->queue, params);
+	if (ret)
+		goto err_media;
+
+	video_set_drvdata(vdev, params);
+
+	node->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+	if (ret)
+		goto err_media;
+
+	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+	if (ret) {
+		dev_err(rkisp2->dev,
+			"failed to register %s, ret=%d\n", vdev->name, ret);
+		return ret;
+	}
+
+	return 0;
+
+err_media:
+	media_entity_cleanup(&vdev->entity);
+	mutex_destroy(&node->vlock);
+	return ret;
+}
+
+void rkisp2_params_unregister(struct rkisp2_device *rkisp2)
+{
+	struct rkisp2_params *params = &rkisp2->params;
+	struct rkisp2_vdev_node *node = &params->vnode;
+	struct video_device *vdev = &node->vdev;
+
+	if (!video_is_registered(vdev))
+		return;
+
+	vb2_video_unregister_device(vdev);
+	media_entity_cleanup(&vdev->entity);
+	mutex_destroy(&node->vlock);
+}
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
index bb79d08d206f..d27d9a48887e 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
@@ -881,6 +881,15 @@
 #define ISP3X_LSC_YSIZE_CD			(ISP3X_LSC_BASE + 0x000A8)
 #define ISP3X_LSC_YSIZE_EF			(ISP3X_LSC_BASE + 0x000AC)
 
+#define ISP3X_LSC_XGRAD(n)		(n < 4 ? (ISP3X_LSC_XGRAD_01 + (n) * 4) : \
+						 (ISP3X_LSC_XGRAD_89 + ((n) - 4) * 4))
+#define ISP3X_LSC_YGRAD(n)		(n < 4 ? (ISP3X_LSC_YGRAD_01 + (n) * 4) : \
+						 (ISP3X_LSC_YGRAD_89 + ((n) - 4) * 4))
+#define ISP3X_LSC_XSIZE(n)		(n < 4 ? (ISP3X_LSC_XSIZE_01 + (n) * 4) : \
+						 (ISP3X_LSC_XSIZE_89 + ((n) - 4) * 4))
+#define ISP3X_LSC_YSIZE(n)		(n < 4 ? (ISP3X_LSC_YSIZE_01 + (n) * 4) : \
+						 (ISP3X_LSC_YSIZE_89 + ((n) - 4) * 4))
+
 #define ISP3X_DEBAYER_BASE			0x00002500
 #define ISP3X_DEBAYER_CONTROL			(ISP3X_DEBAYER_BASE + 0x00000)
 #define ISP3X_DEBAYER_G_INTERP			(ISP3X_DEBAYER_BASE + 0x00004)
@@ -3235,6 +3244,12 @@
 #define ISP33_IIR_WR_ID(x)		(((x) & 0x3f) << 16)
 #define ISP33_IIR_WR_CLEAR		BIT(24)
 
+/* GAMMA_OUT */
+#define ISP3X_GAMMA_OUT_CTRL_EN			BIT(0)
+#define ISP3X_GAMMA_OUT_CTRL_MODE_EQUIDISTANT	BIT(1)
+#define ISP3X_GAMMA_OUT_CTRL_SEGMENTS_48		BIT(2)
+#define ISP3X_GAMMA_OUT_SAMPLE(a, b)		(((a) & 0xfff) | (((b) & 0xfff) << 16))
+
 /* HDRTMO */
 
 /* HDRDRC */
@@ -3264,6 +3279,7 @@
 #define ISP3X_DPCC_WORKING		BIT(30)
 
 /* CCM */
+#define ISP3X_CCM_COEFF(a, b)		((a & 0x7ff) | (b << 16))
 #define ISP3X_CCM_HIGHY_ADJ_DIS		BIT(1)
 #define ISP32_CCM_ENH_ADJ_EN		BIT(2)
 #define ISP32_CCM_ASYM_ADJ_EN		BIT(3)
@@ -3288,6 +3304,21 @@
 #define ISP3X_LSC_TABLE_ADDRESS_0	0
 #define ISP3X_LSC_TABLE_ADDRESS_153	153
 
+#define ISP3X_LSC_CTRL_EN		BIT(0)
+
+#define ISP3X_LSC_ACTIVE_TABLE		BIT(1)
+
+#define ISP3X_LSC_LUT_EN		BIT(1)
+#define ISP3X_LSC_SECTOR_16X16		BIT(2)
+#define ISP3X_LSC_PRE_RD_ST_MODE	BIT(4)
+
+#define ISP3X_LSC_TABLE_DATA(v0, v1)     \
+	(((v0) & 0x1FFF) | (((v1) & 0x1FFF) << 16))
+#define ISP3X_LSC_SECT_SIZE(v0, v1)      \
+	(((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+#define ISP3X_LSC_GRAD_SIZE(v0, v1)      \
+	(((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+
 #define ISP3X_LSC_LUT_EN		BIT(1)
 #define ISP3X_LSC_SECTOR_16X16		BIT(2)
 #define ISP3X_LSC_PRE_RD_ST_MODE	BIT(4)
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-resizer.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-resizer.c
new file mode 100644
index 000000000000..6f9812f1ae84
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-resizer.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+/*
+ * Rockchip ISP2 Driver - Resizer
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2026 Ideas on Board Oy.
+ */
+
+#include "rkisp2-common.h"
+
+/*
+ * Although there is no resizer subdevice like rkisp1, there is significant
+ * control handling that this is split into a separate file. This includes
+ * handling of both the dual crop and resizer.
+ *
+ * In rkisp2, crop rectangles are set by parameter buffers and are thus handled
+ * by params.
+ */
+
+struct rkisp2_resizer_config {
+	int max_rsz_width;
+	int max_rsz_height;
+	int min_rsz_width;
+	int min_rsz_height;
+	struct {
+		u32 ctrl;
+		u32 ctrl_shd;
+		u32 update;
+		u32 src_size;
+		u32 dst_size;
+		u32 scale_hy_offs_mi;
+		u32 scale_hc_offs_mi;
+		u32 scale_in_crop_offs;
+		u32 scale_hy_offs;
+		u32 scale_hc_offs;
+		u32 scale_hy_size;
+		u32 scale_hc_size;
+		u32 scale_hy;
+		u32 scale_hcr;
+		u32 scale_hcb;
+		u32 scale_vy;
+		u32 scale_vc;
+		u32 scale_lut;
+		u32 scale_lut_addr;
+		u32 scale_hy_shd;
+		u32 scale_hcr_shd;
+		u32 scale_hcb_shd;
+		u32 scale_vy_shd;
+		u32 scale_vc_shd;
+		u32 phase_hy;
+		u32 phase_hc;
+		u32 phase_vy;
+		u32 phase_vc;
+		u32 phase_hy_shd;
+		u32 phase_hc_shd;
+		u32 phase_vy_shd;
+		u32 phase_vc_shd;
+	} rsz;
+	struct {
+		u32 ctrl;
+		u32 yuvmode_mask;
+		u32 rawmode_mask;
+		u32 h_offset;
+		u32 v_offset;
+		u32 h_size;
+		u32 v_size;
+	} dual_crop;
+};
+
+static const struct rkisp2_resizer_config rkisp2_resizer_config_mp = {
+	.rsz = {
+		.ctrl = RKISP2_CIF_MRSZ_CTRL,
+		.scale_hy = RKISP2_CIF_MRSZ_SCALE_HY,
+		.scale_hcr = RKISP2_CIF_MRSZ_SCALE_HCR,
+		.scale_hcb = RKISP2_CIF_MRSZ_SCALE_HCB,
+		.scale_vy = RKISP2_CIF_MRSZ_SCALE_VY,
+		.scale_vc = RKISP2_CIF_MRSZ_SCALE_VC,
+		.scale_lut = RKISP2_CIF_MRSZ_SCALE_LUT,
+		.scale_lut_addr = RKISP2_CIF_MRSZ_SCALE_LUT_ADDR,
+		.scale_hy_shd = RKISP2_CIF_MRSZ_SCALE_HY_SHD,
+		.scale_hcr_shd = RKISP2_CIF_MRSZ_SCALE_HCR_SHD,
+		.scale_hcb_shd = RKISP2_CIF_MRSZ_SCALE_HCB_SHD,
+		.scale_vy_shd = RKISP2_CIF_MRSZ_SCALE_VY_SHD,
+		.scale_vc_shd = RKISP2_CIF_MRSZ_SCALE_VC_SHD,
+		.phase_hy = RKISP2_CIF_MRSZ_PHASE_HY,
+		.phase_hc = RKISP2_CIF_MRSZ_PHASE_HC,
+		.phase_vy = RKISP2_CIF_MRSZ_PHASE_VY,
+		.phase_vc = RKISP2_CIF_MRSZ_PHASE_VC,
+		.ctrl_shd = RKISP2_CIF_MRSZ_CTRL_SHD,
+		.phase_hy_shd = RKISP2_CIF_MRSZ_PHASE_HY_SHD,
+		.phase_hc_shd = RKISP2_CIF_MRSZ_PHASE_HC_SHD,
+		.phase_vy_shd = RKISP2_CIF_MRSZ_PHASE_VY_SHD,
+		.phase_vc_shd = RKISP2_CIF_MRSZ_PHASE_VC_SHD,
+	},
+	.dual_crop = {
+		.ctrl = RKISP2_CIF_DUAL_CROP_CTRL,
+		.yuvmode_mask = RKISP2_CIF_DUAL_CROP_MP_MODE_YUV,
+		.rawmode_mask = RKISP2_CIF_DUAL_CROP_MP_MODE_RAW,
+		.h_offset = RKISP2_CIF_DUAL_CROP_M_H_OFFS,
+		.v_offset = RKISP2_CIF_DUAL_CROP_M_V_OFFS,
+		.h_size = RKISP2_CIF_DUAL_CROP_M_H_SIZE,
+		.v_size = RKISP2_CIF_DUAL_CROP_M_V_SIZE,
+	},
+};
+
+static const struct rkisp2_resizer_config rkisp2_resizer_config_sp = {
+	.rsz = {
+		.ctrl = RKISP2_CIF_SRSZ_CTRL,
+		.scale_hy = RKISP2_CIF_SRSZ_SCALE_HY,
+		.scale_hcr = RKISP2_CIF_SRSZ_SCALE_HCR,
+		.scale_hcb = RKISP2_CIF_SRSZ_SCALE_HCB,
+		.scale_vy = RKISP2_CIF_SRSZ_SCALE_VY,
+		.scale_vc = RKISP2_CIF_SRSZ_SCALE_VC,
+		.scale_lut = RKISP2_CIF_SRSZ_SCALE_LUT,
+		.scale_lut_addr = RKISP2_CIF_SRSZ_SCALE_LUT_ADDR,
+		.scale_hy_shd = RKISP2_CIF_SRSZ_SCALE_HY_SHD,
+		.scale_hcr_shd = RKISP2_CIF_SRSZ_SCALE_HCR_SHD,
+		.scale_hcb_shd = RKISP2_CIF_SRSZ_SCALE_HCB_SHD,
+		.scale_vy_shd = RKISP2_CIF_SRSZ_SCALE_VY_SHD,
+		.scale_vc_shd = RKISP2_CIF_SRSZ_SCALE_VC_SHD,
+		.phase_hy = RKISP2_CIF_SRSZ_PHASE_HY,
+		.phase_hc = RKISP2_CIF_SRSZ_PHASE_HC,
+		.phase_vy = RKISP2_CIF_SRSZ_PHASE_VY,
+		.phase_vc = RKISP2_CIF_SRSZ_PHASE_VC,
+		.ctrl_shd = RKISP2_CIF_SRSZ_CTRL_SHD,
+		.phase_hy_shd = RKISP2_CIF_SRSZ_PHASE_HY_SHD,
+		.phase_hc_shd = RKISP2_CIF_SRSZ_PHASE_HC_SHD,
+		.phase_vy_shd = RKISP2_CIF_SRSZ_PHASE_VY_SHD,
+		.phase_vc_shd = RKISP2_CIF_SRSZ_PHASE_VC_SHD,
+	},
+	.dual_crop = {
+		.ctrl = RKISP2_CIF_DUAL_CROP_CTRL,
+		.yuvmode_mask = RKISP2_CIF_DUAL_CROP_SP_MODE_YUV,
+		.rawmode_mask = RKISP2_CIF_DUAL_CROP_SP_MODE_RAW,
+		.h_offset = RKISP2_CIF_DUAL_CROP_S_H_OFFS,
+		.v_offset = RKISP2_CIF_DUAL_CROP_S_V_OFFS,
+		.h_size = RKISP2_CIF_DUAL_CROP_S_H_SIZE,
+		.v_size = RKISP2_CIF_DUAL_CROP_S_V_SIZE,
+	},
+};
+
+static bool rkisp2_rect_equals(const struct v4l2_rect *a,
+			       const struct v4l2_rect *b)
+{
+	return a->left == b->left && a->top == b->top &&
+	       a->width == b->width && a->height == b->height;
+}
+
+static void rkisp2_resizer_disable_dcrop(struct rkisp2_resizer *rsz)
+{
+	struct rkisp2_device *rkisp2 = rsz->rkisp2;
+	const struct rkisp2_resizer_config *config = rsz->config;
+
+	u32 mask = config->dual_crop.yuvmode_mask |
+		   config->dual_crop.rawmode_mask;
+	u32 ctrl = rkisp2_read(rkisp2, config->dual_crop.ctrl);
+
+	ctrl &= ~mask;
+	ctrl |= RKISP2_CIF_DUAL_CROP_CFG_UPD;
+	rkisp2_write(rkisp2, config->dual_crop.ctrl, ctrl);
+}
+
+static void rkisp2_resizer_config_dcrop(struct rkisp2_resizer *rsz,
+					bool is_raw,
+					const struct v4l2_rect *crop)
+{
+	struct rkisp2_device *rkisp2 = rsz->rkisp2;
+	const struct rkisp2_resizer_config *config = rsz->config;
+
+	u32 mask = config->dual_crop.yuvmode_mask |
+		   config->dual_crop.rawmode_mask;
+	u32 ctrl = rkisp2_read(rkisp2, config->dual_crop.ctrl);
+
+	rkisp2_write(rkisp2, config->dual_crop.h_offset, crop->left);
+	rkisp2_write(rkisp2, config->dual_crop.v_offset, crop->top);
+	rkisp2_write(rkisp2, config->dual_crop.h_size, crop->width);
+	rkisp2_write(rkisp2, config->dual_crop.v_size, crop->height);
+
+	ctrl &= ~mask;
+	if (is_raw)
+		ctrl |= config->dual_crop.rawmode_mask;
+	else
+		ctrl |= config->dual_crop.yuvmode_mask;
+	ctrl |= RKISP2_CIF_DUAL_CROP_CFG_UPD;
+	rkisp2_write(rkisp2, config->dual_crop.ctrl, ctrl);
+}
+
+static void rkisp2_resizer_disable_rsz(struct rkisp2_resizer *rsz)
+{
+	struct rkisp2_device *rkisp2 = rsz->rkisp2;
+	const struct rkisp2_resizer_config *config = rsz->config;
+
+	rkisp2_write(rkisp2, config->rsz.ctrl, 0);
+	rkisp2_write(rkisp2, config->rsz.ctrl, RKISP2_CIF_RSZ_CTRL_CFG_UPD);
+}
+
+static int rkisp2_mbus_code_xysubs(u32 code, u32 *xsubs, u32 *ysubs)
+{
+	switch (code) {
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_VYUY8_1X16:
+		*xsubs = 2;
+		*ysubs = 1;
+		return 0;
+	default:
+		*xsubs = 1;
+		*ysubs = 1;
+		return 0;
+	}
+}
+
+static void rkisp2_format_xysubs(const struct v4l2_format_info *info,
+				 u32 *xsubs, u32 *ysubs)
+{
+	if (info && v4l2_is_format_yuv(info)) {
+		*xsubs = info->hdiv ?: 1;
+		*ysubs = info->vdiv ?: 1;
+	} else {
+		*xsubs = 1;
+		*ysubs = 1;
+	}
+}
+
+static u32 rkisp2_scale_up_factor(u32 src, u32 dst)
+{
+	if (src <= 1 || dst <= 1)
+		return RKISP2_CIF_RSZ_SCALER_FACTOR;
+	return ((src - 1) * RKISP2_CIF_RSZ_SCALER_FACTOR) / (dst - 1);
+}
+
+static u32 rkisp2_scale_down_factor(u32 src, u32 dst)
+{
+	if (src <= 1 || dst <= 1)
+		return RKISP2_CIF_RSZ_SCALER_FACTOR;
+	return ((dst - 1) * RKISP2_CIF_RSZ_SCALER_FACTOR) / (src - 1) + 1;
+}
+
+static void rkisp2_resizer_prepare_lut(struct rkisp2_resizer *rsz)
+{
+	struct rkisp2_device *rkisp2 = rsz->rkisp2;
+	const struct rkisp2_resizer_config *config = rsz->config;
+	unsigned int i;
+
+	for (i = 0; i < 64; i++) {
+		rkisp2_write(rkisp2, config->rsz.scale_lut_addr, i);
+		rkisp2_write(rkisp2, config->rsz.scale_lut, i);
+	}
+}
+
+static void rkisp2_resizer_config_rsz(struct rkisp2_resizer *rsz,
+				      const struct v4l2_rect *in_rect,
+				      const struct v4l2_rect *out_rect,
+				      const struct v4l2_mbus_framefmt *src_fmt)
+{
+	struct rkisp2_device *rkisp2 = rsz->rkisp2;
+	const struct rkisp2_resizer_config *config = rsz->config;
+	struct v4l2_rect in_c, out_c;
+	u32 in_xsubs = 1, in_ysubs = 1;
+	u32 out_xsubs = 1, out_ysubs = 1;
+	u32 rsz_ctrl = 0;
+
+	rkisp2_mbus_code_xysubs(src_fmt->code, &in_xsubs, &in_ysubs);
+	rkisp2_format_xysubs(rsz->source_fmt_info, &out_xsubs, &out_ysubs);
+
+	in_c.width = DIV_ROUND_UP(in_rect->width, in_xsubs);
+	in_c.height = DIV_ROUND_UP(in_rect->height, in_ysubs);
+	out_c.width = DIV_ROUND_UP(out_rect->width, out_xsubs);
+	out_c.height = DIV_ROUND_UP(out_rect->height, out_ysubs);
+
+	rkisp2_write(rkisp2, config->rsz.phase_hy, 0);
+	rkisp2_write(rkisp2, config->rsz.phase_hc, 0);
+	rkisp2_write(rkisp2, config->rsz.phase_vy, 0);
+	rkisp2_write(rkisp2, config->rsz.phase_vc, 0);
+
+	rkisp2_resizer_prepare_lut(rsz);
+
+	if (in_rect->width != out_rect->width) {
+		u32 hy;
+
+		rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_HY_ENABLE;
+		if (in_rect->width < out_rect->width) {
+			hy = rkisp2_scale_up_factor(in_rect->width, out_rect->width);
+			rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_HY_UP;
+		} else {
+			hy = rkisp2_scale_down_factor(in_rect->width, out_rect->width);
+		}
+		rkisp2_write(rkisp2, config->rsz.scale_hy, hy);
+	}
+
+	if (in_c.width != out_c.width) {
+		u32 hc;
+
+		rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_HC_ENABLE;
+		if (in_c.width < out_c.width) {
+			hc = rkisp2_scale_up_factor(in_c.width, out_c.width);
+			rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_HC_UP;
+		} else {
+			hc = rkisp2_scale_down_factor(in_c.width, out_c.width);
+		}
+		rkisp2_write(rkisp2, config->rsz.scale_hcb, hc);
+		rkisp2_write(rkisp2, config->rsz.scale_hcr, hc);
+	}
+
+	if (in_rect->height != out_rect->height) {
+		u32 vy;
+
+		rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_VY_ENABLE;
+		if (in_rect->height < out_rect->height) {
+			vy = rkisp2_scale_up_factor(in_rect->height, out_rect->height);
+			rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_VY_UP;
+		} else {
+			vy = rkisp2_scale_down_factor(in_rect->height, out_rect->height);
+		}
+		rkisp2_write(rkisp2, config->rsz.scale_vy, vy);
+	}
+
+	if (in_c.height != out_c.height) {
+		u32 vc;
+
+		rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_VC_ENABLE;
+		if (in_c.height < out_c.height) {
+			vc = rkisp2_scale_up_factor(in_c.height, out_c.height);
+			rsz_ctrl |= RKISP2_CIF_RSZ_CTRL_SCALE_VC_UP;
+		} else {
+			vc = rkisp2_scale_down_factor(in_c.height, out_c.height);
+		}
+		rkisp2_write(rkisp2, config->rsz.scale_vc, vc);
+	}
+
+	rkisp2_write(rkisp2, config->rsz.ctrl,
+		rsz_ctrl | RKISP2_CIF_RSZ_CTRL_CFG_UPD);
+}
+
+void rkisp2_resizer_configure(struct rkisp2_resizer *rsz,
+			      struct v4l2_rect *crop)
+{
+	bool is_raw = v4l2_is_format_bayer(rsz->source_fmt_info);
+
+	dev_dbg(rsz->rkisp2->dev,
+		"%s: Configuring crop for path %d from sink size (%d,%d)/%dx%d, crop (%d,%d)/%dx%d, source size (%d,%d)/%dx%d\n",
+		__func__, rsz->id,
+		rsz->sink_size.left, rsz->sink_size.top, rsz->sink_size.width, rsz->sink_size.height,
+		crop->left, crop->top, crop->width, crop->height,
+		rsz->source_size.left, rsz->source_size.top, rsz->source_size.width, rsz->source_size.height);
+
+	/* Apparently dual crop works on raw (untested) but resizer does not */
+	if (rkisp2_has_feature(rsz->rkisp2, DUAL_CROP)) {
+		if (rkisp2_rect_equals(crop, &rsz->sink_size))
+			rkisp2_resizer_disable_dcrop(rsz);
+		else
+			rkisp2_resizer_config_dcrop(rsz, is_raw, crop);
+	}
+
+	if (crop->width == rsz->source_size.width && crop->height == rsz->source_size.height)
+		rkisp2_resizer_disable_rsz(rsz);
+	else
+		rkisp2_resizer_config_rsz(rsz, crop,
+					  &rsz->source_size,
+					  &rsz->source_format);
+}
+
+void rkisp2_resizer_pre_configure(struct rkisp2_device *rkisp2,
+				  const struct v4l2_mbus_framefmt *sink_frm,
+				  const struct v4l2_mbus_framefmt *mp_src_frm,
+				  const struct v4l2_mbus_framefmt *sp_src_frm)
+{
+	unsigned int i;
+
+	for (i = 0; i < 2; i++) {
+		struct rkisp2_resizer *rsz = &rkisp2->resizer_devs[i];
+		struct rkisp2_capture *cap = &rkisp2->capture_devs[i];
+		struct v4l2_pix_format_mplane *cap_fmt = &cap->pix.fmt;
+
+		rsz->sink_size.left = 0;
+		rsz->sink_size.top = 0;
+		rsz->sink_size.width = sink_frm->width;
+		rsz->sink_size.height = sink_frm->height;
+
+		rsz->source_size.left = 0;
+		rsz->source_size.top = 0;
+		rsz->source_size.width = cap_fmt->width;
+		rsz->source_size.height = cap_fmt->height;
+
+		rsz->source_format = (i == 0 ? *mp_src_frm : *sp_src_frm);
+		rsz->source_fmt_info = cap->pix.info;
+	}
+}
+
+void rkisp2_resizer_devs_init(struct rkisp2_device *rkisp2)
+{
+	unsigned int i;
+
+	for (i = 0; i < 2; i++) {
+		struct rkisp2_resizer *rsz = &rkisp2->resizer_devs[i];
+		rsz->rkisp2 = rkisp2;
+		rsz->id = i;
+		rsz->config = (i == RKISP2_MAINPATH ?
+			       &rkisp2_resizer_config_mp :
+			       &rkisp2_resizer_config_sp);
+	}
+}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 17ba1ae70735..2f2e8bf76168 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1467,6 +1467,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_META_FMT_RK_ISP1_PARAMS:	descr = "Rockchip ISP1 3A Parameters"; break;
 	case V4L2_META_FMT_RK_ISP1_STAT_3A:	descr = "Rockchip ISP1 3A Statistics"; break;
 	case V4L2_META_FMT_RK_ISP1_EXT_PARAMS:	descr = "Rockchip ISP1 Ext 3A Params"; break;
+	case V4L2_META_FMT_RKISP2_PARAMS:	descr = "Rockchip ISP2 3A Parameters"; break;
 	case V4L2_META_FMT_C3ISP_PARAMS:	descr = "Amlogic C3 ISP Parameters"; break;
 	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;
diff --git a/include/uapi/linux/rkisp2-config.h b/include/uapi/linux/rkisp2-config.h
index ce8093db5998..5f8c8a555003 100644
--- a/include/uapi/linux/rkisp2-config.h
+++ b/include/uapi/linux/rkisp2-config.h
@@ -8,6 +8,18 @@
 #ifndef _UAPI_RKISP2_CONFIG_H
 #define _UAPI_RKISP2_CONFIG_H
 
+#ifdef __KERNEL__
+#include <linux/build_bug.h>
+#endif /* __KERNEL__ */
+#include <linux/types.h>
+
+#include <linux/media/v4l2-isp.h>
+
+#define RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS   49
+
+#define RKISP2_ISP_LSC_SAMPLES_MAX		17
+#define RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX	16
+
 /**
  * enum rkisp2_isp_version - ISP variants
  *
@@ -17,4 +29,335 @@ enum rkisp2_isp_version {
 	RKISP3_V0 = 30,
 };
 
+/* See enum rkisp2_isp_goc_segments for logarithmic segment sizes */
+enum rkisp2_isp_goc_mode {
+	RKISP2_ISP_GOC_MODE_LOGARITHMIC,
+	RKISP2_ISP_GOC_MODE_EQUIDISTANT
+};
+
+/*
+ * The segments are:
+ * 1 x8, 2 x4, 4 x4, 8 x4, 16 x4, 32 x4, 64 x4, 128 x4, 256 x4, 512 x4
+ * In 48-segment mode, the last group of 512 x4 becomes 256 x8
+ */
+enum rkisp2_isp_goc_segments {
+	RKISP2_ISP_GOC_SEGMENTS_44,
+	RKISP2_ISP_GOC_SEGMENTS_48
+};
+
+enum rkisp2_isp_lsc_config {
+	RKISP2_ISP_LSC_CONFIG_8X8,
+	RKISP2_ISP_LSC_CONFIG_16X16
+};
+
+enum rkisp2_isp_set_active_table_when {
+	RKISP2_ISP_LSC_SET_ACTIVE_TABLE_AFTER,
+	RKISP2_ISP_LSC_SET_ACTIVE_TABLE_BEFORE,
+};
+
+enum rkisp2_isp_crop_en {
+	RKISP2_ISP_CROP_ENABLE_MAIN = 0x1,
+	RKISP2_ISP_CROP_ENABLE_SELF = 0x2,
+};
+
+/*---------- Parameters ------------*/
+
+/**
+ * enum rkisp2_params_block_type - RkISP2 extensible params block types
+ *
+ * @RKISP2_PARAMS_BLOCK_BLS: Black level subtraction
+ * @RKISP2_PARAMS_BLOCK_AWB_GAINS: AWB gains
+ * @RKISP2_PARAMS_BLOCK_CSM: Color conversion coefficients (in the ISP block)
+ * @RKISP2_PARAMS_BLOCK_CCM: Color correction matrix (in the CCM block)
+ * @RKISP2_PARAMS_BLOCK_GOC: Gamma out correction
+ * @RKISP2_PARAMS_BLOCK_LSC: Lens shading correction
+ * @RKISP2_PARAMS_BLOCK_CROP: Crop config
+ */
+enum rkisp2_params_block_type {
+	RKISP2_PARAMS_BLOCK_BLS,
+	RKISP2_PARAMS_BLOCK_AWB_GAINS,
+	RKISP2_PARAMS_BLOCK_CSM,
+	RKISP2_PARAMS_BLOCK_CCM,
+	RKISP2_PARAMS_BLOCK_GOC,
+	RKISP2_PARAMS_BLOCK_LSC,
+	RKISP2_PARAMS_BLOCK_CROP,
+};
+
+/**
+ * struct rkisp2_isp_window -  measurement window.
+ *
+ * Measurements are calculated per window inside the frame.
+ * This struct represents a window for a measurement.
+ *
+ * @h_offs: the horizontal offset of the window from the left of the frame in pixels.
+ * @v_offs: the vertical offset of the window from the top of the frame in pixels.
+ * @h_size: the horizontal size of the window in pixels
+ * @v_size: the vertical size of the window in pixels.
+ */
+struct rkisp2_isp_window {
+	__u16 h_offs;
+	__u16 v_offs;
+	__u16 h_size;
+	__u16 v_size;
+};
+
+/**
+ * struct rkisp2_isp_bls_fixed_val - BLS fixed subtraction values
+ *
+ * These are signed 13-bit (-4096 to +4095).
+ *
+ * @a: Fixed black level value for Bayer channel 0
+ * @b: Fixed black level value for Bayer channel 1
+ * @c: Fixed black level value for Bayer channel 2
+ * @d: Fixed black level value for Bayer channel 3
+ */
+struct rkisp2_isp_bls_fixed_val {
+	__s16 a;
+	__s16 b;
+	__s16 c;
+	__s16 d;
+};
+
+/**
+ * struct rkisp2_isp_awb_gains - Auto white balance gain in the ISP block
+ *
+ * All fields in this struct are 16 bit, where:
+ * 0x100h = 1, unsigned integer value, range 0 to 63 with 8 bit fractional part.
+ *
+ * This leaves the upper two msb unaccounted for; it is unknown if these are
+ * unused or misdocumented.
+ *
+ * TODO investigate the upper two bits
+ *
+ * @r: gain value for red component.
+ * @gr: gain value for green component in red line.
+ * @b: gain value for blue component.
+ * @gb: gain value for green component in blue line.
+ */
+struct rkisp2_isp_awb_gains {
+	__u16 r;
+	__u16 gr;
+	__u16 b;
+	__u16 gb;
+};
+
+/**
+ * struct rkisp2_params_bls - RkISP2 params BLS config
+ *
+ * RkISP2 parameters Black Level Subtraction configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_BLS`.
+ *
+ * TODO Check if auto-mode and window selection is for both blocks or just for
+ * one block (it might be the same as 2.x)
+ *
+ * @header: The RkISP2 parameters block header
+ * @enable_auto: Automatic mode activated means that the measured values
+ *		 are subtracted. Otherwise the fixed subtraction
+ *		 values will be subtracted.
+ * @enabled_windows: enabled window (bit 0 for window 1, bit 1 for window 2)
+ * @bls_window1: Measurement window 1 size
+ * @bls_window2: Measurement window 2 size
+ * @bls_samples: Set amount of measured pixels for each Bayer position
+ *		 (A, B, C and D) to 2^bls_samples. (TODO needs confirmation)
+ * @bls_fixed_val: Black Level Subtraction fixed values for the BLS module at
+ * 		   the front of the pipeline
+ * @bls1_fixed_val: Black Level Subtraction fixed values for the BLS module after
+ * 		    bayer noise reduction
+ */
+struct rkisp2_params_bls {
+	struct v4l2_isp_params_block_header header;
+	__u8 enable_auto;
+	__u8 enabled_windows;
+	struct rkisp2_isp_window bls_window1;
+	struct rkisp2_isp_window bls_window2;
+	__u8 bls_samples;
+	struct rkisp2_isp_bls_fixed_val bls_fixed_val;
+	struct rkisp2_isp_bls_fixed_val bls1_fixed_val;
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_awb_gains - RKISP2 params AWB gains config
+ *
+ * RkISP2 parameters auto white balance gains configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_AWB_GAINS`.
+ *
+ * TODO investigate what the different blocks mean
+ *
+ * Block 0 is equivalent to the awb gains block on 2.x, but blocks 1 and
+ * 2 do not exist on 2.x.
+ *
+ * @header: The RkISP2 parameters block header
+ * @gains: Gains configuration for block i
+ */
+struct rkisp2_params_awb_gains {
+	struct v4l2_isp_params_block_header header;
+	struct rkisp2_isp_awb_gains gains[3];
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_csm - Configuration used by Color Space Conversion
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_CSM`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @coeff: color correction matrix. Values are 9-bit signed fixed-point numbers with 2 bit integer
+ *		and 7 bit fractional part, ranging from -2 (0x100) to +1.992 (0x0FF). 0 is
+ *		represented by 0x000 and a coefficient value of 1 as 0x080.
+ */
+struct rkisp2_params_csm {
+	struct v4l2_isp_params_block_header header;
+	__u16 coeff[3][3];
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_ccm - Configuration used by Color Correction Matrix
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_CCM`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @high_y_alpha_adj_en: Enable CCM high Y alpha adjustment (TODO figure out what this does)
+ * @coeff: color correction matrix. Values are 11-bit signed fixed-point numbers with 4 bit integer
+ *		and 7 bit fractional part, ranging from -8 (0x400) to +7.992 (0x3FF). 0 is
+ *		represented by 0x000 and a coefficient value of 1 as 0x080. The
+ *		value is expanded 128 times (TODO figure out what this means).
+ * @offset: Red, Green, Blue offsets for the color correction matrix. 12-bits
+ *	    wide ranging from -4096 to 4095, but only for red; green and blue are 11-bit
+ *	    signed fixed-point like coeff, but are still 12-bits wide.
+ * @y_coeff: Red, Green, Blue coefficients for RGB2Y calculation. red and green
+ *	     are 11-bits wide and blue is 12-bits wide. The value is expanded 128 times.
+ * @alp: CCM curve y-axis point definition for ccm input pixel's luminance.
+ *       11-bit unsigned ranging from 0 to 1024. The value is expanded 128 times.
+ * @inflection_point: Inflection point of the ccm alpha interpolation curve.
+ * 		      The inflection point is 2^inflection_point. Since the maximum y-value is
+ * 		      1024, the maximum value of this field is expected to be 10 (0xa), but the
+ * 		      documentation says 4'b10.
+ */
+struct rkisp2_params_ccm {
+	struct v4l2_isp_params_block_header header;
+	__u8 high_y_alpha_adj_en;
+	__u16 coeff[3][3];
+	__u16 offset[3];
+	__u16 y_coeff[3];
+	__u16 alp[17];
+	__u8 inflection_point;
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_goc - Configuration used by Gamma Out correction
+ *
+ * RkISP2 parameters gamma out correction configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_GOC`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @mode: goc mode (from enum rkisp2_isp_goc_mode)
+ * @segments: segments mode (from enum rkisp2_isp_goc_segments)
+ * @offset: offset value of the gamma out curve
+ * @gamma_y: gamma out curve y-axis for all color components
+ *
+ * The number of entries of @gamma_y depends on the segments mode. The entries
+ * are 12-bit unsigned.
+ */
+struct rkisp2_params_goc {
+	struct v4l2_isp_params_block_header header;
+	__u8 mode;
+	__u8 segments;
+	__u16 offset;
+	__u16 gamma_y[RKISP2_ISP_GAMMA_OUT_MAX_SEGMENTS];
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_lsc - Configuration used by Lens shading correction
+ *
+ * RkISP2 parameters lens shading correction configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_LSC`.
+ *
+ * The LSC module on the rkisp2 two tables: the 0th table and the 1th table.
+ * They can be programmed independently and (somewhat) simultaneously, and can be
+ * swapped by setting a single register. Hence the UAPI here is designed so
+ * that all these components can be controlled independently.
+ *
+ * In the first dimension of {r,gr,gb,b}_data_tbl we can designate which table
+ * to write the data to. write_table is then used to signal whether to write
+ * the data, and this can be controlled for both tables. active_table chooses
+ * which table to activate. set_active_table_when signals whether to set the
+ * active_table before or after programming the table. This allows
+ * optimizations such as setting a future table in one parameter buffer while
+ * swapping before setting it.
+ *
+ * This design gives us more control. For example, if we want to only program
+ * the 0th table without modifying the 1th table, we do not need to also
+ * populate the 1th table and we can use write_table to designate that we only
+ * want to program the 0th table. We can also swap tables without needing to
+ * re-populate the tables by setting active_table and unsetting write_table.
+ *
+ * {x,y}_sizes designates the grid of the LSC, and the table entries above
+ * correspond to the *vertices* of the grid. {x,y}_grads control the bilinear
+ * interpolation within the grid.
+ *
+ * @header: The RkISP2 parameters block header
+ * @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
+ * @write_table: Set to 1 to signal to write the respective table from above
+ * @active_table: Choose which of the two tables is active (0 or 1)
+ * @set_active_table_when: From rkisp2_isp_set_active_table_when; switch to the
+ *			   active table before or after programming the table
+ * @x_sizes: Sizes x
+ * @y_sizes: Sizes y
+ * @x_grads: Gradients x
+ * @y_grads: Gradients y
+ * @window_mode: From enum rkisp2_isp_lsc_config
+ */
+struct rkisp2_params_lsc {
+	struct v4l2_isp_params_block_header header;
+
+	__u16 r_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+	__u16 gr_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+	__u16 gb_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+	__u16 b_data_tbl[2][RKISP2_ISP_LSC_SAMPLES_MAX][RKISP2_ISP_LSC_SAMPLES_MAX];
+	__u8 write_table[2];
+	__u8 active_table;
+	__u8 set_active_table_when;
+
+	__u16 x_sizes[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+	__u16 y_sizes[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+	__u16 x_grads[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+	__u16 y_grads[RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX];
+
+	__u8 window_mode;
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_crop - Crop configuration
+ *
+ * RkISP2 parameters crop configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_CROP`.
+ * This block is responsible for configuring main and self path crops. The
+ * driver will automatically compute the scaling factors for the resizer and
+ * any format conversions (eg. for converting subsampling factors).
+ *
+ * @header: The RkISP2 parameters block header
+ * @crop_en: Enable bits for main path and self path crop below. Comes from enum rkisp2_isp_crop_en
+ * @mp_crop: Crop window for the main path, relative to the ISP source format size
+ * @sp_crop: Crop window for the self path, relative to the ISP source format size
+ */
+struct rkisp2_params_crop {
+	struct v4l2_isp_params_block_header header;
+	__u8 crop_en;
+	struct rkisp2_isp_window mp_crop;
+	struct rkisp2_isp_window sp_crop;
+} __attribute__((aligned(8)));
+
+#define RKISP2_PARAMS_MAX_SIZE					\
+	(sizeof(struct rkisp2_params_bls)			+\
+	sizeof(struct rkisp2_params_awb_gains)			+\
+	sizeof(struct rkisp2_params_csm)			+\
+	sizeof(struct rkisp2_params_ccm)			+\
+	sizeof(struct rkisp2_params_goc)			+\
+	sizeof(struct rkisp2_params_lsc)			+\
+	sizeof(struct rkisp2_params_crop))
+
 #endif /* _UAPI_RKISP2_CONFIG_H */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 5373dba640fa..b258422288b0 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -876,6 +876,9 @@ struct v4l2_pix_format {
 #define V4L2_META_FMT_RK_ISP1_STAT_3A	v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
 #define V4L2_META_FMT_RK_ISP1_EXT_PARAMS	v4l2_fourcc('R', 'K', '1', 'E') /* Rockchip ISP1 3a Extensible Parameters */
 
+/* Vendor specific - used for RKISP2 camera sub-system */
+#define V4L2_META_FMT_RKISP2_PARAMS	v4l2_fourcc('R', 'K', '2', 'P') /* Rockchip ISP2 Parameters */
+
 /* Vendor specific - used for C3_ISP */
 #define V4L2_META_FMT_C3ISP_PARAMS	v4l2_fourcc('C', '3', 'P', 'M') /* Amlogic C3 ISP Parameters */
 #define V4L2_META_FMT_C3ISP_STATS	v4l2_fourcc('C', '3', 'S', 'T') /* Amlogic C3 ISP Statistics */
-- 
2.47.2


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* [PATCH v2 5/5] media: rkisp2: Add statistics capture video node
  2026-08-24  9:03 [PATCH v2 0/5] media: rockchip: rkisp2: Add driver for ISP on Rk3588 Paul Elder
                   ` (2 preceding siblings ...)
  2026-08-24  9:04 ` [PATCH v2 4/5] media: rkisp2: Add parameters output video node Paul Elder
@ 2026-08-24  9:04 ` Paul Elder
  3 siblings, 0 replies; 9+ messages in thread
From: Paul Elder @ 2026-08-24  9:04 UTC (permalink / raw)
  To: laurent.pinchart
  Cc: Paul Elder, michael.riesch, xuhf, stefan.klug, mehdi.djait,
	linux-media, linux-arm-kernel, linux-rockchip, linux-kernel

Implement support for getting statistics out of the ISP by dequeueing
statistics buffers from rkisp2.

Support for configuring the statistics engines in the ISP is added as
well, implemented as configurations in parameter buffers.

Support for the following statistics engines is added:
- AE (auto exposure) lite
- Histogram lite
- Histogram big
- AWB (auto white balance)

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---

I've kept the debug counters that helped me debug interrupts for the
stats engines.

---
Changes in v2:
- redesign stats accumulation
  - rfc/v1 checked imsc at v_start to see what stats we expect and send
    them to userspace only after we receive all of them (or at next
    v_start if that comes first)
    - stats interrupt*s* come between v_start and input formatter
  - v2 we just send whatever stats came at end-of-frame time
    - this is guaranteed to be after all stats interrupts have come
- fix memops
- use extensible stats
---
 .../media/v4l/metafmt-rkisp2.rst              |  24 +-
 .../media/platform/rockchip/rkisp2/Makefile   |   3 +-
 .../platform/rockchip/rkisp2/rkisp2-common.h  |  56 ++
 .../platform/rockchip/rkisp2/rkisp2-debug.c   |  14 +
 .../platform/rockchip/rkisp2/rkisp2-dev.c     |  13 +
 .../platform/rockchip/rkisp2/rkisp2-isp.c     |   2 +
 .../platform/rockchip/rkisp2/rkisp2-params.c  | 238 ++++++++
 .../rockchip/rkisp2/rkisp2-regs-v2x.h         |  39 +-
 .../rockchip/rkisp2/rkisp2-regs-v3x.h         |  14 +
 .../platform/rockchip/rkisp2/rkisp2-stats.c   | 508 ++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c          |   1 +
 include/uapi/linux/rkisp2-config.h            | 244 ++++++++-
 include/uapi/linux/videodev2.h                |   1 +
 13 files changed, 1151 insertions(+), 6 deletions(-)
 create mode 100644 drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c

diff --git a/Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst b/Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst
index e0ca6cbed642..ab2e020071d9 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-rkisp2.rst
@@ -1,10 +1,11 @@
 .. SPDX-License-Identifier: GPL-2.0
 
 .. _v4l2-meta-fmt-rkisp2-params:
+.. _v4l2-meta-fmt-rkisp2-stats:
 
-************************************
-V4L2_META_FMT_RKISP2_PARAMS ('RK2P')
-************************************
+*************************************************************************
+V4L2_META_FMT_RKISP2_PARAMS ('RK2P'), V4L2_META_FMT_RKISP2_STATS ('RK2S')
+*************************************************************************
 
 Configuration Parameters
 ========================
@@ -64,6 +65,23 @@ first member and userspace must populate the type member with a value from
 
 	params->data_size += sizeof(struct rkisp2_params_bls);
 
+3A Statistics
+=============
+
+The ISP device collects different statistics over an input bayer frame. Those
+statistics can be obtained by userspace from the
+:ref:`rkisp2_stats <rkisp2_stats>` metadata capture video node, using
+the :c:type:`v4l2_meta_format` interface. The buffer contains a single instance
+of the C structure :c:type:`rkisp2_stats_buffer` defined in
+``rkisp2-config.h``, so the structure can be obtained from the buffer by:
+
+.. code-block:: C
+
+	struct rkisp2_stats_buffer *stats =
+		(struct rkisp2_stats_buffer *)buf;
+
+For details of the statistics see :c:type:`rkisp2_stats_buffer`.
+
 rkisp2 uAPI data types
 ======================
 
diff --git a/drivers/media/platform/rockchip/rkisp2/Makefile b/drivers/media/platform/rockchip/rkisp2/Makefile
index 2d41b4ab8f7c..1f92a8d45cfe 100644
--- a/drivers/media/platform/rockchip/rkisp2/Makefile
+++ b/drivers/media/platform/rockchip/rkisp2/Makefile
@@ -6,7 +6,8 @@ rockchip-isp2-y := rkisp2-capture.o \
            rkisp2-dmarx.o \
            rkisp2-isp.o \
            rkisp2-params.o \
-	   rkisp2-resizer.o
+	   rkisp2-resizer.o \
+           rkisp2-stats.o
 
 rockchip-isp2-$(CONFIG_DEBUG_FS) += rkisp2-debug.o
 
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
index 0479a24fa7f9..7b9eb03fa50e 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-common.h
@@ -105,6 +105,7 @@ enum rkisp2_isp_pad {
 	RKISP2_ISP_PAD_SINK_PARAMS,
 	RKISP2_ISP_PAD_SOURCE_VIDEO_MAIN,
 	RKISP2_ISP_PAD_SOURCE_VIDEO_SELF,
+	RKISP2_ISP_PAD_SOURCE_STATS,
 	RKISP2_ISP_PAD_MAX
 };
 
@@ -320,6 +321,48 @@ struct rkisp2_capture {
 	} pix;
 };
 
+/*
+ * struct rkisp2_stats - ISP Statistics device
+ *
+ * @vnode:	  video node
+ * @rkisp2:	  pointer to the rkisp2 device
+ * @lock:	  locks the buffer list 'stat'
+ * @stat:	  queue of rkisp2_buffer
+ * @vdev_fmt:	  v4l2_format of the metadata format
+ * @icr:	  interrupts that the stats has handled for the current frame (for stats 3a)
+ * @cur_buf:	  the current buffer to accumulate stats over multiple interrupts
+ *
+ * The icr field tracks which stats have been handled and whose interrupts will
+ * be cleared at end-of-frame when the stats are being handled.
+ *
+ * The stats are handled at end-of-frame as opposed to on the stats3a
+ * interrupts as there is one interrupt per stats block that has completed, and
+ * as we don't yet support returning partial stats to userspace, it's not
+ * really worth accumulating them separately. Thus we can simply accumulate
+ * whichever stats blocks have completed by checking their respective done bits
+ * (except ae_lite, whose bit is never set) at end-of-frame time.
+ *
+ * A note on the per-block stats3a interrupts: There seem to be separate
+ * interrupts in stats 3a for big modules and non-big modules. The conditions
+ * that cause separate interrupts are not yet fully understood, but is
+ * hypothesized to be big-mode & different window size compared to lite mode.
+ * The core stats (in the main ISP register space, similar to the rkisp1) are
+ * hypothesized to not exist.
+ */
+struct rkisp2_stats {
+	struct rkisp2_vdev_node vnode;
+	struct rkisp2_device *rkisp2;
+
+	spinlock_t lock; /* locks the buffers list 'stats' */
+	struct list_head stat;
+	struct v4l2_format vdev_fmt;
+
+	u32 icr;
+	struct rkisp2_buffer *cur_buf;
+
+	unsigned int awb_window_offset;
+};
+
 /*
  * struct rkisp2_params - ISP input parameters device
  *
@@ -384,9 +427,16 @@ struct rkisp2_debug {
 	unsigned long irq_delay;
 	unsigned long mipi_error;
 	unsigned long stats_error;
+	unsigned long stats3a_irq;
 	unsigned long stop_timeout[2];
 	unsigned long frame_drop[2];
 	unsigned long complete_frames;
+	unsigned long stats3a_hist_ch0_count;
+	unsigned long stats3a_hist_ch1_count;
+	unsigned long stats3a_hist_ch2_count;
+	unsigned long stats3a_hist_big_count;
+	unsigned long stats3a_awb_count;
+	unsigned long stats3a_awb_done_count;
 };
 
 /*
@@ -408,6 +458,7 @@ struct rkisp2_debug {
  * @capture_devs:  capture devices
  * @dmarx:	   ISP memory read device
  * @params:	   ISP parameters metadata output device
+ * @stats:	   ISP statistics metadata capture device
  * @pipe:	   media pipeline
  * @stream_lock:   serializes {start/stop}_streaming callbacks between the capture devices.
  * @debug:	   debug params to be exposed on debugfs
@@ -431,6 +482,7 @@ struct rkisp2_device {
 	struct rkisp2_capture capture_devs[2];
 	struct rkisp2_dmarx dmarx;
 	struct rkisp2_params params;
+	struct rkisp2_stats stats;
 	struct media_pipeline pipe;
 	struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */
 	struct rkisp2_debug debug;
@@ -521,6 +573,7 @@ irqreturn_t rkisp2_capture_isr(int irq, void *ctx);
 irqreturn_t rkisp2_mipi_isr(int irq, void *ctx);
 void rkisp2_dmarx_isr(struct rkisp2_device *rkisp2, u32 status);
 void rkisp2_params_isr(struct rkisp2_params *params);
+irqreturn_t rkisp2_stats_isr_eof(struct rkisp2_stats *);
 
 /* register/unregisters functions of the entities */
 int rkisp2_capture_devs_register(struct rkisp2_device *rkisp2);
@@ -548,6 +601,9 @@ void rkisp2_resizer_pre_configure(struct rkisp2_device *rkisp2,
 				  const struct v4l2_mbus_framefmt *sp_src_frm);
 void rkisp2_resizer_devs_init(struct rkisp2_device *rkisp2);
 
+int rkisp2_stats_register(struct rkisp2_device *rkisp2);
+void rkisp2_stats_unregister(struct rkisp2_device *rkisp2);
+
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 void rkisp2_debug_init(struct rkisp2_device *rkisp2);
 void rkisp2_debug_cleanup(struct rkisp2_device *rkisp2);
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
index 4f439bd9156b..0788fc12269b 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-debug.c
@@ -191,6 +191,8 @@ void rkisp2_debug_init(struct rkisp2_device *rkisp2)
 			     &debug->mipi_error);
 	debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir,
 			     &debug->stats_error);
+	debugfs_create_ulong("stats3a_irq", 0444, debug->debugfs_dir,
+			     &debug->stats3a_irq);
 	debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir,
 			     &debug->stop_timeout[RKISP2_MAINPATH]);
 	debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir,
@@ -201,6 +203,18 @@ void rkisp2_debug_init(struct rkisp2_device *rkisp2)
 			     &debug->frame_drop[RKISP2_SELFPATH]);
 	debugfs_create_ulong("complete_frames", 0444, debug->debugfs_dir,
 			     &debug->complete_frames);
+	debugfs_create_ulong("stats3a_hist_ch0_count", 0444, debug->debugfs_dir,
+			     &debug->stats3a_hist_ch0_count);
+	debugfs_create_ulong("stats3a_hist_ch1_count", 0444, debug->debugfs_dir,
+			     &debug->stats3a_hist_ch1_count);
+	debugfs_create_ulong("stats3a_hist_ch2_count", 0444, debug->debugfs_dir,
+			     &debug->stats3a_hist_ch2_count);
+	debugfs_create_ulong("stats3a_hist_big_count", 0444, debug->debugfs_dir,
+			     &debug->stats3a_hist_big_count);
+	debugfs_create_ulong("stats3a_awb_count", 0444, debug->debugfs_dir,
+			     &debug->stats3a_awb_count);
+	debugfs_create_ulong("stats3a_awb_done_count", 0444, debug->debugfs_dir,
+			     &debug->stats3a_awb_done_count);
 	debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp2,
 			    &rkisp2_debug_input_status_fops);
 
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
index 6fc0b6044121..1c98f3a3d155 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-dev.c
@@ -131,12 +131,21 @@ static int rkisp2_create_links(struct rkisp2_device *rkisp2)
 	if (ret)
 		return ret;
 
+	/* stats links */
+	ret = media_create_pad_link(&rkisp2->isp.sd.entity,
+				    RKISP2_ISP_PAD_SOURCE_STATS,
+				    &rkisp2->stats.vnode.vdev.entity, 0,
+				    MEDIA_LNK_FL_ENABLED |
+				    MEDIA_LNK_FL_IMMUTABLE);
+	if (ret)
+		return ret;
 
 	return 0;
 }
 
 static void rkisp2_entities_unregister(struct rkisp2_device *rkisp2)
 {
+	rkisp2_stats_unregister(rkisp2);
 	rkisp2_params_unregister(rkisp2);
 	rkisp2_dmarx_unregister(rkisp2);
 	rkisp2_capture_devs_unregister(rkisp2);
@@ -165,6 +174,10 @@ static int rkisp2_entities_register(struct rkisp2_device *rkisp2)
 	if (ret)
 		goto error;
 
+	ret = rkisp2_stats_register(rkisp2);
+	if (ret)
+		goto error;
+
 	ret = rkisp2_create_links(rkisp2);
 	if (ret)
 		goto error;
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
index 00336c5e99f3..0833ffe57f39 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-isp.c
@@ -738,6 +738,7 @@ int rkisp2_isp_register(struct rkisp2_device *rkisp2)
 	pads[RKISP2_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
 	pads[RKISP2_ISP_PAD_SOURCE_VIDEO_MAIN].flags = MEDIA_PAD_FL_SOURCE;
 	pads[RKISP2_ISP_PAD_SOURCE_VIDEO_SELF].flags = MEDIA_PAD_FL_SOURCE;
+	pads[RKISP2_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
 
 	ret = media_entity_pads_init(&sd->entity, RKISP2_ISP_PAD_MAX, pads);
 	if (ret)
@@ -829,6 +830,7 @@ irqreturn_t rkisp2_isp_isr(int irq, void *ctx)
 	if (status & RKISP2_CIF_ISP_FRAME) {
 		rkisp2->debug.complete_frames++;
 		rkisp2_params_isr(&rkisp2->params);
+		rkisp2_stats_isr_eof(&rkisp2->stats);
 	}
 
 	rkisp2_write(rkisp2, RKISP2_CIF_ISP_ICR, status);
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
index 2fac73709ae4..7848e234eece 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-params.c
@@ -34,6 +34,10 @@
 		.priority = RKISP2_PARAMS_CONFIG_PRIO_ ## prio_postfix, \
 	}
 
+/*
+ * We only need one instance of hist_big here since it's only for accessing the
+ * members of the union
+ */
 union rkisp2_params_block {
 	const struct v4l2_isp_params_block_header *header;
 	const struct rkisp2_params_bls *bls;
@@ -43,6 +47,10 @@ union rkisp2_params_block {
 	const struct rkisp2_params_goc *goc;
 	const struct rkisp2_params_lsc *lsc;
 	const struct rkisp2_params_crop *crop;
+	const struct rkisp2_params_ae_lite *ae_lite;
+	const struct rkisp2_params_hist_lite *hist_lite;
+	const struct rkisp2_params_hist_big *hist_big;
+	const struct rkisp2_params_awb_meas *awb_meas;
 	const __u8 *data;
 };
 
@@ -60,6 +68,14 @@ static void rkisp2_params_lsc(struct rkisp2_params *params,
 			      union rkisp2_params_block block);
 static void rkisp2_params_crop(struct rkisp2_params *params,
 			       union rkisp2_params_block block);
+static void rkisp2_params_ae_lite(struct rkisp2_params *params,
+				  union rkisp2_params_block block);
+static void rkisp2_params_hist_lite(struct rkisp2_params *params,
+				    union rkisp2_params_block block);
+static void rkisp2_params_hist_big(struct rkisp2_params *params,
+				   union rkisp2_params_block block);
+static void rkisp2_params_awb_meas(struct rkisp2_params *params,
+				   union rkisp2_params_block block);
 
 typedef void (*rkisp2_params_handler)(struct rkisp2_params *params,
 				      const union rkisp2_params_block block);
@@ -84,6 +100,12 @@ rkisp2_params_handlers[] = {
 	RKISP2_PARAMS_BLOCK_HANDLER_INFO(GOC, goc, PRE),
 	RKISP2_PARAMS_BLOCK_HANDLER_INFO(LSC, lsc, POST),
 	RKISP2_PARAMS_BLOCK_HANDLER_INFO(CROP, crop, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(AE_LITE, ae_lite, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_LITE, hist_lite, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_BIG0, hist_big, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_BIG1, hist_big, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(HIST_BIG2, hist_big, PRE),
+	RKISP2_PARAMS_BLOCK_HANDLER_INFO(AWB_MEAS, awb_meas, PRE),
 };
 
 static const struct v4l2_isp_params_block_type_info
@@ -95,6 +117,12 @@ rkisp2_params_block_types_info[] = {
 	RKISP2_PARAMS_BLOCK_INFO(GOC, goc),
 	RKISP2_PARAMS_BLOCK_INFO(LSC, lsc),
 	RKISP2_PARAMS_BLOCK_INFO(CROP, crop),
+	RKISP2_PARAMS_BLOCK_INFO(AE_LITE, ae_lite),
+	RKISP2_PARAMS_BLOCK_INFO(HIST_LITE, hist_lite),
+	RKISP2_PARAMS_BLOCK_INFO(HIST_BIG0, hist_big),
+	RKISP2_PARAMS_BLOCK_INFO(HIST_BIG1, hist_big),
+	RKISP2_PARAMS_BLOCK_INFO(HIST_BIG2, hist_big),
+	RKISP2_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
 };
 
 static_assert(ARRAY_SIZE(rkisp2_params_handlers) ==
@@ -549,6 +577,216 @@ static void rkisp2_params_crop_init(struct rkisp2_params *params)
 	__rkisp2_params_crop(params, &arg, V4L2_ISP_PARAMS_FL_BLOCK_ENABLE);
 }
 
+static void rkisp2_params_ae_lite(struct rkisp2_params *params,
+				  union rkisp2_params_block block)
+{
+	const struct rkisp2_params_ae_lite *arg = block.ae_lite;
+	u32 control;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ISP_RAWAE_LITE_CTRL,
+					ISP3X_RAWAE_LITE_EN);
+		rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAE_CH0);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	/*
+	 * \todo clamp these: width and height must be even, min size is 16x4,
+	 * and total height must be < frame height
+	 */
+	rkisp2_write(params->rkisp2, ISP_RAWAE_LITE_BLK_SIZ,
+		     ISP3X_RAWAE_LITE_H(arg->meas_window.h_size) |
+		     ISP3X_RAWAE_LITE_V(arg->meas_window.v_size));
+
+	rkisp2_write(params->rkisp2, ISP_RAWAE_LITE_OFFSET,
+		     ISP3X_RAWAE_LITE_H(arg->meas_window.h_offs) |
+		     ISP3X_RAWAE_LITE_V(arg->meas_window.v_offs));
+
+	control = arg->window_num ? ISP3X_RAWAE_LITE_WNDNUM : 0;
+	control |= ISP3X_RAWAE_LITE_EN;
+
+	rkisp2_param_set_bits(params, ISP_RAWAE_LITE_CTRL,
+			      control);
+	/* TODO figure out CH1 and CH2 */
+	rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAE_CH0);
+}
+
+static void rkisp2_params_hist_lite(struct rkisp2_params *params,
+				    union rkisp2_params_block block)
+{
+	const struct rkisp2_params_hist_lite *arg = block.hist_lite;
+	unsigned int i;
+	u32 control;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ISP_RAWHIST_LITE_BASE, ISP_RAWHIST_CTRL_EN);
+		rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_CH0);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_SIZE,
+		     ISP_RAWHIST_H_SIZE(arg->meas_window.h_size) |
+		     ISP_RAWHIST_V_SIZE(arg->meas_window.v_size));
+
+	rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_OFFS,
+		     ISP_RAWHIST_H_OFFS(arg->meas_window.h_offs) |
+		     ISP_RAWHIST_V_OFFS(arg->meas_window.v_offs));
+
+	rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_RAW2Y_CC,
+		     ISP_RAWHIST_RAW2Y_CC_RCC(arg->coeffs.r) |
+		     ISP_RAWHIST_RAW2Y_CC_GCC(arg->coeffs.g) |
+		     ISP_RAWHIST_RAW2Y_CC_BCC(arg->coeffs.b));
+
+	for (i = 0; i < 6; i++) {
+		rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_WEIGHT + 4 * i,
+			     ISP_RAWHIST_LITE_WEIGHT_WND0(arg->weights[4 * i + 0]) |
+			     ISP_RAWHIST_LITE_WEIGHT_WND1(arg->weights[4 * i + 1]) |
+			     ISP_RAWHIST_LITE_WEIGHT_WND2(arg->weights[4 * i + 2]) |
+			     ISP_RAWHIST_LITE_WEIGHT_WND3(arg->weights[4 * i + 3]));
+	}
+
+	rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_WEIGHT + 4 * 6,
+		     ISP_RAWHIST_LITE_WEIGHT_WND0(arg->weights[24]));
+
+	control = ISP_RAWHIST_CTRL_EN |
+		  ISP_RAWHIST_CTRL_STEPSIZE(arg->stepsize) |
+		  ISP_RAWHIST_CTRL_MODE(arg->mode) |
+		  ISP_RAWHIST_CTRL_WATERLINE(arg->waterline) |
+		  ISP_RAWHIST_CTRL_DATA_SEL(arg->data_sel);
+	rkisp2_write(params->rkisp2, ISP_RAWHIST_LITE_CTRL, control);
+	// I think you can choose the channels in VI_ISP_PATH
+	rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_CH0);
+}
+
+static void rkisp2_params_hist_big(struct rkisp2_params *params,
+				   union rkisp2_params_block block)
+{
+	const struct rkisp2_params_hist_big *arg = block.hist_big;
+	unsigned int i, reg_base, ctrl;
+	u32 control;
+
+	switch (block.header->type) {
+	case RKISP2_PARAMS_BLOCK_HIST_BIG0:
+		reg_base = ISP_RAWHIST_BIG1_BASE;
+		break;
+	case RKISP2_PARAMS_BLOCK_HIST_BIG1:
+		reg_base = ISP_RAWHIST_BIG2_BASE;
+		break;
+	case RKISP2_PARAMS_BLOCK_HIST_BIG2:
+		reg_base = ISP_RAWHIST_BIG3_BASE;
+		break;
+	default:
+		WARN_ONCE(1, "invalid big histogram base %x\n", reg_base);
+		return;
+	}
+
+	ctrl = reg_base + ISP_RAWHIST_BIG_CTRL;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ctrl, ISP_RAWHIST_CTRL_EN);
+		rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_BIG);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_SIZE,
+		     ISP_RAWHIST_H_SIZE(arg->meas_window.h_size) |
+		     ISP_RAWHIST_V_SIZE(arg->meas_window.v_size));
+
+	rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_OFFS,
+		     ISP_RAWHIST_H_OFFS(arg->meas_window.h_offs) |
+		     ISP_RAWHIST_V_OFFS(arg->meas_window.v_offs));
+
+	rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_RAW2Y_CC,
+		     ISP_RAWHIST_RAW2Y_CC_RCC(arg->coeffs.r) |
+		     ISP_RAWHIST_RAW2Y_CC_GCC(arg->coeffs.g) |
+		     ISP_RAWHIST_RAW2Y_CC_BCC(arg->coeffs.b));
+
+	/*
+	 * TODO check if the other weights are programmed in the registers
+	 * after the bins, or if it's sequential write
+	 */
+	for (i = 0; i < 16; i++) {
+		rkisp2_write(params->rkisp2, reg_base + ISP_RAWHIST_BIG_WEIGHT_BASE + 4 * i,
+			     ISP_RAWHIST_BIG_WEIGHT_WND0(arg->weights[5 * i + 0]) |
+			     ISP_RAWHIST_BIG_WEIGHT_WND1(arg->weights[5 * i + 1]) |
+			     ISP_RAWHIST_BIG_WEIGHT_WND2(arg->weights[5 * i + 2]) |
+			     ISP_RAWHIST_BIG_WEIGHT_WND3(arg->weights[5 * i + 3]) |
+			     ISP_RAWHIST_BIG_WEIGHT_WND4(arg->weights[5 * i + 4]));
+	}
+
+	rkisp2_param_set_bits(params, ISP_CTRL1, ISP21_BIGMODE_MODE | ISP21_BIGMODE_FORCE_EN);
+
+	control = ISP_RAWHIST_CTRL_EN |
+		  ISP_RAWHIST_CTRL_STEPSIZE(arg->stepsize) |
+		  ISP_RAWHIST_CTRL_MODE(arg->mode) |
+		  ISP_RAWHIST_CTRL_WATERLINE(arg->waterline) |
+		  ISP_RAWHIST_CTRL_DATA_SEL(arg->data_sel) |
+		  (arg->window_num & ISP_RAWHIST_BIG_CTRL_WND_MASK);
+	rkisp2_write(params->rkisp2, ctrl, control);
+	rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWHIST_BIG);
+}
+
+static void rkisp2_params_awb_meas(struct rkisp2_params *params,
+				   union rkisp2_params_block block)
+{
+	const struct rkisp2_params_awb_meas *arg = block.awb_meas;
+	unsigned int i;
+	u32 val;
+	u32 control = 0;
+
+	if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+		rkisp2_param_clear_bits(params, ISP3X_RAWAWB_CTRL,
+					ISP3X_RAWAWB_CTRL_EN);
+		rkisp2_param_clear_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAWB);
+		rkisp2_param_clear_bits(params, ISP21_RAWAWB_BLK_CTRL, 0x1);
+		return;
+	}
+
+	if (!(block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE))
+		return;
+
+	rkisp2_write(params->rkisp2, ISP21_RAWAWB_WIN_OFFS,
+		     ISP3X_RAWAWB_WIN(arg->meas_window.h_offs,
+				    arg->meas_window.v_offs));
+	rkisp2_write(params->rkisp2, ISP21_RAWAWB_WIN_SIZE,
+		     ISP3X_RAWAWB_WIN(arg->meas_window.h_size,
+				    arg->meas_window.v_size));
+
+	rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_RG_MAX,
+		     ISP3X_RAWAWB_LIMITS(arg->limits[1].r, arg->limits[1].g));
+	rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_BY_MAX,
+		     ISP3X_RAWAWB_LIMITS(arg->limits[1].b, arg->limits[1].y));
+	rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_RG_MIN,
+		     ISP3X_RAWAWB_LIMITS(arg->limits[0].r, arg->limits[0].g));
+	rkisp2_write(params->rkisp2, ISP21_RAWAWB_LIMIT_BY_MIN,
+		     ISP3X_RAWAWB_LIMITS(arg->limits[0].b, arg->limits[0].y));
+
+	for (i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE / 5; i++) {
+		val = (arg->weights[5 * i] & 0x3f)
+		    | ((arg->weights[5 * i + 1] & 0x3f) << 6)
+		    | ((arg->weights[5 * i + 2] & 0x3f) << 12)
+		    | ((arg->weights[5 * i + 3] & 0x3f) << 18)
+		    | ((arg->weights[5 * i + 4] & 0x3f) << 24);
+		rkisp2_write(params->rkisp2, ISP21_RAWAWB_WRAM_DATA_BASE, val);
+	}
+
+	// This options looks like it's required to get "useful" data out
+	rkisp2_param_set_bits(params, ISP21_RAWAWB_BLK_CTRL, 0x1);
+
+	control |= ISP3X_RAWAWB_CTRL_EN;
+	rkisp2_write(params->rkisp2, ISP3X_RAWAWB_CTRL, control);
+	rkisp2_param_set_bits(params, ISP_ISP3A_IMSC, ISP2X_3A_RAWAWB);
+}
+
 static void rkisp2_params_configure(struct rkisp2_params *params,
 				    struct rkisp2_params_buffer *buf,
 				    enum rkisp2_params_configure_priority prio)
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
index 883bdb7c2a61..26deacb0b63d 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v2x.h
@@ -1429,7 +1429,7 @@
 #define ISP_RAWAE_LITE_BLK_SIZ			(ISP_RAWAE_LITE_BASE + 0x00004)
 #define ISP_RAWAE_LITE_OFFSET			(ISP_RAWAE_LITE_BASE + 0x00008)
 #define ISP_RAWAE_LITE_R2Y_CC			(ISP_RAWAE_LITE_BASE + 0x0000c)
-#define ISP_RAWAE_LITE_RO_MEAN			(ISP_RAWAE_LITE_BASE + 0x00010)
+#define ISP_RAWAE_LITE_RO_MEAN(i)		(ISP_RAWAE_LITE_BASE + 0x00010 + ((i) * 4))
 #define ISP_RAWAE_LITE_RO_DBG1			(ISP_RAWAE_LITE_BASE + 0x00074)
 #define ISP_RAWAE_LITE_RO_DBG2			(ISP_RAWAE_LITE_BASE + 0x00078)
 
@@ -2585,4 +2585,41 @@
 /* ISP21 DHAZ/DRC/BAY3D */
 #define ISP21_SELF_FORCE_UPD		BIT(31)
 
+/* RAWHIST */
+#define ISP_RAWHIST_CTRL_EN			BIT(0)
+#define ISP_RAWHIST_CTRL_STEPSIZE(a)		(((a) & 0x7) << 1)
+#define ISP_RAWHIST_CTRL_MODE(a)		(((a) & 0x7) << 8)
+#define ISP_RAWHIST_CTRL_WATERLINE(a)		(((a) & 0xfff) << 12)
+#define ISP_RAWHIST_CTRL_DATA_SEL(a)		(((a) & 0x7) << 24)
+#define ISP_RAWHIST_CTRL_MEAS_DONE		BIT(31)
+#define ISP_RAWHIST_BIG_CTRL_WND_5X5_1		BIT(28)
+#define ISP_RAWHIST_BIG_CTRL_WND_15X15_0	BIT(29)
+#define ISP_RAWHIST_BIG_CTRL_WND_15X15_1	(0x3 << 28)
+#define ISP_RAWHIST_BIG_CTRL_WND_MASK		GENMASK(29, 28)
+
+#define ISP_RAWHIST_H_SIZE(a)			((a) & 0x7ff)
+#define ISP_RAWHIST_V_SIZE(a)			(((a) & 0x7ff) << 16)
+#define ISP_RAWHIST_H_OFFS(a)			((a) & 0x1fff)
+#define ISP_RAWHIST_V_OFFS(a)			(((a) & 0x1fff) << 16)
+
+#define ISP_RAWHIST_RAW2Y_CC_RCC(a)		((a) & 0xff)
+#define ISP_RAWHIST_RAW2Y_CC_GCC(a)		(((a) & 0xff) << 8)
+#define ISP_RAWHIST_RAW2Y_CC_BCC(a)		(((a) & 0xff) << 16)
+#define ISP_RAWHIST_RAW2Y_CC_OFF(a)		(((a) & 0xff) << 24)
+
+#define ISP_RAWHIST_LITE_WEIGHT_WND0(a)		((a) & 0x3f)
+#define ISP_RAWHIST_LITE_WEIGHT_WND1(a)		(((a) & 0x3f) << 8)
+#define ISP_RAWHIST_LITE_WEIGHT_WND2(a)		(((a) & 0x3f) << 16)
+#define ISP_RAWHIST_LITE_WEIGHT_WND3(a)		(((a) & 0x3f) << 24)
+
+#define ISP_RAWHIST_BIG_WEIGHT_WND0(a)		((a) & 0x3f)
+#define ISP_RAWHIST_BIG_WEIGHT_WND1(a)		(((a) & 0x3f) << 6)
+#define ISP_RAWHIST_BIG_WEIGHT_WND2(a)		(((a) & 0x3f) << 12)
+#define ISP_RAWHIST_BIG_WEIGHT_WND3(a)		(((a) & 0x3f) << 18)
+#define ISP_RAWHIST_BIG_WEIGHT_WND4(a)		(((a) & 0x3f) << 24)
+
+#define ISP_RAWHIST_BIN_MASK 			GENMASK(27, 0)
+
+#define ISP_RAWHIST_RAM_OFFSET(a)		((a) & 0xff)
+
 #endif /* _RKISP_REGS_V2X_H */
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
index d27d9a48887e..5a478d7b3565 100644
--- a/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-regs-v3x.h
@@ -3359,6 +3359,7 @@
 /* RAWAE */
 #define ISP3X_RAWAE_LITE_EN		BIT(0)
 #define ISP3X_RAWAE_LITE_WNDNUM		BIT(1)
+#define ISP3X_RAWAE_LITE_DONE		BIT(31)
 
 #define ISP3X_RAWAE_BIG_EN		BIT(0)
 #define ISP3X_RAWAE_BIG_WND0_NUM(x)	(((x) & 0x3) << 1)
@@ -3367,6 +3368,13 @@
 #define ISP3X_RAWAE_BIG_WND3_EN		BIT(6)
 #define ISP3X_RAWAE_BIG_WND4_EN		BIT(7)
 
+#define ISP3X_RAWAE_LITE_H(a)			((a) & 0x1fff)
+#define ISP3X_RAWAE_LITE_V(a)			(((a) & 0x1fff) << 16)
+
+#define ISP3X_RAWAE_LITE_RO_MEAN_R(a)		(((a) >> 22) & 0x3ff)
+#define ISP3X_RAWAE_LITE_RO_MEAN_B(a)		(((a) >> 12) & 0x3ff)
+#define ISP3X_RAWAE_LITE_RO_MEAN_G(a)		((a) & 0xfff)
+
 /* RAWHIST */
 #define ISP3X_RAWHIST_EN		BIT(0)
 #define ISP3X_RAWHIST_STEPSIZE(x)	(((x) & 0x7) << 1)
@@ -3397,6 +3405,12 @@
 #define ISP3X_RAWAF_INTLINE0_EN		BIT(27)
 
 /* RAWAWB */
+#define ISP3X_RAWAWB_CTRL_EN		BIT(0)
+#define ISP3X_RAWAWB_CTRL_MEAS_DONE	BIT(31)
+
+#define ISP3X_RAWAWB_WIN(h, v)		(((h) & 0x1fff) | (((v) & 0x1fff) << 16))
+#define ISP3X_RAWAWB_LIMITS(a, b)		(((a) & 0xff) | (((b) & 0xff) << 16))
+
 #define ISP32_RAWAWB_2DDR_PATH_EN	BIT(23)
 #define ISP32_RAWAWB_2DDR_PATH_DS	BIT(27)
 #define ISP32_RAWAWB_2DDR_PATH_ERR	BIT(29)
diff --git a/drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c b/drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c
new file mode 100644
index 000000000000..597ef0fe9051
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp2/rkisp2-stats.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+/*
+ * Rockchip ISP2 Driver - Stats subdevice
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2026 Ideas on Board Oy.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "rkisp2-common.h"
+
+#define RKISP2_STATS_DEV_NAME	RKISP2_DRIVER_NAME "_stats"
+
+#define RKISP2_STATS_BLOCK_INFO(block, data) \
+	[RKISP2_STATS_BLOCK_ ## block] = { \
+		.size = sizeof(struct rkisp2_stats_ ## data), \
+	}
+
+static const struct v4l2_isp_stats_block_type_info
+rkisp2_stats_blocks_info[] = {
+	RKISP2_STATS_BLOCK_INFO(AE_LITE, ae_lite),
+	RKISP2_STATS_BLOCK_INFO(HIST_LITE, hist),
+	RKISP2_STATS_BLOCK_INFO(HIST_BIG0, hist),
+	RKISP2_STATS_BLOCK_INFO(HIST_BIG1, hist),
+	RKISP2_STATS_BLOCK_INFO(HIST_BIG1, hist),
+	RKISP2_STATS_BLOCK_INFO(AWB, awb),
+};
+
+/*
+ * We only need one instance of hist here since it's only for accessing the
+ * members of the union
+ */
+union rkisp2_stats_block {
+	struct v4l2_isp_block_header header;
+	struct rkisp2_stats_ae_lite ae_lite;
+	struct rkisp2_stats_hist hist;
+	struct rkisp2_stats_awb awb;
+};
+
+#define rkisp2_init_stats_block(rkisp2, buf, type)			\
+	((union rkisp2_stats_block *)					\
+	v4l2_isp_stats_init_block((rkisp2)->dev, (buf),			\
+				  rkisp2_stats_blocks_info,		\
+				  ARRAY_SIZE(rkisp2_stats_blocks_info),	\
+				  (type), RKISP2_STATS_MAX_SIZE))
+
+static int rkisp2_stats_enum_fmt_meta_cap(struct file *file, void *priv,
+					  struct v4l2_fmtdesc *f)
+{
+	if (f->index)
+		return -EINVAL;
+
+	f->pixelformat = V4L2_META_FMT_RKISP2_STATS;
+
+	return 0;
+}
+
+static int rkisp2_stats_g_fmt_meta_cap(struct file *file, void *priv,
+				       struct v4l2_format *f)
+{
+	static const struct v4l2_meta_format mfmt = {
+		.dataformat = V4L2_META_FMT_RKISP2_STATS,
+		.buffersize = v4l2_isp_buffer_size(RKISP2_STATS_MAX_SIZE)
+	};
+
+	f->fmt.meta = mfmt;
+
+	return 0;
+}
+
+static int rkisp2_stats_querycap(struct file *file,
+				 void *priv, struct v4l2_capability *cap)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	strscpy(cap->driver, RKISP2_DRIVER_NAME, sizeof(cap->driver));
+	strscpy(cap->card, vdev->name, sizeof(cap->card));
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops rkisp2_stats_ioctl = {
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_enum_fmt_meta_cap = rkisp2_stats_enum_fmt_meta_cap,
+	.vidioc_g_fmt_meta_cap = rkisp2_stats_g_fmt_meta_cap,
+	.vidioc_s_fmt_meta_cap = rkisp2_stats_g_fmt_meta_cap,
+	.vidioc_try_fmt_meta_cap = rkisp2_stats_g_fmt_meta_cap,
+	.vidioc_querycap = rkisp2_stats_querycap,
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations rkisp2_stats_fops = {
+	.mmap = vb2_fop_mmap,
+	.unlocked_ioctl = video_ioctl2,
+	.poll = vb2_fop_poll,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release
+};
+
+static int rkisp2_stats_vb2_queue_setup(struct vb2_queue *vq,
+					unsigned int *num_buffers,
+					unsigned int *num_planes,
+					unsigned int sizes[],
+					struct device *alloc_devs[])
+{
+	/* TODO num_buffers */
+
+	*num_planes = 1;
+
+	sizes[0] = v4l2_isp_buffer_size(RKISP2_STATS_MAX_SIZE);
+
+	return 0;
+}
+
+static void rkisp2_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rkisp2_buffer *stats_buf =
+		container_of(vbuf, struct rkisp2_buffer, vb);
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct rkisp2_stats *stats_dev = vq->drv_priv;
+
+	spin_lock_irq(&stats_dev->lock);
+	list_add_tail(&stats_buf->queue, &stats_dev->stat);
+	spin_unlock_irq(&stats_dev->lock);
+}
+
+static int rkisp2_stats_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+	if (vb2_plane_size(vb, 0) < v4l2_isp_buffer_size(RKISP2_STATS_MAX_SIZE))
+		return -EINVAL;
+
+	vb2_set_plane_payload(vb, 0, v4l2_isp_buffer_size(RKISP2_STATS_MAX_SIZE));
+
+	return 0;
+}
+
+static int rkisp2_stats_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct rkisp2_stats *stats = vq->drv_priv;
+	stats->icr = 0;
+	stats->cur_buf = NULL;
+	stats->awb_window_offset = 0;
+
+	return 0;
+}
+
+static void rkisp2_stats_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct rkisp2_stats *stats = vq->drv_priv;
+	struct rkisp2_buffer *buf;
+	LIST_HEAD(tmp_list);
+
+	spin_lock_irq(&stats->lock);
+	list_splice_init(&stats->stat, &tmp_list);
+	spin_unlock_irq(&stats->lock);
+
+	list_for_each_entry(buf, &tmp_list, queue)
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+
+	if (stats->cur_buf != NULL) {
+		vb2_buffer_done(&stats->cur_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+		stats->cur_buf = NULL;
+	}
+}
+
+static const struct vb2_ops rkisp2_stats_vb2_ops = {
+	.queue_setup = rkisp2_stats_vb2_queue_setup,
+	.buf_queue = rkisp2_stats_vb2_buf_queue,
+	.buf_prepare = rkisp2_stats_vb2_buf_prepare,
+	.start_streaming = rkisp2_stats_vb2_start_streaming,
+	.stop_streaming = rkisp2_stats_vb2_stop_streaming,
+};
+
+static int
+rkisp2_stats_init_vb2_queue(struct vb2_queue *q, struct rkisp2_stats *stats)
+{
+	struct rkisp2_vdev_node *node;
+
+	node = container_of(q, struct rkisp2_vdev_node, buf_queue);
+
+	q->type = V4L2_BUF_TYPE_META_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = stats;
+	q->ops = &rkisp2_stats_vb2_ops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->buf_struct_size = sizeof(struct rkisp2_buffer);
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &node->vlock;
+	q->dev = stats->rkisp2->dev;
+
+	return vb2_queue_init(q);
+}
+
+static void rkisp2_stats_get_rawae_lite(struct rkisp2_stats *stats,
+					u32 status,
+					struct v4l2_isp_buffer *pbuf)
+{
+	struct rkisp2_device *rkisp2 = stats->rkisp2;
+	union rkisp2_stats_block *block;
+	struct rkisp2_stats_ae_lite *ae_lite;
+	unsigned int i;
+	u32 val;
+
+	block = rkisp2_init_stats_block(rkisp2, pbuf,
+					RKISP2_STATS_BLOCK_AE_LITE);
+	if (IS_ERR(block))
+		return;
+
+	ae_lite = &block->ae_lite;
+
+	/* TODO figure out what the other channels are for */
+	if (!(status & ISP2X_3A_RAWAE_CH0))
+		return;
+	stats->icr |= ISP2X_3A_RAWAE_CH0;
+
+	for (i = 0; i < RKISP2_ISP_AE_MEAN_MAX_LITE; i++) {
+		val = rkisp2_read(rkisp2, ISP_RAWAE_LITE_RO_MEAN(i));
+		ae_lite->exp_mean_r[i] = ISP3X_RAWAE_LITE_RO_MEAN_R(val);
+		ae_lite->exp_mean_g[i] = ISP3X_RAWAE_LITE_RO_MEAN_G(val);
+		ae_lite->exp_mean_b[i] = ISP3X_RAWAE_LITE_RO_MEAN_B(val);
+	}
+
+	/*
+	 * The done bit is never set in the register; set it here to signal
+	 * done to userspace
+	 */
+	ae_lite->done = 1;
+}
+
+static void rkisp2_stats_hist_read(struct rkisp2_stats *stats, u32 reg_base,
+				   struct v4l2_isp_buffer *pbuf,
+				   enum rkisp2_stats_block_type type)
+{
+	struct rkisp2_device *rkisp2 = stats->rkisp2;
+	unsigned int i;
+	u32 val, done;
+	unsigned int ctrl = reg_base + ISP_RAWHIST_BIG_CTRL;
+	union rkisp2_stats_block *block;
+	struct rkisp2_stats_hist *hist;
+
+	block = rkisp2_init_stats_block(rkisp2, pbuf, type);
+	if (IS_ERR(block))
+		return;
+
+	hist = &block->hist;
+
+	/*
+	 * TODO Figure out how the done bit works (it doesn't seem to ever be
+	 * set)
+	 */
+	done = rkisp2_read(rkisp2, ctrl) & ISP_RAWHIST_CTRL_MEAS_DONE;
+	hist->done = done ? 1 : 0;
+	if (!done)
+		return;
+
+	rkisp2_write(rkisp2, reg_base + ISP_RAWHIST_BIG_HRAM_CTRL,
+		     ISP_RAWHIST_RAM_OFFSET(0));
+
+	for (i = 0; i < RKISP2_ISP_HIST_BIN_N_MAX; i++) {
+		val = rkisp2_read(rkisp2, reg_base + ISP_RAWHIST_BIG_RO_BASE_BIN);
+		hist->hist_bins[i] = val;
+	}
+
+	/* Set the done bit */
+	val = rkisp2_read(rkisp2, ctrl);
+	rkisp2_write(rkisp2, ctrl, val | ISP_RAWHIST_CTRL_MEAS_DONE);
+}
+
+static void rkisp2_stats_get_hist(struct rkisp2_stats *stats,
+				  u32 status,
+				  struct v4l2_isp_buffer *pbuf)
+{
+	/*
+	 * It seems that big only triggers the big interrupt, and lite only
+	 * triggers the ch0 interrupts. ch1 and ch2 interrupts are therefore
+	 * unknown, and we need to check the done bits of every big module to
+	 * determine which to process
+	 */
+
+	/*
+	 * It looks like we get separate interrupts for big in big-mode (ie.
+	 * using the 15x15 grid) and for non-big stuff
+	 */
+
+	if (status & ISP2X_3A_RAWHIST_CH0) {
+		stats->rkisp2->debug.stats3a_hist_ch0_count++;
+		stats->icr |= ISP2X_3A_RAWHIST_CH0;
+		rkisp2_stats_hist_read(stats, ISP_RAWHIST_LITE_BASE, pbuf,
+				       RKISP2_STATS_BLOCK_HIST_LITE);
+	}
+
+	if (status & ISP2X_3A_RAWHIST_BIG) {
+		stats->rkisp2->debug.stats3a_hist_big_count++;
+		stats->icr |= ISP2X_3A_RAWHIST_BIG;
+		rkisp2_stats_hist_read(stats, ISP_RAWHIST_BIG1_BASE, pbuf,
+				       RKISP2_STATS_BLOCK_HIST_BIG0);
+		rkisp2_stats_hist_read(stats, ISP_RAWHIST_BIG2_BASE, pbuf,
+				       RKISP2_STATS_BLOCK_HIST_BIG1);
+		rkisp2_stats_hist_read(stats, ISP_RAWHIST_BIG3_BASE, pbuf,
+				       RKISP2_STATS_BLOCK_HIST_BIG2);
+	}
+
+}
+
+static void rkisp2_stats_get_rawawb(struct rkisp2_stats *stats,
+				    u32 status,
+				    struct v4l2_isp_buffer *pbuf)
+{
+	struct rkisp2_device *rkisp2 = stats->rkisp2;
+	union rkisp2_stats_block *block;
+	struct rkisp2_stats_awb *awb;
+	unsigned int ctrl = ISP21_RAWAWB_CTRL;
+	unsigned int i;
+	u32 val1, val2;
+	u32 done;
+
+	block = rkisp2_init_stats_block(rkisp2, pbuf, RKISP2_STATS_BLOCK_AWB);
+	if (IS_ERR(block))
+		return;
+
+	awb = &block->awb;
+
+	if (!(status & ISP2X_3A_RAWAWB))
+		return;
+	stats->icr |= ISP2X_3A_RAWAWB;
+
+	rkisp2->debug.stats3a_awb_count++;
+
+	done = rkisp2_read(rkisp2, ctrl) & ISP3X_RAWAWB_CTRL_MEAS_DONE;
+	awb->done = done ? 1 : 0;
+	if (!done)
+		return;
+
+	rkisp2->debug.stats3a_awb_done_count++;
+
+	for (i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++) {
+		val1 = rkisp2_read(rkisp2, ISP21_RAWAWB_RAM_DATA_BASE);
+		val2 = rkisp2_read(rkisp2, ISP21_RAWAWB_RAM_DATA_BASE);
+		awb->counts_r[i] = (val2 >> 4) & 0x3ffff;
+		awb->counts_g[i] = (val1 >> 18) | ((val2 & 0xf) << 14);
+		awb->counts_b[i] = val1 & 0x3ffff;
+		awb->counts_w[i] = val2 >> 22;
+	}
+
+	/*
+	 * Clear the done bit (reference says write 0 but writing 1 seems to be
+	 * the correct reset, plus all the other stats 3a write 1 to reset)
+	 */
+	val1 = rkisp2_read(rkisp2, ctrl);
+	rkisp2_write(rkisp2, ctrl, val1 | ISP3X_RAWAWB_CTRL_MEAS_DONE);
+}
+
+/* This is always called in an intterupt context */
+static struct rkisp2_buffer *rkisp2_stats_get_buf(struct rkisp2_stats *stats)
+{
+	struct rkisp2_buffer *ret = NULL;
+
+	if (stats->cur_buf != NULL)
+		return stats->cur_buf;
+
+	/* get one empty buffer */
+	if (!list_empty(&stats->stat)) {
+		ret = list_first_entry(&stats->stat, struct rkisp2_buffer, queue);
+		list_del(&ret->queue);
+	}
+
+	stats->cur_buf = ret;
+
+	return ret;
+}
+
+static void rkisp2_stats_complete_buf(struct rkisp2_stats *stats, struct rkisp2_buffer *buf)
+{
+	unsigned int frame_sequence = stats->rkisp2->isp.frame_sequence;
+	u64 timestamp = ktime_get_ns();
+
+	vb2_set_plane_payload(&buf->vb.vb2_buf, 0,
+			      v4l2_isp_buffer_size(RKISP2_STATS_MAX_SIZE));
+	buf->vb.sequence = frame_sequence;
+	buf->vb.vb2_buf.timestamp = timestamp;
+	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+	stats->cur_buf = NULL;
+}
+
+static void rkisp2_stats_send_measurement_3a(struct rkisp2_stats *stats, u32 status)
+{
+	struct v4l2_isp_buffer *cur_stat_buf;
+	struct rkisp2_buffer *cur_buf;
+
+	cur_buf = rkisp2_stats_get_buf(stats);
+	if (!cur_buf)
+		return;
+
+	cur_stat_buf = (struct v4l2_isp_buffer *)
+			vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0);
+
+	v4l2_isp_stats_init_buffer(cur_stat_buf, V4L2_ISP_VERSION_V1);
+
+	rkisp2_stats_get_rawae_lite(stats, status, cur_stat_buf);
+
+	rkisp2_stats_get_hist(stats, status, cur_stat_buf);
+
+	rkisp2_stats_get_rawawb(stats, status, cur_stat_buf);
+
+	rkisp2_write(stats->rkisp2, ISP_ISP3A_ICR, stats->icr);
+
+	rkisp2_stats_complete_buf(stats, cur_buf);
+	stats->icr = 0;
+}
+
+irqreturn_t rkisp2_stats_isr_eof(struct rkisp2_stats *stats)
+{
+	struct rkisp2_device *rkisp2 = stats->rkisp2;
+	u32 status = rkisp2_read(rkisp2, ISP_ISP3A_MIS);
+
+	spin_lock(&stats->lock);
+	rkisp2_stats_send_measurement_3a(stats, status);
+	spin_unlock(&stats->lock);
+
+	return IRQ_HANDLED;
+}
+
+static void rkisp2_init_stats(struct rkisp2_stats *stats)
+{
+	stats->vdev_fmt.fmt.meta.dataformat =
+		V4L2_META_FMT_RKISP2_STATS;
+	stats->vdev_fmt.fmt.meta.buffersize =
+		v4l2_isp_buffer_size(RKISP2_STATS_MAX_SIZE);
+}
+
+int rkisp2_stats_register(struct rkisp2_device *rkisp2)
+{
+	struct rkisp2_stats *stats = &rkisp2->stats;
+	struct rkisp2_vdev_node *node = &stats->vnode;
+	struct video_device *vdev = &node->vdev;
+	int ret;
+
+	stats->rkisp2 = rkisp2;
+	mutex_init(&node->vlock);
+	INIT_LIST_HEAD(&stats->stat);
+	spin_lock_init(&stats->lock);
+
+	strscpy(vdev->name, RKISP2_STATS_DEV_NAME, sizeof(vdev->name));
+
+	video_set_drvdata(vdev, stats);
+	vdev->ioctl_ops = &rkisp2_stats_ioctl;
+	vdev->fops = &rkisp2_stats_fops;
+	vdev->release = video_device_release_empty;
+	vdev->lock = &node->vlock;
+	vdev->v4l2_dev = &rkisp2->v4l2_dev;
+	vdev->queue = &node->buf_queue;
+	vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+	vdev->vfl_dir =  VFL_DIR_RX;
+	rkisp2_stats_init_vb2_queue(vdev->queue, stats);
+	rkisp2_init_stats(stats);
+	video_set_drvdata(vdev, stats);
+
+	node->pad.flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+	if (ret)
+		goto error;
+
+	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+	if (ret) {
+		dev_err(&vdev->dev,
+			"failed to register %s, ret=%d\n", vdev->name, ret);
+		goto error;
+	}
+
+	return 0;
+
+error:
+	media_entity_cleanup(&vdev->entity);
+	mutex_destroy(&node->vlock);
+	stats->rkisp2 = NULL;
+	return ret;
+}
+
+void rkisp2_stats_unregister(struct rkisp2_device *rkisp2)
+{
+	struct rkisp2_stats *stats = &rkisp2->stats;
+	struct rkisp2_vdev_node *node = &stats->vnode;
+	struct video_device *vdev = &node->vdev;
+
+	if (!stats->rkisp2)
+		return;
+
+	vb2_video_unregister_device(vdev);
+	media_entity_cleanup(&vdev->entity);
+	mutex_destroy(&node->vlock);
+}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 2f2e8bf76168..08ae575e90b0 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1468,6 +1468,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_META_FMT_RK_ISP1_STAT_3A:	descr = "Rockchip ISP1 3A Statistics"; break;
 	case V4L2_META_FMT_RK_ISP1_EXT_PARAMS:	descr = "Rockchip ISP1 Ext 3A Params"; break;
 	case V4L2_META_FMT_RKISP2_PARAMS:	descr = "Rockchip ISP2 3A Parameters"; break;
+	case V4L2_META_FMT_RKISP2_STATS:	descr = "Rockchip ISP2 3A Statistics"; break;
 	case V4L2_META_FMT_C3ISP_PARAMS:	descr = "Amlogic C3 ISP Parameters"; break;
 	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;
diff --git a/include/uapi/linux/rkisp2-config.h b/include/uapi/linux/rkisp2-config.h
index 5f8c8a555003..d798cae1076e 100644
--- a/include/uapi/linux/rkisp2-config.h
+++ b/include/uapi/linux/rkisp2-config.h
@@ -20,6 +20,17 @@
 #define RKISP2_ISP_LSC_SAMPLES_MAX		17
 #define RKISP2_ISP_LSC_SECTORS_TBL_SIZE_MAX	16
 
+#define RKISP2_ISP_AE_MEAN_MAX_LITE	25
+#define RKISP2_ISP_AE_MEAN_MAX_BIG	225
+
+#define RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_LITE	25
+#define RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_BIG	225
+#define RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_MAX		RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_BIG
+
+#define RKISP2_ISP_HIST_BIN_N_MAX	256
+
+#define RKISP2_ISP_AWB_COUNTS_SIZE	225
+
 /**
  * enum rkisp2_isp_version - ISP variants
  *
@@ -60,6 +71,109 @@ enum rkisp2_isp_crop_en {
 	RKISP2_ISP_CROP_ENABLE_SELF = 0x2,
 };
 
+enum rkisp2_isp_histogram_mode {
+	RKISP2_ISP_HISTOGRAM_MODE_DISABLE,
+	RKISP2_ISP_HISTOGRAM_MODE_R_HISTOGRAM = 2,
+	RKISP2_ISP_HISTOGRAM_MODE_G_HISTOGRAM,
+	RKISP2_ISP_HISTOGRAM_MODE_B_HISTOGRAM,
+	RKISP2_ISP_HISTOGRAM_MODE_Y_HISTOGRAM
+};
+
+/*
+ * This selects which bits are used from the input data to compute the
+ * histogram
+ */
+enum rkisp2_isp_histogram_data_sel {
+	RKISP2_ISP_HISTOGRAM_DATA_SEL_11_4,
+	RKISP2_ISP_HISTOGRAM_DATA_SEL_10_3,
+	RKISP2_ISP_HISTOGRAM_DATA_SEL_9_2,
+	RKISP2_ISP_HISTOGRAM_DATA_SEL_8_1,
+	RKISP2_ISP_HISTOGRAM_DATA_SEL_7_0,
+};
+
+/*---------- Statistics ------------*/
+
+/**
+ * enum rkisp2_stats_block_type - RkISP2 extensible stats block types
+ *
+ * @RKISP2_STATS_BLOCK_AE_LITE: Auto exposite (lite) measurements
+ * @RKISP2_STATS_BLOCK_HIST_LITE: Histogram (lite)
+ * @RKISP2_STATS_BLOCK_HIST_BIG0: Histogram (big 0)
+ * @RKISP2_STATS_BLOCK_HIST_BIG1: Histogram (big 1)
+ * @RKISP2_STATS_BLOCK_HIST_BIG2: Histogram (big 2)
+ * @RKISP2_STATS_BLOCK_AWB: Auto white balance counts
+ */
+enum rkisp2_stats_block_type {
+	RKISP2_STATS_BLOCK_AE_LITE,
+	RKISP2_STATS_BLOCK_HIST_LITE,
+	RKISP2_STATS_BLOCK_HIST_BIG0,
+	RKISP2_STATS_BLOCK_HIST_BIG1,
+	RKISP2_STATS_BLOCK_HIST_BIG2,
+	RKISP2_STATS_BLOCK_AWB,
+};
+
+/**
+ * struct rkisp2_stats_ae_lite - statistics auto exposure data
+ *
+ * @header: block header (type = RKISP2_STATS_BLOCK_AE_LITE)
+ * @exp_mean_r: Mean luminance value of block xy for r channel
+ * @exp_mean_g: Mean luminance value of block xy for g channel
+ * @exp_mean_b: Mean luminance value of block xy for b channel
+ * @done: This set to nonzero when the stats are ready
+ *
+ * Image is divided into 5x5 blocks on lite and 15x15 blocks on big.
+ */
+struct rkisp2_stats_ae_lite {
+	struct v4l2_isp_block_header header;
+	__u16 exp_mean_r[RKISP2_ISP_AE_MEAN_MAX_LITE];
+	__u16 exp_mean_g[RKISP2_ISP_AE_MEAN_MAX_LITE];
+	__u16 exp_mean_b[RKISP2_ISP_AE_MEAN_MAX_LITE];
+	__u8 done;
+};
+
+/**
+ * struct rkisp2_stats_hist - statistics histogram data
+ *
+ * @header: block header (type = RKISP2_STATS_BLOCK_HIST_{LITE,BIG0,BIG1,BIG2})
+ * @hist_bins: measured bin counters. Each bin is a 28 bits unsigned fixed point
+ *	       type. Bits 0-4 are the fractional part and bits 5-27 are the
+ *	       integer part.
+ * @done: This set to nonzero when the stats are ready
+ *
+ * There are 256 bins, at least on 3.x.
+ */
+struct rkisp2_stats_hist {
+	struct v4l2_isp_block_header header;
+	__u32 hist_bins[RKISP2_ISP_HIST_BIN_N_MAX];
+	__u8 done;
+};
+
+/**
+ * struct rkisp2_stats_awb - statistics auto white balance data
+ *
+ * @header: block header (type = RKISP2_STATS_BLOCK_AWB)
+ * @counts_r: Counts of red (18-bits)
+ * @counts_g: Counts of green (18-bits)
+ * @counts_b: Counts of blue (18-bits)
+ * @counts_w: Counts of white point (10-bits)
+ * @done: This set to nonzero when the stats are ready
+ *
+ * TODO Figure out what is being counted
+ */
+struct rkisp2_stats_awb {
+	struct v4l2_isp_block_header header;
+	__u32 counts_r[RKISP2_ISP_AWB_COUNTS_SIZE];
+	__u32 counts_g[RKISP2_ISP_AWB_COUNTS_SIZE];
+	__u32 counts_b[RKISP2_ISP_AWB_COUNTS_SIZE];
+	__u16 counts_w[RKISP2_ISP_AWB_COUNTS_SIZE];
+	__u8 done;
+};
+
+#define RKISP2_STATS_MAX_SIZE					\
+	(sizeof(struct rkisp2_stats_ae_lite)			+\
+	sizeof(struct rkisp2_stats_hist) * 4			+\
+	sizeof(struct rkisp2_stats_awb))
+
 /*---------- Parameters ------------*/
 
 /**
@@ -72,6 +186,12 @@ enum rkisp2_isp_crop_en {
  * @RKISP2_PARAMS_BLOCK_GOC: Gamma out correction
  * @RKISP2_PARAMS_BLOCK_LSC: Lens shading correction
  * @RKISP2_PARAMS_BLOCK_CROP: Crop config
+ * @RKISP2_PARAMS_BLOCK_AE_LITE: AE measurement config (lite)
+ * @RKISP2_PARAMS_BLOCK_HIST_LITE: Histogram measurement config (lite)
+ * @RKISP2_PARAMS_BLOCK_HIST_BIG0: Histogram measurement config (zeroth big block)
+ * @RKISP2_PARAMS_BLOCK_HIST_BIG1: Histogram measurement config (first big block)
+ * @RKISP2_PARAMS_BLOCK_HIST_BIG2: Histogram measurement config (second big block)
+ * @RKISP2_PARAMS_BLOCK_AWB_MEAS: AWB measurements config
  */
 enum rkisp2_params_block_type {
 	RKISP2_PARAMS_BLOCK_BLS,
@@ -81,6 +201,12 @@ enum rkisp2_params_block_type {
 	RKISP2_PARAMS_BLOCK_GOC,
 	RKISP2_PARAMS_BLOCK_LSC,
 	RKISP2_PARAMS_BLOCK_CROP,
+	RKISP2_PARAMS_BLOCK_AE_LITE,
+	RKISP2_PARAMS_BLOCK_HIST_LITE,
+	RKISP2_PARAMS_BLOCK_HIST_BIG0,
+	RKISP2_PARAMS_BLOCK_HIST_BIG1,
+	RKISP2_PARAMS_BLOCK_HIST_BIG2,
+	RKISP2_PARAMS_BLOCK_AWB_MEAS,
 };
 
 /**
@@ -141,6 +267,36 @@ struct rkisp2_isp_awb_gains {
 	__u16 gb;
 };
 
+/**
+ * struct rkisp2_isp_color_cc - Color coefficients
+ *
+ * @r: Red coefficient
+ * @g: Green coefficient
+ * @b: Blue coefficient
+ */
+struct rkisp2_isp_color_cc {
+	__u8 r;
+	__u8 g;
+	__u8 b;
+};
+
+/**
+ * struct rkisp2_isp_awb_color_quad - Group of RGB and luminance for AWB
+ *
+ * TODO redesign this?
+ *
+ * @r: Red
+ * @g: Green
+ * @b: Blue
+ * @y: Y (luminance)
+ */
+struct rkisp2_isp_awb_color_quad {
+	__u8 r;
+	__u8 g;
+	__u8 b;
+	__u8 y;
+};
+
 /**
  * struct rkisp2_params_bls - RkISP2 params BLS config
  *
@@ -351,6 +507,88 @@ struct rkisp2_params_crop {
 	struct rkisp2_isp_window sp_crop;
 } __attribute__((aligned(8)));
 
+/**
+ * struct rkisp2_params_ae_lite - RKISP2 params AE lite config
+ *
+ * RkISP2 parameters auto exposure measurement configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_AE_LITE`.
+ *
+ * TODO change window_num to enum?
+ *
+ * @header: The RkISP2 parameters block header
+ * @window_num: 0 for 1x1, 1 for 5x5
+ * @meas_window: Size of measurement window. First window for 5x5.
+ */
+struct rkisp2_params_ae_lite {
+	struct v4l2_isp_params_block_header header;
+	__u8 window_num;
+	struct rkisp2_isp_window meas_window;
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_hist_lite RKISP2 params histogram lite config
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_HIST_LITE`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @data_sel: Data selection mode (from enum rkisp2_isp_histogram_data_sel)
+ * @mode: Histogram mode (from enum rkisp2_isp_histogram_mode)
+ * @stepsize: Predivider (count every <stepsize> pixel)
+ * @waterline: Waterline for region statics
+ * @coeffs: Coefficients for raw2y formula
+ * @meas_window: Size of first measurement subwindow
+ * @weights: Weights
+ */
+struct rkisp2_params_hist_lite {
+	struct v4l2_isp_params_block_header header;
+	__u8 data_sel;
+	__u8 mode;
+	__u8 stepsize;
+	__u16 waterline;
+	struct rkisp2_isp_color_cc coeffs;
+	struct rkisp2_isp_window meas_window;
+	__u8 weights[RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_LITE];
+} __attribute__((aligned(8)));
+
+/**
+ * Same as struct rkisp2_params_hist_lite but for big channel
+ *
+ * RkISP2 parameters histogram configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_HIST_BIG{0,1,2}`.
+ *
+ * @window_num: 0 or 1 for 5x5, 2 or 3 for 15x15
+ */
+struct rkisp2_params_hist_big {
+	struct v4l2_isp_params_block_header header;
+	__u8 window_num;
+	__u8 data_sel;
+	__u8 mode;
+	__u8 stepsize;
+	__u16 waterline;
+	struct rkisp2_isp_color_cc coeffs;
+	struct rkisp2_isp_window meas_window;
+	__u8 weights[RKISP2_ISP_HIST_WEIGHT_GRIDS_SIZE_BIG];
+} __attribute__((aligned(8)));
+
+/**
+ * struct rkisp2_params_awb_meas - Configuration used by rawawb
+ *
+ * RkISP2 parameters AWB measurement configuration block.
+ * Identified by :c:type:`RKISP2_PARAMS_BLOCK_AWB_MEAS`.
+ *
+ * @header: The RkISP2 parameters block header
+ * @meas_window: Size of first measurement subwindow (13 bits)
+ * @limits: Limits for white point detection [min, max] (8 bits)
+ * @weights: Weights (6-bits)
+ */
+struct rkisp2_params_awb_meas {
+	struct v4l2_isp_params_block_header header;
+	struct rkisp2_isp_window meas_window;
+	struct rkisp2_isp_awb_color_quad limits[2];
+	__u8 weights[RKISP2_ISP_AWB_COUNTS_SIZE];
+} __attribute__((aligned(8)));
+
 #define RKISP2_PARAMS_MAX_SIZE					\
 	(sizeof(struct rkisp2_params_bls)			+\
 	sizeof(struct rkisp2_params_awb_gains)			+\
@@ -358,6 +596,10 @@ struct rkisp2_params_crop {
 	sizeof(struct rkisp2_params_ccm)			+\
 	sizeof(struct rkisp2_params_goc)			+\
 	sizeof(struct rkisp2_params_lsc)			+\
-	sizeof(struct rkisp2_params_crop))
+	sizeof(struct rkisp2_params_crop)			+\
+	sizeof(struct rkisp2_params_ae_lite)			+\
+	sizeof(struct rkisp2_params_hist_lite)			+\
+	sizeof(struct rkisp2_params_hist_big) * 3		+\
+	sizeof(struct rkisp2_params_awb_meas))
 
 #endif /* _UAPI_RKISP2_CONFIG_H */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index b258422288b0..e0f37820387f 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -878,6 +878,7 @@ struct v4l2_pix_format {
 
 /* Vendor specific - used for RKISP2 camera sub-system */
 #define V4L2_META_FMT_RKISP2_PARAMS	v4l2_fourcc('R', 'K', '2', 'P') /* Rockchip ISP2 Parameters */
+#define V4L2_META_FMT_RKISP2_STATS	v4l2_fourcc('R', 'K', '2', 'S') /* Rockchip ISP2 3A Statistics */
 
 /* Vendor specific - used for C3_ISP */
 #define V4L2_META_FMT_C3ISP_PARAMS	v4l2_fourcc('C', '3', 'P', 'M') /* Amlogic C3 ISP Parameters */
-- 
2.47.2


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* Re: [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings
  2026-08-24  9:03 ` [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings Paul Elder
@ 2026-08-24 10:52   ` Krzysztof Kozlowski
  2026-08-25  6:05     ` Paul Elder
  0 siblings, 1 reply; 9+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-24 10:52 UTC (permalink / raw)
  To: Paul Elder, laurent.pinchart
  Cc: michael.riesch, xuhf, stefan.klug, mehdi.djait, linux-media,
	linux-arm-kernel, linux-rockchip, linux-kernel, Heiko Stuebner

On 24/08/2026 11:03, Paul Elder wrote:
> Add documentation for the Rockchip rkisp2 bindings. This is meant to
> support multiple versions of Rockchip ISPs going forward, including the
> 2.x series and 3.x series. The current version only adds the compatible
> for the RK3588, which is a 3.0.
> 
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

Testing is disabled for some time but it's not really a reason to opt out.

Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.

Tools like b4 or scripts/get_maintainer.pl provide you proper list of
people, so fix your workflow. Tools might also fail if you work on some
ancient tree (don't, instead use mainline) or work on fork of kernel
(don't, instead use mainline). Just use b4 and everything should be
fine, although remember about `b4 prep --auto-to-cc` if you added new
patches to the patchset.

You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time.

Please kindly resend and include all necessary To/Cc entries.

A nit, subject: drop second/last, redundant "bindings". The
"dt-bindings" prefix is already stating that these are bindings.
See also:
https://elixir.bootlin.com/linux/v7.1-rc7/source/Documentation/devicetree/bindings/submitting-patches.rst#L23


Best regards,
Krzysztof

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* Re: [PATCH v2 2/5] arm64: dts: rockchip: add ISP nodes to rk3588
  2026-08-24  9:03 ` [PATCH v2 2/5] arm64: dts: rockchip: add ISP nodes to rk3588 Paul Elder
@ 2026-08-24 10:52   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 9+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-24 10:52 UTC (permalink / raw)
  To: Paul Elder, laurent.pinchart
  Cc: Xu Hongfei, michael.riesch, stefan.klug, mehdi.djait, linux-media,
	linux-arm-kernel, linux-rockchip, linux-kernel, Heiko Stuebner

On 24/08/2026 11:03, Paul Elder wrote:
> From: Xu Hongfei <xuhf@rock-chips.com>
> 
> Add device tree nodes for the ISP and their iommus on the RK3588.
> 
> Signed-off-by: Xu Hongfei <xuhf@rock-chips.com>
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> 
> ---
> No change in v2
> ---
>  arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 57 +++++++++++++++++++
>  1 file changed, 57 insertions(+)
> 

Why is DTS in the middle of driver patches? Please organize your work
correctly, see submitting patches in DT.

Best regards,
Krzysztof

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* Re: [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings
  2026-08-24 10:52   ` Krzysztof Kozlowski
@ 2026-08-25  6:05     ` Paul Elder
  2026-08-25  6:15       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 9+ messages in thread
From: Paul Elder @ 2026-08-25  6:05 UTC (permalink / raw)
  To: Krzysztof Kozlowski, laurent.pinchart
  Cc: michael.riesch, xuhf, stefan.klug, mehdi.djait, linux-media,
	linux-arm-kernel, linux-rockchip, linux-kernel, Heiko Stuebner

Hi Krzysztof,

Quoting Krzysztof Kozlowski (2026-08-24 19:52:26)
> On 24/08/2026 11:03, Paul Elder wrote:
> > Add documentation for the Rockchip rkisp2 bindings. This is meant to
> > support multiple versions of Rockchip ISPs going forward, including the
> > 2.x series and 3.x series. The current version only adds the compatible
> > for the RK3588, which is a 3.0.
> > 
> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> 
> Testing is disabled for some time but it's not really a reason to opt out.
> 
> Please use scripts/get_maintainers.pl to get a list of necessary people
> and lists to CC. It might happen, that command when run on an older
> kernel, gives you outdated entries. Therefore please be sure you base
> your patches on recent Linux kernel.
> 
> Tools like b4 or scripts/get_maintainer.pl provide you proper list of
> people, so fix your workflow. Tools might also fail if you work on some
> ancient tree (don't, instead use mainline) or work on fork of kernel
> (don't, instead use mainline). Just use b4 and everything should be
> fine, although remember about `b4 prep --auto-to-cc` if you added new
> patches to the patchset.

I did not use b4 but I did use scripts/get_maintainer.pl via --cc-cmd on git
send-email. Is that not expected to work?

> You missed at least devicetree list (maybe more), so this won't be
> tested by automated tooling. Performing review on untested code might be
> a waste of time.
> 
> Please kindly resend and include all necessary To/Cc entries.
> 
> A nit, subject: drop second/last, redundant "bindings". The
> "dt-bindings" prefix is already stating that these are bindings.
> See also:
> https://elixir.bootlin.com/linux/v7.1-rc7/source/Documentation/devicetree/bindings/submitting-patches.rst#L23

Indeed I missed that, my bad.


Paul

> 
> 
> Best regards,
> Krzysztof

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

* Re: [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings
  2026-08-25  6:05     ` Paul Elder
@ 2026-08-25  6:15       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 9+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-25  6:15 UTC (permalink / raw)
  To: Paul Elder, laurent.pinchart
  Cc: michael.riesch, xuhf, stefan.klug, mehdi.djait, linux-media,
	linux-arm-kernel, linux-rockchip, linux-kernel, Heiko Stuebner

On 25/08/2026 08:05, Paul Elder wrote:
> Hi Krzysztof,
> 
> Quoting Krzysztof Kozlowski (2026-08-24 19:52:26)
>> On 24/08/2026 11:03, Paul Elder wrote:
>>> Add documentation for the Rockchip rkisp2 bindings. This is meant to
>>> support multiple versions of Rockchip ISPs going forward, including the
>>> 2.x series and 3.x series. The current version only adds the compatible
>>> for the RK3588, which is a 3.0.
>>>
>>> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
>>
>> Testing is disabled for some time but it's not really a reason to opt out.
>>
>> Please use scripts/get_maintainers.pl to get a list of necessary people
>> and lists to CC. It might happen, that command when run on an older
>> kernel, gives you outdated entries. Therefore please be sure you base
>> your patches on recent Linux kernel.
>>
>> Tools like b4 or scripts/get_maintainer.pl provide you proper list of
>> people, so fix your workflow. Tools might also fail if you work on some
>> ancient tree (don't, instead use mainline) or work on fork of kernel
>> (don't, instead use mainline). Just use b4 and everything should be
>> fine, although remember about `b4 prep --auto-to-cc` if you added new
>> patches to the patchset.
> 
> I did not use b4 but I did use scripts/get_maintainer.pl via --cc-cmd on git
> send-email. Is that not expected to work?

Works on people setups, but whatever you produced was not sent to right
folks, so I doubt that you actually scripted it, e.g. like [1].
Otherwise why script would remove ONLY one specific address from entire
list it gets?

[1] https://lore.kernel.org/all/?q=git_send_email

Best regards,
Krzysztof

_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

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

end of thread, other threads:[~2026-08-25  6:16 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-24  9:03 [PATCH v2 0/5] media: rockchip: rkisp2: Add driver for ISP on Rk3588 Paul Elder
2026-08-24  9:03 ` [PATCH v2 1/5] media: dt-bindings: Add rockchip rkisp2 bindings Paul Elder
2026-08-24 10:52   ` Krzysztof Kozlowski
2026-08-25  6:05     ` Paul Elder
2026-08-25  6:15       ` Krzysztof Kozlowski
2026-08-24  9:03 ` [PATCH v2 2/5] arm64: dts: rockchip: add ISP nodes to rk3588 Paul Elder
2026-08-24 10:52   ` Krzysztof Kozlowski
2026-08-24  9:04 ` [PATCH v2 4/5] media: rkisp2: Add parameters output video node Paul Elder
2026-08-24  9:04 ` [PATCH v2 5/5] media: rkisp2: Add statistics capture " Paul Elder

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