Re: [PATCH v4 6/7] media: qcom: camss: Add CAMSS Offline Processing Engine driver

Bryan O'Donoghue <[email protected]>
Newsgroups org.kernel.vger.linux-hardening,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.linux-media
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.