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