On Mon, Jul 13, 2026 at 3:05 PM Bryan O'Donoghue
<[email protected]> wrote:
>
> 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.
You mean a OPE specific directory or a kind of offline engine generic one like:
drivers/media/platform/qcom/camss/offline/ope.c ?
>
> > 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.
Sure, will do.
> > +#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 ?
Nope, you're right.
>
> I think you should check again.
>
> Same comment for each of these blocks HW_VERSION should be the first
> register.
Ack I will define generic macro for them.
Regards,
Loic