On 10/07/2026 10:04, Loic Poulain wrote:
Add an image processing driver for the Qualcomm Offline Processing Engine
(OPE). OPE is a memory-to-memory ISP block that converts raw Bayer
frames to YUV, performing white balance, demosaic, chroma enhancement,
color correction and downscaling.

The hardware architecture consists of Fetch Engines and Write Engines,
connected through intermediate pipeline modules for pix processing.

The driver exposes three video nodes per pipeline instance:
   - ope_input: Bayer RAW input (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
   - ope_disp_output: YUV output     (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
   - ope_params: ISP parameters (V4L2_BUF_TYPE_META_OUTPUT)

Hardware features:
   - Stripe-based processing (up to 336 pixels wide per stripe)
   - White balance (CLC_WB)
   - Demosaic / Bayer-to-RGB (CLC_DEMO)
   - RGB-to-YUV conversion (CLC_CHROMA_ENHAN)
   - Color correction matrix (CLC_CC)
   - MN downscaler for chroma and luma planes

Default configuration values are based on public standards such as BT.601.

Processing Model:
OPE processes frames in stripes of up to 336 pixels. Therefore, frames must
be split into stripes for processing. Each stripe is configured after the
previous one has been acquired (double buffered registers). To minimize
inter-stripe latency, stripe configurations are generated ahead of time.

Signed-off-by: Loic Poulain <[email protected]>
Co-developed-by: Hans de Goede <[email protected]>
Signed-off-by: Hans de Goede <[email protected]>
---
  drivers/media/platform/qcom/camss/Kconfig     |   18 +
  drivers/media/platform/qcom/camss/Makefile    |    4 +
  drivers/media/platform/qcom/camss/camss-ope.c | 3245 +++++++++++++++++++++++++

I think this should be in a sub-directory.

  3 files changed, 3267 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/Kconfig 
b/drivers/media/platform/qcom/camss/Kconfig
index 
4eda48cb1adf049a7fb6cb59b9da3c0870fe57f4..895fc57a679655fcb6f308be1565dc6b77bbbd67
 100644
--- a/drivers/media/platform/qcom/camss/Kconfig
+++ b/drivers/media/platform/qcom/camss/Kconfig
@@ -7,3 +7,21 @@ config VIDEO_QCOM_CAMSS
        select VIDEO_V4L2_SUBDEV_API
        select VIDEOBUF2_DMA_SG
        select V4L2_FWNODE
+
+config VIDEO_QCOM_CAMSS_OPE
+       tristate "Qualcomm Offline Processing Engine (OPE) driver"
+       depends on VIDEO_QCOM_CAMSS
+       depends on V4L_PLATFORM_DRIVERS
+       depends on VIDEO_DEV
+       depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+       select V4L2_ISP
+       select VIDEOBUF2_DMA_CONTIG
+       select VIDEOBUF2_VMALLOC
+       help
+         Enable support for the Qualcomm Offline Processing Engine (OPE).
+         OPE is a memory-to-memory ISP block that converts raw Bayer frames
+         to YUV, performing white balance, demosaic, chroma enhancement and
+         downscaling. Found on QCM2290 and related SoCs.
+
+         To compile this driver as a module, choose M here: the module
+         will be called qcom-camss-ope.
diff --git a/drivers/media/platform/qcom/camss/Makefile 
b/drivers/media/platform/qcom/camss/Makefile
index 
5678621efb6780b67a043ec8a2e914cce02d9b98..422eebc0a86301de3f39c743fbc06c437b17ac9a
 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -31,3 +31,7 @@ qcom-camss-objs += \
                camss-params.o \
obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o
+
+qcom-camss-ope-objs := camss-ope.o
+
+obj-$(CONFIG_VIDEO_QCOM_CAMSS_OPE) += qcom-camss-ope.o
diff --git a/drivers/media/platform/qcom/camss/camss-ope.c 
b/drivers/media/platform/qcom/camss/camss-ope.c
new file mode 100644
index 
0000000000000000000000000000000000000000..2c0d68cf1a637c998ebe4d3afb1fa6dbdb68f029
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ope.c
@@ -0,0 +1,3245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-ope.c
+ *
+ * Qualcomm MSM Camera Subsystem - Offline Processing Engine
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+/*
+ * This driver provides driver implementation for the Qualcomm Offline
+ * Processing Engine (OPE). OPE is a memory-to-memory hardware block
+ * designed for image processing on a source frame. Typically, the input
+ * frame originates from the SoC CSI capture path, though not limited to.
+ *
+ * The hardware architecture consists of Fetch Engines and Write Engines,
+ * connected through intermediate pipeline modules:
+ *   [FETCH ENGINES] => [Pipeline Modules] => [WRITE ENGINES]
+ *
+ * Current Configuration:
+ *     Fetch Engine: One fetch engine is used for Bayer frame input.
+ *     Write Engines: Two display write engines for Y and UV planes output.
+ *
+ * Only a subset of the pipeline modules are enabled:
+ *   CLC_WB: White balance for channel gain configuration
+ *   CLC_DEMO: Demosaic for Bayer to RGB conversion
+ *   CLC_CC: Color Correct, coefficient based RGB correction
+ *   CLC_CHROMA_ENHAN: for RGB to YUV conversion
+ *   CLC_DOWNSCALE*: Downscaling for UV (YUV444 -> YUV422/YUV420) and YUV 
planes
+ *
+ * Default configuration values are based on public standards such as BT.601.
+ *
+ * Processing Model:
+ * OPE processes frames in stripes of up to 336 pixels. Therefore, frames must
+ * be split into stripes for processing. Each stripe is configured after the
+ * previous one has been acquired (double buffered registers). To minimize
+ * inter-stripe latency, the stripe configurations are generated ahead of time.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interconnect.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+
+#include <media/v4l2-device.h>
+#include <media/media-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-rect.h>
+
+#include "camss-pipeline.h"
+
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <uapi/linux/camss-config.h>
+
+#include "camss-params.h"
+
+#define OPE_NAME       "qcom-camss-ope"
+
+/* Format descriptor */
+struct ope_fmt {
+       u32             fourcc;
+       unsigned int    depth;
+       unsigned int    align;
+       unsigned int    num_planes;
+       u32             mbus_code;
+       unsigned int    c_hsub;
+       unsigned int    c_vsub;
+};
+
+/* Per-queue format state */
+struct ope_fmt_state {
+       const struct ope_fmt    *fmt;
+       unsigned int            width;
+       unsigned int            height;
+       struct v4l2_rect        crop;
+       unsigned int            bytesperline;
+       unsigned int            sizeimage;
+       enum v4l2_colorspace    colorspace;
+       enum v4l2_xfer_func     xfer_func;
+       enum v4l2_ycbcr_encoding        ycbcr_enc;
+       enum v4l2_quantization  quantization;
+       unsigned int            sequence;
+       struct v4l2_fract       timeperframe;
+};
+
+/* -------- Register layout -------- */
+
+#define OPE_TOP_HW_VERSION                                     0x000
+#define                OPE_TOP_HW_VERSION_STEP         GENMASK(15, 0)
+#define                OPE_TOP_HW_VERSION_REV          GENMASK(27, 16)
+#define                OPE_TOP_HW_VERSION_GEN          GENMASK(31, 28)
+#define OPE_TOP_RESET_CMD                                      0x004
+#define                OPE_TOP_RESET_CMD_HW            BIT(0)
+#define                OPE_TOP_RESET_CMD_SW            BIT(1)
+#define OPE_TOP_IRQ_STATUS                                     0x014
+#define OPE_TOP_IRQ_MASK                                       0x018
+#define                OPE_TOP_IRQ_STATUS_RST_DONE     BIT(0)
+#define                OPE_TOP_IRQ_STATUS_WE           BIT(1)
+#define                OPE_TOP_IRQ_STATUS_FE           BIT(2)
+#define                OPE_TOP_IRQ_STATUS_VIOL         BIT(3)
+#define                OPE_TOP_IRQ_STATUS_IDLE         BIT(4)
+#define OPE_TOP_IRQ_CLEAR                                      0x01c
+#define OPE_TOP_IRQ_CMD                                                0x024
+#define                OPE_TOP_IRQ_CMD_CLEAR           BIT(0)
+#define OPE_TOP_VIOLATION_STATUS                               0x028
+
+/* Fetch engine */
+#define OPE_BUS_RD_INPUT_IF_IRQ_MASK                           0x00c
+#define OPE_BUS_RD_INPUT_IF_IRQ_CLEAR                          0x010
+#define OPE_BUS_RD_INPUT_IF_IRQ_CMD                            0x014
+#define                OPE_BUS_RD_INPUT_IF_IRQ_CMD_CLEAR       BIT(0)
+#define OPE_BUS_RD_INPUT_IF_IRQ_STATUS                         0x018
+#define OPE_BUS_RD_INPUT_IF_CMD                                        0x01c
+#define                OPE_BUS_RD_INPUT_IF_CMD_GO_CMD          BIT(0)
+#define OPE_BUS_RD_CLIENT_0_CORE_CFG                           0x050
+#define                OPE_BUS_RD_CLIENT_0_CORE_CFG_EN BIT(0)
+#define OPE_BUS_RD_CLIENT_0_CCIF_META_DATA                     0x054
+#define                OPE_BUS_RD_CLIENT_0_CCIF_MD_PIX_PATTERN GENMASK(7, 2)
+#define OPE_BUS_RD_CLIENT_0_ADDR_IMAGE                         0x058
+#define OPE_BUS_RD_CLIENT_0_RD_BUFFER_SIZE                     0x05c
+#define OPE_BUS_RD_CLIENT_0_RD_STRIDE                          0x060
+#define OPE_BUS_RD_CLIENT_0_UNPACK_CFG_0                       0x064
+
+/* Write engines */
+#define OPE_BUS_WR_INPUT_IF_IRQ_MASK_0                         0x018
+#define OPE_BUS_WR_INPUT_IF_IRQ_MASK_1                         0x01c
+#define OPE_BUS_WR_INPUT_IF_IRQ_CLEAR_0                                0x020
+#define OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0                       0x028
+#define                OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_RUP_DONE       BIT(0)
+#define                OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_BUF_DONE       BIT(8)
+#define                OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_CONS_VIOL      BIT(28)
+#define                OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_VIOL           BIT(30)
+#define                OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_IMG_SZ_VIOL    BIT(31)
+#define OPE_BUS_WR_INPUT_IF_IRQ_CMD                            0x030
+#define                OPE_BUS_WR_INPUT_IF_IRQ_CMD_CLEAR       BIT(0)
+#define OPE_BUS_WR_VIOLATION_STATUS                            0x064
+#define OPE_BUS_WR_IMAGE_SIZE_VIOLATION_STATUS                 0x070
+#define OPE_BUS_WR_CLIENT_CFG(c)                               (0x200 + (c) * 
0x100)
+#define                OPE_BUS_WR_CLIENT_CFG_EN                BIT(0)
+#define                OPE_BUS_WR_CLIENT_CFG_AUTORECOVER       BIT(4)
+#define OPE_BUS_WR_CLIENT_ADDR_IMAGE(c)                                (0x204 
+ (c) * 0x100)
+#define OPE_BUS_WR_CLIENT_IMAGE_CFG_0(c)                       (0x20c + (c) * 
0x100)
+#define OPE_BUS_WR_CLIENT_IMAGE_CFG_1(c)                       (0x210 + (c) * 
0x100)
+#define OPE_BUS_WR_CLIENT_IMAGE_CFG_2(c)                       (0x214 + (c) * 
0x100)
+#define OPE_BUS_WR_CLIENT_PACKER_CFG(c)                                (0x218 
+ (c) * 0x100)
+#define OPE_BUS_WR_CLIENT_MAX  4
+
+/* Pipeline modules */
+#define OPE_PP_CLC_WB_GAIN_MODULE_CFG                          (0x200 + 0x60)

Can you just map the individual blocks so that we can interrogate HW_VERSION HW_STATUS and friends ? Those regs usually come first. I can see useful debugfs and/or dev_dbg() usages of those data.
+#define                OPE_PP_CLC_WB_GAIN_MODULE_CFG_EN        BIT(0)
+#define OPE_PP_CLC_WB_GAIN_WB_CFG(ch)                          (0x200 + 0x68 + 
4 * (ch))
+#define                OPE_PP_CLC_WB_GAIN_WB_CFG_GAIN          GENMASK(14, 0)
+#define OPE_PP_CLC_WB_GAIN_WB_SUB_CFG(ch)                      (0x200 + 0x74 + 
4 * (ch))
+#define                OPE_PP_CLC_WB_GAIN_WB_SUB_CFG_VAL       GENMASK(31, 20)
+#define OPE_PP_CLC_WB_GAIN_WB_ADD_CFG(ch)                      (0x200 + 0x80 + 
4 * (ch))
+#define                OPE_PP_CLC_WB_GAIN_WB_ADD_CFG_VAL       GENMASK(31, 20)
+
+#define OPE_PP_CLC_CC_BASE                                     0x400

Is this actually the correct register base for CCM ?

I think you should check again.

Same comment for each of these blocks HW_VERSION should be the first register.

---
bod

Reply via email to