mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 00/45] media: qcom: camss: add IFE pixel path support
@ 2026-10-06 22:39 Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 01/45] media: qcom: camss: support CSID streams on one source pad Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (45 more replies)
  0 siblings, 46 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

This series adds pixel-path support for three platforms: SM8250,
SA8775P, and QCS8300.

To keep the implementation manageable across platforms, instead of a
single monolithic VFE abstraction, more granular hardware abstractions
are introduced: ife-top, ife-camif, ife-bus, and ife-modules.

We initially attempted to modify and extend the existing VFE code, but
the changes touched every file and led to a third generation of VFE
support (multiple pads, etc.) being added on top of the existing two.
The result was a crowded, hard-to-review camss-vfe.c carrying three
hardware generations in a single file. Because of this, we introduced
a new sub-device, IFE (the new name for what was called VFE on older
platforms). The IFE sub-device supports the multistream API and
multiple source/sink pads for handling parameters and statistics.
Stream routing to each output is handled through the streams API. It
also defines, per pad, the basic configuration shared across all
platforms (WM IDs, composite IRQs, etc.), as described in the patches
that follow.

On the CSID side, a single source pad can now expose several hardware
outputs as separate streams, and the gen2 CSID backend gains a routed
pixel path (IPP) alongside the existing RDI paths. The TPG internal
path is also fixed to use one CSI-2 lane, avoiding packet header ECC
errors, and gains explicit links into the new IFE topology.

QCS8300 shares the same IFE/CSID pixel path hardware as SA8775P, with
the only difference being that QCS8300 has 3 CSIPHYs (0-2) instead of
SA8775P's 4 (0-3).

The IFE parameter and statistics blocks are now built on top of the
generic v4l2-isp parameter/statistics block validation introduced for
the OPE driver.

The selection of pixel path topology is done using a different
compatible for the same platform. This will most probably not be
accepted, but as of today it is the only way to support both
topologies.

NOTE: Bryan has posted RFC changes for pixel path params and
statistics. This series was ready to be posted at that time, so it is
not rebased on top of Bryan's implementation. Both implementations
share common parts, but take a different direction towards enabling
the pixel path: Bryan's series extends camss-vfe, while this series
introduces the new camss-ife sub-device described above.

Depends-on:
https://lore.kernel.org/all/20260925-camss-isp-ope-v10-0-2622411034cb@oss.qualcomm.com/
https://lore.kernel.org/all/20260911062213.195007-1-gjorgji.rosikopulos@oss.qualcomm.com/

Atanas Filipov (33):
  media: qcom: camss: report probe failures
  dt-bindings: media: qcom,sm8250-camss: add pixel path variant
  media: qcom: camss: keep CSIPHY lanes active while streaming
  media: qcom: camss: add IFE subdevice interface
  media: qcom: camss: sm8250: add IFE BUS v20x
  media: qcom: camss: sm8250: add IFE CamIF v10x
  media: qcom: camss: sm8250: add IFE scaler v10x
  media: qcom: camss: sm8250: add IFE module engine v10x
  media: qcom: camss: sm8250: add IFE top v20x
  media: qcom: camss: sm8250: add IFE Lite top v10x
  media: qcom: camss: sm8250: add IFE pad descriptors
  media: qcom: camss: formats: add pixel format lookup helpers
  media: qcom: camss: video: use helpers for line-based formats
  media: qcom: camss: video: add managed streaming callbacks
  media: qcom: camss: add common IFE subdevice core
  media: qcom: camss: integrate IFE devices
  media: qcom: camss: sm8250: add pixel path resources
  media: uapi: add Qualcomm IFE statistics
  media: v4l2: add Qualcomm IFE statistics format
  media: qcom: camss: add IFE statistics output
  media: uapi: add Qualcomm IFE parameters
  media: v4l2: add Qualcomm IFE parameters format
  media: qcom: camss: sm8250: apply IFE parameters
  dt-bindings: media: qcom,sa8775p-camss: add pixel path variant
  media: qcom: camss: sa8775p: add IFE BUS v3.1x
  media: qcom: camss: sa8775p: add IFE scaler v2.3x
  media: qcom: camss: sa8775p: add IFE top v4.1x
  media: qcom: camss: sa8775p: add IFE lite top v3.0x
  media: qcom: camss: sa8775p: add IFE pad descriptors
  media: qcom: camss: sa8775p: add IFE completion groups
  media: qcom: camss: sa8775p: add CSID pixel path
  media: qcom: camss: sa8775p: add IFE modules v4.1x
  media: qcom: camss: sa8775p: add pixel path resources

Gjorgji Rosikopulos (8):
  media: qcom: camss: support CSID streams on one source pad
  media: qcom: camss: add CSID gen2 pixel path
  media: qcom: camss: sa8775p: add CCM parameters
  media: qcom: camss: sa8775p: add LSC parameters
  media: qcom: camss: sa8775p: add gamma parameters
  media: qcom: camss: sa8775p: add white balance parameters
  media: qcom: camss: sa8775p: add AWB BG parameters
  media: qcom: camss: ife: queue params buffers in FIFO order

Prashant Shrotriya (4):
  media: qcom: camss: create TPG links for IFE
  media: qcom: camss: use one lane for TPG internal path
  dt-bindings: media: qcom,qcs8300-camss: add pixel path variant
  media: qcom: camss: sa8775p: add pixel path resources for QCS8300

 .../bindings/media/qcom,qcs8300-camss.yaml    |    4 +-
 .../bindings/media/qcom,sa8775p-camss.yaml    |    4 +-
 .../bindings/media/qcom,sm8250-camss.yaml     |    4 +-
 drivers/media/platform/qcom/camss/Kconfig     |    1 +
 drivers/media/platform/qcom/camss/Makefile    |   14 +
 .../platform/qcom/camss/camss-csid-gen2.c     |  158 +-
 .../platform/qcom/camss/camss-csid-gen3.c     |  265 ++-
 .../media/platform/qcom/camss/camss-csid.c    |  148 +-
 .../media/platform/qcom/camss/camss-csid.h    |   10 +
 .../media/platform/qcom/camss/camss-csiphy.c  |   12 +-
 .../media/platform/qcom/camss/camss-csiphy.h  |    1 +
 .../media/platform/qcom/camss/camss-format.c  |   31 +
 .../media/platform/qcom/camss/camss-format.h  |    3 +
 .../platform/qcom/camss/camss-ife-bus-v20x.c  |  287 +++
 .../platform/qcom/camss/camss-ife-bus-v31x.c  |  267 +++
 .../qcom/camss/camss-ife-camif-v10x.c         |   59 +
 .../qcom/camss/camss-ife-camif-v10x.h         |   42 +
 .../qcom/camss/camss-ife-lite-top-v10x.c      |  387 ++++
 .../qcom/camss/camss-ife-lite-top-v30x.c      |  265 +++
 .../platform/qcom/camss/camss-ife-mod-v10x.c  |  603 ++++++
 .../platform/qcom/camss/camss-ife-mod-v41x.c  |  739 +++++++
 .../platform/qcom/camss/camss-ife-pads.c      |  607 ++++++
 .../platform/qcom/camss/camss-ife-params.c    |   74 +
 .../platform/qcom/camss/camss-ife-params.h    |   75 +
 .../qcom/camss/camss-ife-scale-v10x.c         |  148 ++
 .../qcom/camss/camss-ife-scale-v10x.h         |   15 +
 .../qcom/camss/camss-ife-scale-v23x.c         |  203 ++
 .../qcom/camss/camss-ife-scale-v23x.h         |   19 +
 .../platform/qcom/camss/camss-ife-top-v20x.c  |  570 ++++++
 .../platform/qcom/camss/camss-ife-top-v41x.c  |  384 ++++
 drivers/media/platform/qcom/camss/camss-ife.c | 1812 +++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |  413 ++++
 .../platform/qcom/camss/camss-tpg-gen1.c      |    2 +-
 drivers/media/platform/qcom/camss/camss-tpg.h |    1 +
 .../media/platform/qcom/camss/camss-video.c   |  299 ++-
 .../media/platform/qcom/camss/camss-video.h   |    5 +
 drivers/media/platform/qcom/camss/camss.c     | 1362 ++++++++++++-
 drivers/media/platform/qcom/camss/camss.h     |   47 +
 drivers/media/v4l2-core/v4l2-ioctl.c          |    6 +
 include/uapi/linux/qcom-camss-config.h        |  157 +-
 include/uapi/linux/qcom-camss-stats.h         |   62 +
 include/uapi/linux/videodev2.h                |    4 +
 42 files changed, 9355 insertions(+), 214 deletions(-)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-bus-v20x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-camif-v10x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-camif-v10x.h
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-lite-top-v10x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-lite-top-v30x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-pads.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-params.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-params.h
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-top-v20x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-top-v41x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife.h
 create mode 100644 include/uapi/linux/qcom-camss-stats.h

-- 
2.34.1


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

* [PATCH 01/45] media: qcom: camss: support CSID streams on one source pad
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 02/45] media: qcom: camss: add CSID gen2 pixel path Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (44 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

Allow a CSID resource to expose hardware outputs as streams on one
source pad. Keep the existing one-output-per-pad layout as the default.

Translate V4L2 stream IDs to hardware ports. Keep shared sink streams
enabled until their last source stream stops.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../media/platform/qcom/camss/camss-csid.c    | 129 +++++++++++-------
 .../media/platform/qcom/camss/camss-csid.h    |   6 +
 2 files changed, 83 insertions(+), 52 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index 2aab9104ac5b..bdac29b7af86 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -1385,6 +1385,7 @@ static bool csid_get_stream_csi2_desc(struct v4l2_mbus_frame_desc *frame_desc,
  * @state: V4L2 subdevice state
  * @remote_pad: Remote pad linked on the CSID sink pad, or NULL if unlinked
  * @pad: Source pad number the caller is enabling a stream on
+ * @stream: Source stream number
  * @format_dt: Data type derived from the sink format, used as a fallback
  *	       and sanity-checked against the discovered data type
  *
@@ -1393,7 +1394,7 @@ static bool csid_get_stream_csi2_desc(struct v4l2_mbus_frame_desc *frame_desc,
  */
 static struct v4l2_mbus_frame_desc_entry_csi2
 csid_get_stream_vc_dt(struct csid_device *csid, struct v4l2_subdev_state *state,
-		      struct media_pad *remote_pad, u32 pad, u8 format_dt)
+		      struct media_pad *remote_pad, u32 pad, u32 stream, u8 format_dt)
 {
 	struct v4l2_mbus_frame_desc_entry_csi2 desc_csi2 = { .dt = format_dt };
 	struct v4l2_mbus_frame_desc fd = { };
@@ -1404,7 +1405,8 @@ csid_get_stream_vc_dt(struct csid_device *csid, struct v4l2_subdev_state *state,
 			     pad, get_frame_desc, remote_pad->index, &fd))
 		return desc_csi2;
 
-	if (v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, NULL, &sink_stream))
+	if (v4l2_subdev_routing_find_opposite_end(&state->routing,
+						  pad, stream, NULL, &sink_stream))
 		return desc_csi2;
 
 	if (!csid_get_stream_csi2_desc(&fd, sink_stream, &desc_csi2)) {
@@ -1422,6 +1424,14 @@ csid_get_stream_vc_dt(struct csid_device *csid, struct v4l2_subdev_state *state,
 	return desc_csi2;
 }
 
+static u32 csid_get_hw_port(struct csid_device *csid, u32 pad, u32 stream)
+{
+	if (csid->res->source_streams_mode == CSID_STREAMS_SINGLE_PAD)
+		return stream;
+
+	return pad - MSM_CSID_PAD_FIRST_SRC;
+}
+
 /*
  * csid_pad_enable_streams - Enable one or more streams on a source pad
  * @sd: CSID V4L2 subdevice
@@ -1442,9 +1452,8 @@ static int csid_pad_enable_streams(struct v4l2_subdev *sd,
 	const struct csid_hw_ops *hw_ops = csid->res->hw_ops;
 	struct media_pad *remote_pad =
 		media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK]);
-	unsigned int hw_port = pad - MSM_CSID_PAD_FIRST_SRC;
-	const struct csid_format_info *format;
-	struct v4l2_mbus_frame_desc_entry_csi2 desc_csi2;
+	unsigned long streams;
+	unsigned int stream_id;
 	u64 sink_streams, propagate_mask;
 	int ret;
 
@@ -1478,12 +1487,19 @@ static int csid_pad_enable_streams(struct v4l2_subdev *sd,
 	csid->enabled_streams[MSM_CSID_PAD_SINK] |= sink_streams;
 	csid->enabled_streams[pad] |= streams_mask;
 
-	format = csid_get_fmt_entry(csid->res->formats->formats,
-				    csid->res->formats->nformats,
-				    csid->fmt[pad].code);
-	desc_csi2 = csid_get_stream_vc_dt(csid, state, remote_pad, pad, format->data_type);
-
-	hw_ops->enable_stream(csid, hw_port, desc_csi2.vc, desc_csi2.dt);
+	streams = streams_mask;
+	for_each_set_bit(stream_id, &streams, BITS_PER_TYPE(streams_mask)) {
+		const struct csid_format_info *format =
+			csid_get_fmt_entry(csid->res->formats->formats,
+					   csid->res->formats->nformats,
+					   csid->fmt[pad + stream_id].code);
+		struct v4l2_mbus_frame_desc_entry_csi2 desc_csi2 =
+			csid_get_stream_vc_dt(csid, state, remote_pad, pad, stream_id,
+					      format->data_type);
+		u32 hw_port = csid_get_hw_port(csid, pad, stream_id);
+
+		hw_ops->enable_stream(csid, hw_port, desc_csi2.vc, desc_csi2.dt);
+	}
 
 	if (propagate_mask && remote_pad) {
 		ret = v4l2_subdev_enable_streams(media_entity_to_v4l2_subdev(remote_pad->entity),
@@ -1492,7 +1508,12 @@ static int csid_pad_enable_streams(struct v4l2_subdev *sd,
 			csid->enabled_streams[MSM_CSID_PAD_SINK] &= ~propagate_mask;
 			csid->enabled_streams[pad] &= ~streams_mask;
 
-			hw_ops->disable_stream(csid, hw_port);
+			streams = streams_mask;
+			for_each_set_bit(stream_id, &streams, BITS_PER_TYPE(streams_mask)) {
+				u32 hw_port = csid_get_hw_port(csid, pad, stream_id);
+
+				hw_ops->disable_stream(csid, hw_port);
+			}
 
 			return ret;
 		}
@@ -1502,18 +1523,17 @@ static int csid_pad_enable_streams(struct v4l2_subdev *sd,
 }
 
 /*
- * csid_sink_streams_in_use - Compute the subset of sink streams still
- *			      referenced by a source pad other than @pad
+ * csid_sink_streams_in_use - Find sink streams used by another source stream
  * @csid: CSID device
  * @state: V4L2 subdevice state
- * @pad: Source pad to exclude from the check
+ * @pad: Source pad being disabled
+ * @streams_mask: Source streams being disabled
  * @sink_streams: Candidate sink streams to check
  *
- * Return the subset of @sink_streams still referenced by some other source
- * pad
+ * Return the subset of @sink_streams still in use
  */
 static u64 csid_sink_streams_in_use(struct csid_device *csid, struct v4l2_subdev_state *state,
-				    u32 pad, u64 sink_streams)
+				    u32 pad, u64 streams_mask, u64 sink_streams)
 {
 	u64 in_use = 0;
 	unsigned int i;
@@ -1523,7 +1543,7 @@ static u64 csid_sink_streams_in_use(struct csid_device *csid, struct v4l2_subdev
 		u64 other_sink_streams;
 
 		if (i == pad)
-			continue;
+			other_streams &= ~streams_mask;
 
 		other_sink_streams = v4l2_subdev_state_xlate_streams(state, i, MSM_CSID_PAD_SINK,
 								     &other_streams);
@@ -1540,12 +1560,8 @@ static u64 csid_sink_streams_in_use(struct csid_device *csid, struct v4l2_subdev
  * @pad: Pad number
  * @streams_mask: Bitmask of v4l2 streams to disable
  *
- * The v4l2 core only calls this on a source pad (v4l2_subdev_disable_streams()
- * rejects sink pads with -EOPNOTSUPP before reaching the driver), so @pad is
- * not checked here. Each source pad only ever carries stream 0.
- *
  * A sink stream is only disabled, and propagated upstream to disable it there
- * too, once no source pad references it any more.
+ * too, once no source stream references it any more.
  *
  * Return 0 on success or a negative error code otherwise
  */
@@ -1557,16 +1573,18 @@ static int csid_pad_disable_streams(struct v4l2_subdev *sd,
 	const struct csid_hw_ops *hw_ops = csid->res->hw_ops;
 	struct media_pad *remote_pad =
 		media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK]);
-	unsigned int hw_port = pad - MSM_CSID_PAD_FIRST_SRC;
+	unsigned long streams = streams_mask;
+	unsigned int stream_id;
 	u64 sink_streams, disable_sink_streams;
 	int ret = 0;
 
 	sink_streams = v4l2_subdev_state_xlate_streams(state, pad, MSM_CSID_PAD_SINK,
 						       &streams_mask);
 
-	/* Keep a sink stream active as long as any other source pad still uses it. */
+	/* Keep a sink stream active as long as another source stream uses it. */
 	disable_sink_streams = sink_streams &
-				~csid_sink_streams_in_use(csid, state, pad, sink_streams);
+				~csid_sink_streams_in_use(csid, state, pad, streams_mask,
+						  sink_streams);
 
 	if (disable_sink_streams && remote_pad) {
 		ret = v4l2_subdev_disable_streams(media_entity_to_v4l2_subdev(remote_pad->entity),
@@ -1576,7 +1594,11 @@ static int csid_pad_disable_streams(struct v4l2_subdev *sd,
 				"Failed to disable stream on remote pad: %d\n", ret);
 	}
 
-	hw_ops->disable_stream(csid, hw_port);
+	for_each_set_bit(stream_id, &streams, BITS_PER_TYPE(streams_mask)) {
+		u32 hw_port = csid_get_hw_port(csid, pad, stream_id);
+
+		hw_ops->disable_stream(csid, hw_port);
+	}
 
 	csid->enabled_streams[pad] &= ~streams_mask;
 	csid->enabled_streams[MSM_CSID_PAD_SINK] &= ~disable_sink_streams;
@@ -1606,6 +1628,8 @@ static int csid_set_routing(struct v4l2_subdev *sd,
 			    enum v4l2_subdev_format_whence which,
 			    struct v4l2_subdev_krouting *routing)
 {
+	enum v4l2_subdev_routing_restriction restrictions =
+		V4L2_SUBDEV_ROUTING_NO_SOURCE_STREAM_MIX | V4L2_SUBDEV_ROUTING_NO_N_TO_1;
 	struct csid_device *csid = v4l2_get_subdevdata(sd);
 	unsigned int i;
 	int ret;
@@ -1613,14 +1637,15 @@ static int csid_set_routing(struct v4l2_subdev *sd,
 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE && csid->enabled_streams[MSM_CSID_PAD_SINK])
 		return -EBUSY;
 
-	for (i = 0; i < routing->num_routes; i++)
-		if (routing->routes[i].source_stream != 0)
-			return -EINVAL;
+	if (csid->res->source_streams_mode != CSID_STREAMS_SINGLE_PAD) {
+		for (i = 0; i < routing->num_routes; i++)
+			if (routing->routes[i].source_stream != 0)
+				return -EINVAL;
+
+		restrictions |= V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING;
+	}
 
-	ret = v4l2_subdev_routing_validate(sd, routing,
-					   V4L2_SUBDEV_ROUTING_NO_SOURCE_STREAM_MIX |
-					   V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING |
-					   V4L2_SUBDEV_ROUTING_NO_N_TO_1);
+	ret = v4l2_subdev_routing_validate(sd, routing, restrictions);
 	if (ret)
 		return ret;
 
@@ -1635,9 +1660,8 @@ static int csid_set_routing(struct v4l2_subdev *sd,
  * @stream: stream from which format is requested
  * @which: TRY or ACTIVE format
  *
- * Same as __csid_get_format(), but honors @stream for TRY-state lookups.
- * For ACTIVE state, csid->fmt[] is indexed by pad + stream. @stream is
- * always 0 and @pad selects the RDI channel (0-3).
+ * Same as __csid_get_format(), but honors @stream. For ACTIVE state,
+ * csid->fmt[] is indexed by pad + stream.
  *
  * Return pointer to TRY or ACTIVE format structure
  */
@@ -1750,16 +1774,8 @@ static const struct v4l2_subdev_ops csid_streams_v4l2_ops = {
  * @sd: CSID V4L2 subdevice
  * @state: V4L2 subdev state
  *
- * source_stream is always 0: each source pad MSM_CSID_PAD_FIRST_SRC + i
- * links to its own independent downstream subdev, and a link's sink side is
- * validated against the implicit stream 0 exposed by any subdev without
- * V4L2_SUBDEV_FL_STREAMS (see v4l2_link_validate_get_streams()) - every
- * downstream VFE line is such a subdev.
- *
- * All source pads route from sink_stream 0 by default, fanning the single
- * incoming stream out to every port; a multi-VC source is supported by
- * remapping each route's sink_stream via .set_routing, leaving
- * source_pad/source_stream untouched.
+ * Multi-pad mode exposes one stream on each source pad. Single-pad mode
+ * exposes all output streams on the first source pad.
  *
  * Return 0 on success or a negative error code otherwise
  */
@@ -1780,8 +1796,13 @@ static int csid_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
 	for (i = 0; i < num_routes; i++) {
 		routes[i].sink_pad = MSM_CSID_PAD_SINK;
 		routes[i].sink_stream = 0;
-		routes[i].source_pad = MSM_CSID_PAD_FIRST_SRC + i;
-		routes[i].source_stream = 0;
+		if (csid->res->source_streams_mode == CSID_STREAMS_SINGLE_PAD) {
+			routes[i].source_pad = MSM_CSID_PAD_FIRST_SRC;
+			routes[i].source_stream = i;
+		} else {
+			routes[i].source_pad = MSM_CSID_PAD_FIRST_SRC + i;
+			routes[i].source_stream = 0;
+		}
 		routes[i].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
 	}
 
@@ -1821,11 +1842,15 @@ int msm_csid_register_entity(struct csid_device *csid,
 	struct media_pad *pads = csid->pads;
 	struct device *dev = csid->camss->dev;
 	bool streams_api = csid->res->streams_enable;
-	unsigned int num_pads = csid_is_lite(csid) ? MSM_CSID_PADS_NUM :
-						       MSM_CSID_PADS_NUM - 1;
+	unsigned int num_pads;
 	int i;
 	int ret;
 
+	if (csid->res->source_streams_mode == CSID_STREAMS_SINGLE_PAD)
+		num_pads = MSM_CSID_PAD_FIRST_SRC + 1;
+	else
+		num_pads = csid_is_lite(csid) ? MSM_CSID_PADS_NUM : MSM_CSID_PADS_NUM - 1;
+
 	v4l2_subdev_init(sd, streams_api ? &csid_streams_v4l2_ops : &csid_v4l2_ops);
 	sd->internal_ops = streams_api ? &csid_streams_internal_ops
 					: &csid_v4l2_internal_ops;
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index 30674eac51a7..f079956bae8e 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -29,6 +29,11 @@
 
 #define CSID_RESET_TIMEOUT_MS 500
 
+enum csid_streams_mode {
+	CSID_STREAMS_MULTI_PAD = 0,
+	CSID_STREAMS_SINGLE_PAD,
+};
+
 enum csid_testgen_mode {
 	CSID_PAYLOAD_MODE_DISABLED = 0,
 	CSID_PAYLOAD_MODE_INCREMENTING = 1,
@@ -166,6 +171,7 @@ struct csid_hw_ops {
 struct csid_subdev_resources {
 	bool is_lite;
 	bool streams_enable;
+	enum csid_streams_mode source_streams_mode;
 	const struct csid_hw_ops *hw_ops;
 	const struct parent_dev_ops *parent_dev_ops;
 	const struct csid_formats *formats;
-- 
2.34.1


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

* [PATCH 02/45] media: qcom: camss: add CSID gen2 pixel path
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 01/45] media: qcom: camss: support CSID streams on one source pad Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 03/45] media: qcom: camss: report probe failures Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (43 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

Add IPP register programming and interrupt handling for the full-frame
pixel output on non-lite gen2 CSID. Use stream 3 for the pixel path and
expose all four output routes.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-csid-gen2.c     | 158 +++++++++++++++++-
 .../media/platform/qcom/camss/camss-csid.c    |  13 +-
 2 files changed, 152 insertions(+), 19 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
index 3af043c38786..72a2b7fc5dd0 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
@@ -12,6 +12,8 @@
 #include <linux/kernel.h>
 #include <linux/of.h>
 
+#include <media/mipi-csi2.h>
+
 #include "camss-csid.h"
 #include "camss-csid-gen2.h"
 #include "camss.h"
@@ -22,6 +24,36 @@
  * alternate register layout.
  */
 
+#define CSID_PIX_STREAM_ID	3
+
+static bool csid_is_pix_stream(struct csid_device *csid, u8 stream_id)
+{
+	return stream_id == CSID_PIX_STREAM_ID && !csid_is_lite(csid);
+}
+
+static u8 csid_pix_dt_to_decode_format(u8 dt)
+{
+	switch (dt) {
+	case MIPI_CSI2_DT_RAW6:
+		return DECODE_FORMAT_UNCOMPRESSED_6_BIT;
+	case MIPI_CSI2_DT_YUV422_8B:
+	case MIPI_CSI2_DT_RAW8:
+		return DECODE_FORMAT_UNCOMPRESSED_8_BIT;
+	case MIPI_CSI2_DT_RAW10:
+		return DECODE_FORMAT_UNCOMPRESSED_10_BIT;
+	case MIPI_CSI2_DT_RAW12:
+		return DECODE_FORMAT_UNCOMPRESSED_12_BIT;
+	case MIPI_CSI2_DT_RAW14:
+		return DECODE_FORMAT_UNCOMPRESSED_14_BIT;
+	case MIPI_CSI2_DT_RAW16:
+		return DECODE_FORMAT_UNCOMPRESSED_16_BIT;
+	case MIPI_CSI2_DT_RAW20:
+		return DECODE_FORMAT_UNCOMPRESSED_20_BIT;
+	default:
+		return DECODE_FORMAT_PAYLOAD_ONLY;
+	}
+}
+
 #define CSID_RST_STROBES	0x10
 #define		RST_STROBES	0
 
@@ -120,6 +152,33 @@
 #define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi)		((csid_is_lite(csid) ? 0x230 : 0x330)\
 							+ 0x100 * (rdi))
 
+#define CSID_IPP_CFG0		0x200
+#define		IPP_CFG0_FMT_MEASURE_EN		0
+#define		IPP_CFG0_TIMESTAMP_EN		1
+#define		IPP_CFG0_DECODE_FORMAT		12
+#define		IPP_CFG0_DATA_TYPE		16
+#define		IPP_CFG0_VIRTUAL_CHANNEL	22
+#define		IPP_CFG0_DT_ID			27
+#define		IPP_CFG0_ENABLE			31
+
+#define CSID_IPP_CFG1		0x204
+#define		IPP_CFG1_MIN_HBI		4
+
+#define CSID_IPP_CTRL		0x208
+#define		IPP_CTRL_HALT_CMD		0
+#define		IPP_CTRL_HALT_MASTER_SEL	4
+#define			HALT_MASTER_SEL_IPP	3
+
+#define CSID_IPP_FRM_DROP_PATTERN	0x20c
+#define CSID_IPP_FRM_DROP_PERIOD		0x210
+#define CSID_IPP_IRQ_SUBSAMPLE_PATTERN	0x214
+#define CSID_IPP_IRQ_SUBSAMPLE_PERIOD	0x218
+#define CSID_IPP_LINE_DROP_PATTERN	0x22c
+#define CSID_IPP_LINE_DROP_PERIOD	0x230
+
+#define CSID_CSI2_IPP_IRQ_STATUS	0x30
+#define CSID_CSI2_IPP_IRQ_CLEAR		0x38
+
 #define CSID_TPG_CTRL		0x600
 #define		TPG_CTRL_TEST_EN		0
 #define		TPG_CTRL_FS_PKT_EN		1
@@ -203,6 +262,64 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
 	writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi));
 }
 
+static void __csid_ctrl_pix(struct csid_device *csid, int enable)
+{
+	u32 val;
+
+	if (enable)
+		val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << IPP_CTRL_HALT_CMD;
+	else
+		val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << IPP_CTRL_HALT_CMD;
+	writel_relaxed(val, csid->base + CSID_IPP_CTRL);
+
+	if (!enable) {
+		writel_relaxed(~0, csid->base + CSID_CSI2_IPP_IRQ_CLEAR);
+		writel_relaxed(1 << IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+	}
+}
+
+static void __csid_configure_pix(struct csid_device *csid, u8 enable, u8 vc, u8 dt)
+{
+	u32 val;
+
+	val = 1 << IPP_CFG0_FMT_MEASURE_EN;
+	val |= 1 << IPP_CFG0_TIMESTAMP_EN;
+	val |= csid_pix_dt_to_decode_format(dt) << IPP_CFG0_DECODE_FORMAT;
+	val |= dt << IPP_CFG0_DATA_TYPE;
+	val |= vc << IPP_CFG0_VIRTUAL_CHANNEL;
+	val |= CSID_PIX_STREAM_ID << IPP_CFG0_DT_ID;
+	writel_relaxed(val, csid->base + CSID_IPP_CFG0);
+
+	val = 4 << IPP_CFG1_MIN_HBI;
+	writel_relaxed(val, csid->base + CSID_IPP_CFG1);
+
+	val = 1;
+	writel_relaxed(val, csid->base + CSID_IPP_FRM_DROP_PERIOD);
+
+	val = 0;
+	writel_relaxed(val, csid->base + CSID_IPP_FRM_DROP_PATTERN);
+
+	val = 1;
+	writel_relaxed(val, csid->base + CSID_IPP_IRQ_SUBSAMPLE_PERIOD);
+
+	val = 0;
+	writel_relaxed(val, csid->base + CSID_IPP_IRQ_SUBSAMPLE_PATTERN);
+
+	val = 1;
+	writel_relaxed(val, csid->base + CSID_IPP_LINE_DROP_PERIOD);
+
+	val = 0;
+	writel_relaxed(val, csid->base + CSID_IPP_LINE_DROP_PATTERN);
+
+	val = HALT_MASTER_SEL_IPP << IPP_CTRL_HALT_MASTER_SEL;
+	val |= HALT_CMD_RESUME_AT_FRAME_BOUNDARY << IPP_CTRL_HALT_CMD;
+	writel_relaxed(val, csid->base + CSID_IPP_CTRL);
+
+	val = readl_relaxed(csid->base + CSID_IPP_CFG0);
+	val |= enable << IPP_CFG0_ENABLE;
+	writel_relaxed(val, csid->base + CSID_IPP_CFG0);
+}
+
 static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc)
 {
 	struct csid_testgen_config *tg = &csid->testgen;
@@ -337,9 +454,16 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
 			if (tg->enabled)
 				__csid_configure_testgen(csid, enable, i, 0);
 
-			__csid_configure_rdi_stream(csid, enable, i, 0, format->data_type);
-			__csid_configure_rx(csid, &csid->phy, 0);
-			__csid_ctrl_rdi(csid, enable, i);
+			if (csid_is_pix_stream(csid, i)) {
+				__csid_configure_pix(csid, enable, 0, format->data_type);
+				__csid_configure_rx(csid, &csid->phy, 0);
+				__csid_ctrl_pix(csid, enable);
+			} else {
+				__csid_configure_rdi_stream(csid, enable, i, 0,
+							    format->data_type);
+				__csid_configure_rx(csid, &csid->phy, 0);
+				__csid_ctrl_rdi(csid, enable, i);
+			}
 		}
 }
 
@@ -355,20 +479,31 @@ static void csid_enable_stream(struct csid_device *csid, u32 stream_id, u8 vc, u
 	if (tg->enabled)
 		__csid_configure_testgen(csid, 1, stream_id, vc);
 
-	__csid_configure_rdi_stream(csid, 1, stream_id, vc, dt);
-	__csid_ctrl_rdi(csid, 1, stream_id);
+	if (csid_is_pix_stream(csid, stream_id)) {
+		__csid_configure_pix(csid, 1, vc, dt);
+		__csid_ctrl_pix(csid, 1);
+	} else {
+		__csid_configure_rdi_stream(csid, 1, stream_id, vc, dt);
+		__csid_ctrl_rdi(csid, 1, stream_id);
+	}
 }
 
 static void csid_disable_stream(struct csid_device *csid, u32 stream_id)
 {
 	struct csid_testgen_config *tg = &csid->testgen;
 
-	__csid_ctrl_rdi(csid, 0, stream_id);
+	if (csid_is_pix_stream(csid, stream_id)) {
+		__csid_configure_pix(csid, 0, 0, 0);
+		__csid_ctrl_pix(csid, 0);
+	} else {
+		__csid_ctrl_rdi(csid, 0, stream_id);
+	}
 
 	if (tg->enabled)
 		__csid_configure_testgen(csid, 0, stream_id, 0);
 
-	__csid_configure_rdi_stream(csid, 0, stream_id, 0, 0);
+	if (!csid_is_pix_stream(csid, stream_id))
+		__csid_configure_rdi_stream(csid, 0, stream_id, 0, 0);
 }
 
 static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
@@ -403,8 +538,13 @@ static irqreturn_t csid_isr(int irq, void *dev)
 	/* Read and clear IRQ status for each enabled RDI channel */
 	for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
 		if (csid->phy.en_vc & BIT(i)) {
-			val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
-			writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+			if (csid_is_pix_stream(csid, i)) {
+				val = readl_relaxed(csid->base + CSID_CSI2_IPP_IRQ_STATUS);
+				writel_relaxed(val, csid->base + CSID_CSI2_IPP_IRQ_CLEAR);
+			} else {
+				val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
+				writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+			}
 		}
 
 	val = 1 << IRQ_CMD_CLEAR;
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index bdac29b7af86..b7e6bfc58bcf 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -1784,16 +1784,9 @@ static int csid_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
 	struct csid_device *csid = v4l2_get_subdevdata(sd);
 	struct v4l2_subdev_route routes[MSM_CSID_MAX_SRC_STREAMS];
 	struct v4l2_subdev_krouting routing = { };
-	unsigned int num_routes;
 	int i, ret;
 
-	/* The full IFE has only 3 rdi's and pix output is not functional */
-	if (csid_is_lite(csid))
-		num_routes = MSM_CSID_MAX_SRC_STREAMS;
-	else
-		num_routes = MSM_CSID_MAX_SRC_STREAMS - 1;
-
-	for (i = 0; i < num_routes; i++) {
+	for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
 		routes[i].sink_pad = MSM_CSID_PAD_SINK;
 		routes[i].sink_stream = 0;
 		if (csid->res->source_streams_mode == CSID_STREAMS_SINGLE_PAD) {
@@ -1806,7 +1799,7 @@ static int csid_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
 		routes[i].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
 	}
 
-	routing.num_routes = num_routes;
+	routing.num_routes = MSM_CSID_MAX_SRC_STREAMS;
 	routing.routes = routes;
 	ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &csid_default_format);
 	if (ret)
@@ -1849,7 +1842,7 @@ int msm_csid_register_entity(struct csid_device *csid,
 	if (csid->res->source_streams_mode == CSID_STREAMS_SINGLE_PAD)
 		num_pads = MSM_CSID_PAD_FIRST_SRC + 1;
 	else
-		num_pads = csid_is_lite(csid) ? MSM_CSID_PADS_NUM : MSM_CSID_PADS_NUM - 1;
+		num_pads = MSM_CSID_PADS_NUM;
 
 	v4l2_subdev_init(sd, streams_api ? &csid_streams_v4l2_ops : &csid_v4l2_ops);
 	sd->internal_ops = streams_api ? &csid_streams_internal_ops
-- 
2.34.1


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

* [PATCH 03/45] media: qcom: camss: report probe failures
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 01/45] media: qcom: camss: support CSID streams on one source pad Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 02/45] media: qcom: camss: add CSID gen2 pixel path Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (42 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Report errors returned by interconnect, DMA and firmware setup.

This makes early probe failures visible in the kernel log.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 9bfb0290395b..1f662b1caf5a 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -5418,8 +5418,10 @@ static int camss_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	ret = camss_icc_get(camss);
-	if (ret < 0)
+	if (ret < 0) {
+		dev_err(dev, "Failed to configure ICC: %d\n", ret);
 		return ret;
+	}
 
 	ret = camss_configure_pd(camss);
 	if (ret < 0) {
@@ -5432,8 +5434,10 @@ static int camss_probe(struct platform_device *pdev)
 		goto err_genpd_cleanup;
 
 	ret = dma_set_mask_and_coherent(dev, 0xffffffff);
-	if (ret)
+	if (ret) {
+		dev_err(dev, "Failed to configure DMA coherent: %d\n", ret);
 		goto err_genpd_cleanup;
+	}
 
 	camss->media_dev.dev = camss->dev;
 	strscpy(camss->media_dev.model, "Qualcomm Camera Subsystem",
@@ -5453,8 +5457,10 @@ static int camss_probe(struct platform_device *pdev)
 	pm_runtime_enable(dev);
 
 	ret = camss_parse_ports(camss);
-	if (ret < 0)
+	if (ret < 0) {
+		dev_err(dev, "Failed to parse ports: %d\n", ret);
 		goto err_v4l2_device_unregister;
+	}
 
 	ret = camss_register_entities(camss);
 	if (ret < 0)
-- 
2.34.1


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

* [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (2 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 03/45] media: qcom: camss: report probe failures Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-07  7:41   ` Krzysztof Kozlowski
  2026-10-07  9:52   ` Bryan O'Donoghue
  2026-10-06 22:39 ` [PATCH 05/45] media: qcom: camss: keep CSIPHY lanes active while streaming Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (41 subsequent siblings)
  45 siblings, 2 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Document the qcom,sm8250pix-camss compatible for CAMSS configurations
that use the IFE pixel path. It uses the existing CAMSS property
layout without additional properties.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../devicetree/bindings/media/qcom,sm8250-camss.yaml          | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
index a509d4bbcb4a..d561179be48c 100644
--- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
@@ -14,7 +14,9 @@ description: |
 
 properties:
   compatible:
-    const: qcom,sm8250-camss
+    enum:
+      - qcom,sm8250-camss
+      - qcom,sm8250pix-camss
 
   clocks:
     minItems: 37
-- 
2.34.1


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

* [PATCH 05/45] media: qcom: camss: keep CSIPHY lanes active while streaming
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (3 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 06/45] media: qcom: camss: add IFE subdevice interface Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (40 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add a stream_count reference counter to csiphy_device. The CSIPHY
lanes are enabled on the first stream start and disabled only when
the last stream stops, preventing a shared CSI PHY from being torn
down while another output is still active.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss-csiphy.c | 12 ++++++++++--
 drivers/media/platform/qcom/camss/camss-csiphy.h |  1 +
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 9e95d04b7235..4938e56f481d 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -324,10 +324,18 @@ static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
 	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
 	int ret = 0;
 
-	if (enable)
+	if (enable) {
+		if (csiphy->stream_count) {
+			csiphy->stream_count++;
+			return 0;
+		}
+
 		ret = csiphy_stream_on(csiphy);
-	else
+		if (!ret)
+			csiphy->stream_count++;
+	} else if (csiphy->stream_count && !--csiphy->stream_count) {
 		csiphy_stream_off(csiphy);
+	}
 
 	return ret;
 }
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index e55c098dbe67..8c1eb83de751 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -115,6 +115,7 @@ struct csiphy_device {
 	struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM];
 	const struct csiphy_subdev_resources *res;
 	struct csiphy_device_regs *regs;
+	unsigned int stream_count;
 	u64 enabled_streams[MSM_CSIPHY_PADS_NUM];
 };
 
-- 
2.34.1


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

* [PATCH 06/45] media: qcom: camss: add IFE subdevice interface
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (4 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 05/45] media: qcom: camss: keep CSIPHY lanes active while streaming Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 07/45] media: qcom: camss: sm8250: add IFE BUS v20x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (39 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the common IFE types and hardware operation interfaces. Later
patches use them to add the IFE engines and subdevice.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss-ife.h | 392 ++++++++++++++++++
 1 file changed, 392 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife.h

diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
new file mode 100644
index 000000000000..39e8cfa74909
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -0,0 +1,392 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#ifndef CAMSS_IFE_SUB_DEV_H
+#define CAMSS_IFE_SUB_DEV_H
+
+#include <media/v4l2-isp.h>
+#include <media/v4l2-subdev.h>
+
+#include "camss-format.h"
+#include "camss-video.h"
+
+#define IFE_CAMIF_HW_PIPES_MAX	6
+
+/* IFE irq 0 status flags */
+#define IFE_HW_TOP_IRQ0_RST_DONE	BIT(0)
+#define IFE_HW_TOP_IRQ0_BUSWR		BIT(1)
+
+/* IFE top */
+#define IFE_HW_TOP_IRQ1_RDI0_SOF	BIT(0)
+#define IFE_HW_TOP_IRQ1_RDI0_EPOCH0	BIT(1)
+#define IFE_HW_TOP_IRQ1_RDI0_EPOCH1	BIT(2)
+#define IFE_HW_TOP_IRQ1_RDI1_SOF	BIT(3)
+#define IFE_HW_TOP_IRQ1_RDI1_EPOCH0	BIT(4)
+#define IFE_HW_TOP_IRQ1_RDI1_EPOCH1	BIT(5)
+#define IFE_HW_TOP_IRQ1_RDI2_SOF	BIT(6)
+#define IFE_HW_TOP_IRQ1_RDI2_EPOCH0	BIT(7)
+#define IFE_HW_TOP_IRQ1_RDI2_EPOCH1	BIT(8)
+#define IFE_HW_TOP_IRQ1_RDI3_SOF	BIT(9)
+#define IFE_HW_TOP_IRQ1_PP_SOF		BIT(12)
+#define IFE_HW_TOP_IRQ1_PP_EPOCH0	BIT(13)
+#define IFE_HW_TOP_IRQ1_PP_EPOCH1	BIT(14)
+
+#define BUSWR_CLIENT_CFG_MODE_LINE_BASED	0
+#define BUSWR_CLIENT_CFG_MODE_FRAME_BASED	1
+
+enum camss_ife_output {
+	CAMSS_IFE_OUTPUT_INVALID,
+	CAMSS_IFE_OUTPUT_RDI0,
+	CAMSS_IFE_OUTPUT_RDI1,
+	CAMSS_IFE_OUTPUT_RDI2,
+	CAMSS_IFE_OUTPUT_RDI3,
+	CAMSS_IFE_OUTPUT_VID_FULL,
+	CAMSS_IFE_OUTPUT_DISP_FULL,
+	CAMSS_IFE_OUTPUT_FD,
+};
+
+enum camss_ife_hw_pipe {
+	CAMSS_IFE_HW_PIPE_RDI0,
+	CAMSS_IFE_HW_PIPE_RDI1,
+	CAMSS_IFE_HW_PIPE_RDI2,
+	CAMSS_IFE_HW_PIPE_RDI3,
+	CAMSS_IFE_HW_PIPE_PP,
+	CAMSS_IFE_HW_PIPE_INVALID,
+};
+
+enum ife_hw_planes {
+	Y_PLANE = 0,
+	C_PLANE,
+	IFE_HW_PLANES
+};
+
+enum ife_top_status {
+	IFE_TOP_STATUS_ID0,
+	IFE_TOP_STATUS_ID1,
+	IFE_TOP_STATUS_ID2,
+	IFE_TOP_STATUS_MAX,
+};
+
+struct camss_hw_version {
+	union {
+		u32 hw_version;
+		struct {
+			u32 major:  4;
+			u32 minor: 12;
+			u32 steps: 16;
+		} ids;
+	};
+};
+
+struct camss_ife_wm_config {
+	u32 wm_id;
+	u32 width;
+	u32 height;
+	u32 bytes;
+	u32 stride;
+	u32 lines;
+	u32 mode_config;
+	u32 packing_fmt;
+};
+
+struct camss_ife_pipeline_cfg {
+	u32 in_code;
+	u32 in_width;
+	u32 in_height;
+	struct v4l2_rect *ds_mn_crop;
+};
+
+struct camss;
+struct camss_subdev_resources;
+struct ife_device;
+struct ife_pad_desc;
+struct ife_top_ctx;
+struct ife_pp_ctx;
+struct vb2_buffer;
+struct ife_buswr_ctx;
+
+/**
+ * struct ife_hw_camif_ops - CAMIF hardware ops interface.
+ *
+ * Abstracts the CAMIF config/enable/disable operations so that
+ * top-level controllers are not coupled to a specific CAMIF IP version.
+ *
+ * @config:  Program CAMIF registers for the given pipe and pipeline config.
+ * @enable:  Enable the CAMIF output for the given pipe.
+ * @disable: Disable the CAMIF output for the given pipe.
+ */
+struct ife_hw_camif_ops {
+	int  (*config)(void __iomem *base, enum camss_ife_hw_pipe hw_pipe,
+		       const struct camss_ife_pipeline_cfg *cfg);
+	void (*enable)(void __iomem *base, enum camss_ife_hw_pipe hw_pipe);
+	void (*disable)(void __iomem *base, enum camss_ife_hw_pipe hw_pipe);
+};
+
+struct ife_hw_top_ops {
+	int	(*acquire)(struct ife_device *ife, void __iomem *base,
+			   void __iomem *csid_base);
+	void	(*release)(struct ife_top_ctx *ctx);
+
+	u32	(*version)(struct ife_top_ctx *ctx);
+	void	(*prepare)(struct ife_top_ctx *ctx);
+	void	(*enable)(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe);
+	void	(*disable)(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe);
+
+	int	(*pipe_config)(struct ife_top_ctx *ctx, const struct ife_pad_desc *desc,
+			       const struct camss_ife_pipeline_cfg *pipeline_cfg,
+			       const struct camss_ife_wm_config *wm_cfg);
+	int	(*pipe_enable)(struct ife_top_ctx *ctx, const struct ife_pad_desc *desc);
+	int	(*pipe_disable)(struct ife_top_ctx *ctx, const struct ife_pad_desc *desc);
+
+	void	(*reg_update)(struct ife_top_ctx *ctx, const struct ife_pad_desc *dsc);
+	void	(*reset)(struct ife_top_ctx *ctx);
+	void	(*irq_status)(struct ife_top_ctx *ctx, u32 isr_stat[IFE_TOP_STATUS_MAX]);
+};
+
+struct ife_hw_buswr_ops {
+	void	(*acquire)(struct ife_device *ife, void __iomem *base);
+	void	(*release)(struct ife_buswr_ctx *ctx);
+	void	(*irq_enable)(struct ife_buswr_ctx *ctx);
+	void	(*irq_disable)(struct ife_buswr_ctx *ctx);
+	void	(*wm_config)(struct ife_buswr_ctx *ctx, const u8 wm,
+			     const struct camss_ife_wm_config *cfg);
+	void	(*wm_enable)(struct ife_buswr_ctx *ctx, u8 wm);
+	void	(*wm_disable)(struct ife_buswr_ctx *ctx, u8 wm);
+	void	(*queue_buf)(struct ife_buswr_ctx *ctx, const u8 num_wms,
+			     const u8 *wm, dma_addr_t *addr);
+	u32	(*consumed_addr)(struct ife_buswr_ctx *ctx, u8 wm);
+	u32	(*irq_status)(struct ife_buswr_ctx *ctx);
+	bool	(*is_fifo_full)(struct ife_buswr_ctx *ctx, u8 wm);
+};
+
+/**
+ * struct camss_pp_cfg - PP module pipeline configuration.
+ *
+ * @width:   Input frame width in pixels.
+ * @height:  Input frame height in pixels.
+ * @in_code: Input media bus format code (MEDIA_BUS_FMT_*).
+ */
+struct camss_pp_cfg {
+	u32 width;
+	u32 height;
+	u32 in_code;
+};
+
+/**
+ * struct camss_mod_ops - PP module ops interface.
+ *
+ * Engine-agnostic ops for configuring, enabling and disabling a set of
+ * post-processing hardware modules.  The concrete implementation is
+ * provided by the IP-specific backend (e.g. pp_v10x_modules_ops).
+ *
+ * @acquire:      Initialise the context; store @base in @ctx.
+ * @release:      Release the context.
+ * @config:       Program module registers with default values derived from @cfg.
+ *                Called once per stream start before @enable.
+ * @enable:       Enable all modules managed by this backend.
+ * @disable:      Disable all modules managed by this backend.
+ * @apply_params: Apply per-frame IFE module parameters (optional).
+ */
+struct camss_mod_ops {
+	void (*acquire)(struct ife_device *ife, struct ife_pp_ctx *ctx, void __iomem *base);
+	void (*release)(struct ife_pp_ctx *ctx);
+	void (*config)(struct ife_pp_ctx *ctx, const struct camss_pp_cfg *cfg);
+	void (*enable)(struct ife_pp_ctx *ctx);
+	void (*disable)(struct ife_pp_ctx *ctx);
+	int  (*apply_params)(struct ife_pp_ctx *ctx, struct vb2_buffer *vb);
+};
+
+/**
+ * struct ife_top_ctx - runtime context for the IFE top controller.
+ *
+ * Holds the hardware base address and the CAMIF ops pointer so that
+ * top-level pipe functions do not need to receive them as arguments.
+ * Initialised once at device probe via ife_hw_top_ops::acquire().
+ *
+ * @base:      MMIO base of the IFE top register block.
+ * @camif_ops: CAMIF hardware ops bound to this IFE hardware block.
+ * @dev:       Device used for diagnostic messages.
+ */
+struct ife_top_ctx {
+	void __iomem                  *base;
+	void __iomem                  *csid_base;
+	void __iomem                  *rdi_base;
+	void __iomem                  *pp_base;
+	const struct ife_hw_camif_ops *camif_ops;
+	struct device                 *dev;
+};
+
+/**
+ * struct ife_buswr_ctx - runtime context for the IFE BUS write engine.
+ *
+ * Holds the hardware base address so that BUS write ops do not need to
+ * receive it as an argument on every call.
+ * Initialised once at device probe via ife_hw_buswr_ops::acquire().
+ *
+ * @base: MMIO base of the IFE BUS write register block.
+ * @dev:  Device used for diagnostics.
+ */
+struct ife_buswr_ctx {
+	void __iomem  *base;
+	struct device *dev;
+};
+
+/**
+ * struct ife_pp_ctx - runtime context for the IFE post-processing engine.
+ *
+ * Holds the hardware base address so that PP ops do not need to receive
+ * it as an argument on every call.
+ * Initialised once at device probe via camss_mod_ops::acquire().
+ * @in_width/@in_height are latched from struct camss_pp_cfg on
+ * camss_mod_ops::config() and reused by apply_params() for modules (e.g.
+ * LSC) whose per-frame register values depend on the input frame size.
+ *
+ * @base:          MMIO base of the IFE PP register block.
+ * @in_width:      Input frame width in pixels, latched from the last @config call.
+ * @in_height:     Input frame height in pixels, latched from the last @config call.
+ * @dev:           Device used for allocating @params_scratch and diagnostics.
+ * @params_scratch: Scratch buffer used to validate/stage an apply_params() buffer.
+ */
+struct ife_pp_ctx {
+	void __iomem *base;
+	u32 in_width;
+	u32 in_height;
+	struct device *dev;
+	struct v4l2_isp_params_buffer *params_scratch;
+};
+
+struct ife_pad_limits {
+	u32 min_width;
+	u32 min_height;
+	u32 max_width;
+	u32 max_height;
+	u32 max_h_ratio;
+	u32 max_v_ratio;
+};
+
+struct ife_pad_desc {
+	u16 pad_flags;
+	struct camss_formats sup_fmts;
+	const struct ife_pad_limits *limits;
+
+	const char *video_name;
+	u32 link_flags;
+
+	enum v4l2_buf_type buf_type;
+	enum camss_ife_output output;
+	u32 hw_pipe;
+
+	u8 packing_fmt;
+	u8 mode_config;
+
+	u32 rup_group_mask;
+	u32 comp_group_mask;
+
+	u8 num_wms;
+	u8 wm[IFE_HW_PLANES];
+};
+
+struct ife_pads_descriptors {
+	unsigned int num_desc;
+	const struct ife_pad_desc *desc;
+};
+
+enum ife_subdev_type {
+	CAMSS_IFE_SUBDEV_TYPE_IFE = 0,
+	CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+};
+
+struct camss_ife_stream {
+	u32 id;
+	enum camss_ife_hw_pipe pipe;
+};
+
+struct ife_subdev_resources {
+	enum ife_subdev_type type;
+	const struct ife_hw_top_ops	*top_ops;
+	const struct ife_hw_camif_ops	*camif_ops;
+	const struct camss_mod_ops	*mod_ops;
+	const struct ife_hw_buswr_ops	*buswr_ops;
+	const struct ife_pads_descriptors *pads;
+
+	unsigned int num_sink_streams;
+	const struct camss_ife_stream *sink_streams;
+};
+
+struct ife_video_pad {
+	struct ife_device *ife;
+	u32 pad_index;
+
+	struct camss_video video;
+	bool streaming;
+
+	struct list_head pending_bufs;
+	struct list_head processing_bufs;
+};
+
+struct ife_subdev_pads {
+	const struct ife_pad_desc *desc;
+	struct v4l2_mbus_framefmt fmt;
+	struct ife_video_pad *vid_pad;
+};
+
+struct ife_device {
+	u8 id;
+	struct camss *camss;
+	struct ife_top_ctx   top_ctx;
+	struct ife_buswr_ctx buswr_ctx;
+	struct ife_pp_ctx    pp_ctx;
+
+	const struct ife_subdev_resources *res;
+	u32 irq;
+	char irq_name[sizeof(((struct v4l2_subdev *)0)->name)];
+
+	int nclocks;
+	struct camss_clock *clock;
+
+	struct completion reset_complete;
+
+	struct mutex state_mutex; /* Protects all pad states */
+	spinlock_t bufs_lock; /* Protects buffer lists */
+	unsigned int stream_count;
+
+	struct ife_subdev_pads *pads;
+	struct media_pad *mpads;
+	int npads;
+	u32 sink_pad;
+
+	struct {
+		unsigned int stream_count;
+		u32 next_seq;
+	} camif_data[IFE_CAMIF_HW_PIPES_MAX];
+
+	struct camss_ife_pipeline_cfg pipeline_cfg[IFE_CAMIF_HW_PIPES_MAX];
+
+	struct v4l2_subdev sd;
+	struct device *genpd;
+	struct device_link *genpd_link;
+};
+
+int camss_ife_subdev_init(struct camss *camss, struct ife_device *ife,
+			  const struct camss_subdev_resources *res, u8 id,
+			  void __iomem *csid_base);
+
+int ife_register_entities(struct ife_device *ife, struct v4l2_device *v4l2_dev);
+void ife_unregister_entities(struct ife_device *ife);
+
+int ife_get(struct ife_device *ife);
+void ife_put(struct ife_device *ife);
+
+int camss_ife_link_entities(struct camss *camss);
+
+u32 camss_ife_stream_id_for_pipe(int nstreams,
+				 const struct camss_ife_stream *streams,
+				 enum camss_ife_hw_pipe pipe);
+enum camss_ife_hw_pipe
+camss_ife_pipe_for_stream_id(int nstreams,
+			     const struct camss_ife_stream *streams,
+			     u32 id);
+
+#endif /* CAMSS_IFE_SUB_DEV_H */
-- 
2.34.1


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

* [PATCH 07/45] media: qcom: camss: sm8250: add IFE BUS v20x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (5 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 06/45] media: qcom: camss: add IFE subdevice interface Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 08/45] media: qcom: camss: sm8250: add IFE CamIF v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (38 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the IFE BUS write engine used by the SM8250 full and lite pixel
paths.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-bus-v20x.c  | 287 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   2 +
 3 files changed, 290 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-bus-v20x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index b79c40626c40..e30fe07b16f4 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -14,6 +14,7 @@ qcom-camss-objs += \
 		camss-csiphy-2ph-1-0.o \
 		camss-csiphy-3ph-1-0.o \
 		camss-format.o \
+		camss-ife-bus-v20x.o \
 		camss-ispif.o \
 		camss-tpg.o \
 		camss-tpg-gen1.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-bus-v20x.c b/drivers/media/platform/qcom/camss/camss-ife-bus-v20x.c
new file mode 100644
index 000000000000..3e27494d91da
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-bus-v20x.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+
+#include "camss.h"
+#include "camss-ife.h"
+
+/* Block base offsets */
+#define BUSWR_V20X_OFFS		0xaa00
+#define BUSWR_V20X_LITE_OFFS	0x1a00
+
+/* Per-client register offset */
+#define BUSWR_V20X_CLIENT_OFFS(n)	((n) * 0x100)
+
+/* Interface interrupt and violation status bits */
+#define BUSWR_V20X_IIF_IRQ_CMD_CLEAR			BIT(0)
+#define BUSWR_V20X_OVERFLOW_ERR_MASK			GENMASK(25, 0)
+#define IS_BUSWR_V20X_OVERFLOW_ERR(s)	\
+	((s) & BUSWR_V20X_OVERFLOW_ERR_MASK)
+#define BUSWR_V20X_VIOLATION_ERR_MASK			GENMASK(25, 0)
+#define IS_BUSWR_V20X_VIOLATION_ERR(s)	\
+	((s) & BUSWR_V20X_VIOLATION_ERR_MASK)
+#define BUSWR_V20X_IMG_SIZE_VIOLATION_ERR_MASK		GENMASK(25, 0)
+#define IS_BUSWR_V20X_IMG_SIZE_VIOLATION_ERR(s)	\
+	((s) & BUSWR_V20X_IMG_SIZE_VIOLATION_ERR_MASK)
+#define BUSWR_V20X_IIF_IRQ_S0_IMG_SIZE_VIOLATION	BIT(31)
+#define IS_BUSWR_V20X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(s)	\
+	((s) & BUSWR_V20X_IIF_IRQ_S0_IMG_SIZE_VIOLATION)
+#define BUSWR_V20X_IIF_IRQ_S0_VIOLATION			BIT(30)
+#define IS_BUSWR_V20X_IIF_IRQ_S0_VIOLATION(s)	\
+	((s) & BUSWR_V20X_IIF_IRQ_S0_VIOLATION)
+
+/* Client configuration field values */
+#define BUSWR_V20X_IIF_CGC_OVERRIDE			GENMASK(25, 0)
+#define BUSWR_V20X_CLIENT_CFG_MODE			GENMASK(17, 16)
+#define BUSWR_V20X_CLIENT_CFG_AUTO_RECOVERY_EN		BIT(4)
+#define BUSWR_V20X_CLIENT_CFG_EN			BIT(0)
+#define BUSWR_V20X_CLIENT_IMG_CFG0_HEIGHT		GENMASK_U32(31, 16)
+#define BUSWR_V20X_CLIENT_IMG_CFG0_WIDTH		GENMASK_U32(15, 0)
+#define BUSWR_V20X_CLIENT_IMG_CFG2_STRIDE		GENMASK_U32(15, 0)
+#define BUSWR_V20X_CLIENT_PACKER_CFG_ALIGN		GENMASK(4, 4)
+#define BUSWR_V20X_CLIENT_PACKER_CFG_FORMAT		GENMASK(3, 0)
+#define BUSWR_V20X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD	GENMASK(4, 0)
+#define BUSWR_V20X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN	GENMASK(31, 0)
+#define BUSWR_V20X_CLIENT_FRAMEDROP_CFG0_PERIOD		GENMASK(4, 0)
+#define BUSWR_V20X_CLIENT_FRAMEDROP_CFG1_PATTERN	GENMASK(31, 0)
+
+/* Burst and FIFO limits */
+#define BUSWR_V20X_BURST_BEATS8_256			3
+#define BUSWR_V20X_CLIENT_BURST_LIMIT_CFG_MAX_LENGTH	GENMASK(1, 0)
+#define BUSWR_V20X_IMG_ADDR_FIFO_MAX_DEPTH		4
+
+/* Common BUSWR registers */
+#define BUSWR_V20X_IIF_CGC_OVERRIDE_REG		0x00000008
+#define BUSWR_V20X_IIF_IRQ_MASK_S0		0x00000018
+#define BUSWR_V20X_IIF_IRQ_MASK_S1		0x0000001c
+#define BUSWR_V20X_IIF_IRQ_CLEAR_S0		0x00000020
+#define BUSWR_V20X_IIF_IRQ_CLEAR_S1		0x00000024
+#define BUSWR_V20X_IIF_IRQ_STATUS_S0		0x00000028
+#define BUSWR_V20X_IIF_IRQ_STATUS_S1		0x0000002c
+#define BUSWR_V20X_IIF_IRQ_CMD_REG		0x00000030
+#define BUSWR_V20X_OVERFLOW_CLEAR_REG		0x00000060
+#define BUSWR_V20X_VIOLATION_STATUS_REG		0x00000064
+#define BUSWR_V20X_OVERFLOW_STATUS_REG		0x00000068
+#define BUSWR_V20X_IMG_SIZE_VIOLATION_REG	0x00000070
+
+/* Per-client BUSWR registers */
+#define BUSWR_V20X_CLIENT_CFG_REG		0x00000200
+#define BUSWR_V20X_CLIENT_ADDR_IMAGE_REG	0x00000204
+#define BUSWR_V20X_CLIENT_IMG_CFG0_REG		0x0000020c
+#define BUSWR_V20X_CLIENT_IMG_CFG2_REG		0x00000214
+#define BUSWR_V20X_CLIENT_PACKER_CFG_REG	0x00000218
+#define BUSWR_V20X_CLIENT_IRQ_SS_CFG0_REG	0x00000230
+#define BUSWR_V20X_CLIENT_IRQ_SS_CFG1_REG	0x00000234
+#define BUSWR_V20X_CLIENT_FRAMEDROP_CFG0_REG	0x00000238
+#define BUSWR_V20X_CLIENT_FRAMEDROP_CFG1_REG	0x0000023c
+#define BUSWR_V20X_CLIENT_BURST_LIMIT_REG	0x00000264
+#define BUSWR_V20X_CLIENT_ADDR_STATUS_0_REG	0x00000268
+#define BUSWR_V20X_CLIENT_ADDR_STATUS_2_REG	0x00000270
+
+static void ife_buswr_20x_acquire(struct ife_device *ife, void __iomem *base)
+{
+	struct ife_buswr_ctx *ctx = &ife->buswr_ctx;
+
+	if (ife->res->type == CAMSS_IFE_SUBDEV_TYPE_IFE_LITE)
+		ctx->base = base + BUSWR_V20X_LITE_OFFS;
+	else
+		ctx->base = base + BUSWR_V20X_OFFS;
+
+	ctx->dev = ife->camss->dev;
+}
+
+static void ife_buswr_20x_release(struct ife_buswr_ctx *ctx)
+{
+	ctx->base = NULL;
+}
+
+static void ife_buswr_20x_wm_config(struct ife_buswr_ctx *ctx, const u8 wm,
+				    const struct camss_ife_wm_config *cfg)
+{
+	void __iomem *base = ctx->base;
+	u32 rval, period = 0, pattern = 1;
+
+	writel_relaxed(BUSWR_V20X_IIF_CGC_OVERRIDE,
+		       base + BUSWR_V20X_IIF_CGC_OVERRIDE_REG);
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_CFG_MODE, cfg->mode_config);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_CFG_REG + BUSWR_V20X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_PACKER_CFG_FORMAT, cfg->packing_fmt) |
+	       FIELD_PREP(BUSWR_V20X_CLIENT_PACKER_CFG_ALIGN, 1);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_PACKER_CFG_REG + BUSWR_V20X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_IMG_CFG0_WIDTH, cfg->bytes) |
+	       FIELD_PREP(BUSWR_V20X_CLIENT_IMG_CFG0_HEIGHT, cfg->lines);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_IMG_CFG0_REG +
+		       BUSWR_V20X_CLIENT_OFFS(wm));
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_IMG_CFG2_STRIDE, cfg->stride);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_IMG_CFG2_REG +
+		       BUSWR_V20X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD, period);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_IRQ_SS_CFG0_REG +
+		       BUSWR_V20X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN, pattern);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_IRQ_SS_CFG1_REG +
+		       BUSWR_V20X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_FRAMEDROP_CFG0_PERIOD, period);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_FRAMEDROP_CFG0_REG +
+		       BUSWR_V20X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_FRAMEDROP_CFG1_PATTERN, pattern);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_FRAMEDROP_CFG1_REG +
+		       BUSWR_V20X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V20X_CLIENT_BURST_LIMIT_CFG_MAX_LENGTH,
+			  BUSWR_V20X_BURST_BEATS8_256);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_BURST_LIMIT_REG +
+		       BUSWR_V20X_CLIENT_OFFS(wm));
+}
+
+static void ife_buswr_20x_wm_enable(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	void __iomem *base = ctx->base;
+	u32 rval;
+
+	rval = readl_relaxed(base + BUSWR_V20X_CLIENT_CFG_REG + BUSWR_V20X_CLIENT_OFFS(wm));
+	rval |= BUSWR_V20X_CLIENT_CFG_EN | BUSWR_V20X_CLIENT_CFG_AUTO_RECOVERY_EN;
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_CFG_REG + BUSWR_V20X_CLIENT_OFFS(wm));
+}
+
+static void ife_buswr_20x_wm_disable(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	void __iomem *base = ctx->base;
+	u32 rval;
+
+	rval = readl_relaxed(base + BUSWR_V20X_CLIENT_CFG_REG + BUSWR_V20X_CLIENT_OFFS(wm));
+	rval &= ~(BUSWR_V20X_CLIENT_CFG_EN | BUSWR_V20X_CLIENT_CFG_AUTO_RECOVERY_EN);
+	writel_relaxed(rval, base + BUSWR_V20X_CLIENT_CFG_REG + BUSWR_V20X_CLIENT_OFFS(wm));
+}
+
+static bool ife_buswr_20x_is_fifo_full(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	u32 rval = readl_relaxed(ctx->base + BUSWR_V20X_CLIENT_OFFS(wm) +
+				 BUSWR_V20X_CLIENT_ADDR_STATUS_2_REG);
+
+	return rval >= BUSWR_V20X_IMG_ADDR_FIFO_MAX_DEPTH;
+}
+
+static void ife_buswr_20x_queue_buf(struct ife_buswr_ctx *ctx, const u8 num_wms, const u8 *wm,
+				    dma_addr_t *addr)
+{
+	int i;
+
+	for (i = 0; i < num_wms; i++) {
+		if (addr[i])
+			writel_relaxed(addr[i], ctx->base + BUSWR_V20X_CLIENT_ADDR_IMAGE_REG +
+				       BUSWR_V20X_CLIENT_OFFS(wm[i]));
+	}
+}
+
+static u32 ife_buswr_20x_consumed_addr(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	return readl_relaxed(ctx->base + BUSWR_V20X_CLIENT_ADDR_STATUS_0_REG +
+			     BUSWR_V20X_CLIENT_OFFS(wm));
+}
+
+static void ife_buswr_20x_log_image_size(struct ife_buswr_ctx *ctx, u32 clients)
+{
+	unsigned int wm;
+	u32 cfg0, cfg2;
+
+	dev_dbg(ctx->dev, "BUSWR image size violation clients: %#010x\n", clients);
+
+	for (wm = 0; wm < 26; wm++) {
+		if (!(clients & BIT(wm)))
+			continue;
+
+		cfg0 = readl_relaxed(ctx->base + BUSWR_V20X_CLIENT_IMG_CFG0_REG +
+				     BUSWR_V20X_CLIENT_OFFS(wm));
+		cfg2 = readl_relaxed(ctx->base + BUSWR_V20X_CLIENT_IMG_CFG2_REG +
+				     BUSWR_V20X_CLIENT_OFFS(wm));
+
+		dev_dbg(ctx->dev, "BUSWR client %u config: width=%u height=%u stride=%u\n",
+			wm, FIELD_GET(BUSWR_V20X_CLIENT_IMG_CFG0_WIDTH, cfg0),
+			FIELD_GET(BUSWR_V20X_CLIENT_IMG_CFG0_HEIGHT, cfg0),
+			FIELD_GET(BUSWR_V20X_CLIENT_IMG_CFG2_STRIDE, cfg2));
+	}
+}
+
+static u32 ife_buswr_20x_irq_status(struct ife_buswr_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+	u32 bus_stat, iif_stat = 0;
+
+	bus_stat = readl_relaxed(base + BUSWR_V20X_OVERFLOW_STATUS_REG);
+	writel_relaxed(BIT(0), base + BUSWR_V20X_OVERFLOW_CLEAR_REG);
+	if (IS_BUSWR_V20X_OVERFLOW_ERR(bus_stat)) {
+		writel_relaxed(bus_stat, base + BUSWR_V20X_OVERFLOW_CLEAR_REG);
+		pr_warn("BUSWR common overflow: 0x%x\n", bus_stat);
+	}
+
+	iif_stat = readl_relaxed(base + BUSWR_V20X_IIF_IRQ_STATUS_S1);
+	writel_relaxed(iif_stat, base + BUSWR_V20X_IIF_IRQ_CLEAR_S1);
+
+	iif_stat = readl_relaxed(base + BUSWR_V20X_IIF_IRQ_STATUS_S0);
+	writel_relaxed(iif_stat, base + BUSWR_V20X_IIF_IRQ_CLEAR_S0);
+	if (iif_stat) {
+		if (IS_BUSWR_V20X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(iif_stat)) {
+			bus_stat = readl_relaxed(base + BUSWR_V20X_IMG_SIZE_VIOLATION_REG);
+			if (IS_BUSWR_V20X_IMG_SIZE_VIOLATION_ERR(bus_stat))
+				pr_warn("BUSWR common image size violation: 0x%x\n", bus_stat);
+			ife_buswr_20x_log_image_size(ctx, bus_stat);
+			pr_warn("BUSWR image size violation from CCIF: 0x%x\n", iif_stat);
+		}
+		if (IS_BUSWR_V20X_IIF_IRQ_S0_VIOLATION(iif_stat)) {
+			bus_stat = readl_relaxed(base + BUSWR_V20X_VIOLATION_STATUS_REG);
+			if (IS_BUSWR_V20X_VIOLATION_ERR(bus_stat))
+				pr_warn("BUSWR common violation: 0x%x\n", bus_stat);
+			pr_warn("BUSWR violation from CCIF: 0x%x\n", iif_stat);
+		}
+	}
+
+	writel_relaxed(BUSWR_V20X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V20X_IIF_IRQ_CMD_REG);
+
+	return iif_stat;
+}
+
+static void ife_buswr_20x_irq_enable(struct ife_buswr_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	writel_relaxed(~0, base + BUSWR_V20X_IIF_IRQ_MASK_S0);
+	writel_relaxed(~0, base + BUSWR_V20X_IIF_IRQ_MASK_S1);
+}
+
+static void ife_buswr_20x_irq_disable(struct ife_buswr_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	writel_relaxed(0, base + BUSWR_V20X_IIF_IRQ_MASK_S0);
+	writel_relaxed(~0, base + BUSWR_V20X_IIF_IRQ_CLEAR_S0);
+	writel_relaxed(0, base + BUSWR_V20X_IIF_IRQ_MASK_S1);
+	writel_relaxed(~0, base + BUSWR_V20X_IIF_IRQ_CLEAR_S1);
+
+	writel_relaxed(BUSWR_V20X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V20X_IIF_IRQ_CMD_REG);
+}
+
+const struct ife_hw_buswr_ops ife_buswr_20x_ops = {
+	.acquire	= ife_buswr_20x_acquire,
+	.release	= ife_buswr_20x_release,
+	.irq_enable	= ife_buswr_20x_irq_enable,
+	.irq_disable	= ife_buswr_20x_irq_disable,
+	.wm_config	= ife_buswr_20x_wm_config,
+	.wm_enable	= ife_buswr_20x_wm_enable,
+	.wm_disable	= ife_buswr_20x_wm_disable,
+	.queue_buf	= ife_buswr_20x_queue_buf,
+	.consumed_addr	= ife_buswr_20x_consumed_addr,
+	.irq_status	= ife_buswr_20x_irq_status,
+	.is_fifo_full	= ife_buswr_20x_is_fifo_full,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 39e8cfa74909..48701cb67e1e 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -389,4 +389,6 @@ camss_ife_pipe_for_stream_id(int nstreams,
 			     const struct camss_ife_stream *streams,
 			     u32 id);
 
+extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
+
 #endif /* CAMSS_IFE_SUB_DEV_H */
-- 
2.34.1


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

* [PATCH 08/45] media: qcom: camss: sm8250: add IFE CamIF v10x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (6 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 07/45] media: qcom: camss: sm8250: add IFE BUS v20x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 09/45] media: qcom: camss: sm8250: add IFE scaler v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (37 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the IFE camera input interface used by the SM8250 pixel path.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |  1 +
 .../qcom/camss/camss-ife-camif-v10x.c         | 59 +++++++++++++++++++
 .../qcom/camss/camss-ife-camif-v10x.h         | 42 +++++++++++++
 3 files changed, 102 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-camif-v10x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-camif-v10x.h

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index e30fe07b16f4..2da998444554 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -15,6 +15,7 @@ qcom-camss-objs += \
 		camss-csiphy-3ph-1-0.o \
 		camss-format.o \
 		camss-ife-bus-v20x.o \
+		camss-ife-camif-v10x.o \
 		camss-ispif.o \
 		camss-tpg.o \
 		camss-tpg-gen1.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-camif-v10x.c b/drivers/media/platform/qcom/camss/camss-ife-camif-v10x.c
new file mode 100644
index 000000000000..c89caf38fb8c
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-camif-v10x.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include "camss.h"
+#include "camss-ife.h"
+#include "camss-ife-camif-v10x.h"
+
+static int camif_v10x_config(void __iomem *camif_base, enum camss_ife_hw_pipe hw_pipe,
+			     const struct camss_ife_pipeline_cfg *sink_cfg)
+{
+	u32 period = 0, pattern = 1, epoch;
+
+	epoch = FIELD_PREP(CAMIF_V10X_CFG_EPOCH0_LINE, sink_cfg->in_height / 3) |
+		FIELD_PREP(CAMIF_V10X_CFG_EPOCH1_LINE, sink_cfg->in_height / 2);
+
+	if (hw_pipe == CAMSS_IFE_HW_PIPE_PP) {
+		writel_relaxed(period, camif_base + CAMIF_V10X_PP_PERIOD_CFG);
+		writel_relaxed(pattern, camif_base + CAMIF_V10X_PP_PATTERN_CFG);
+		writel_relaxed(epoch, camif_base + CAMIF_V10X_PP_EPOCH_IRQ_CFG);
+	} else {
+		writel_relaxed(epoch, camif_base + CAMIF_V10X_RDI_EPOCH_IRQ_CFG);
+		writel_relaxed(period, camif_base + CAMIF_V10X_RDI_PERIOD_CFG);
+		writel_relaxed(pattern, camif_base + CAMIF_V10X_RDI_PATTERN_CFG);
+	}
+
+	return 0;
+}
+
+static void camif_v10x_enable(void __iomem *camif_base, enum camss_ife_hw_pipe hw_pipe)
+{
+	u32 rval = readl_relaxed(camif_base + CAMIF_V10X_MODULE_CFG);
+
+	if (hw_pipe == CAMSS_IFE_HW_PIPE_PP)
+		rval |= CAMIF_V10X_PP_CFG_EN | CAMIF_V10X_PP_CFG_OUTPUT_EN;
+	else
+		rval |= CAMIF_V10X_RDI_CFG_EN;
+
+	writel_relaxed(rval, camif_base + CAMIF_V10X_MODULE_CFG);
+}
+
+static void camif_v10x_disable(void __iomem *camif_base, enum camss_ife_hw_pipe hw_pipe)
+{
+	u32 rval = readl_relaxed(camif_base + CAMIF_V10X_MODULE_CFG);
+
+	if (hw_pipe == CAMSS_IFE_HW_PIPE_PP)
+		rval &= ~(CAMIF_V10X_PP_CFG_EN | CAMIF_V10X_PP_CFG_OUTPUT_EN);
+	else
+		rval &= ~CAMIF_V10X_RDI_CFG_EN;
+
+	writel_relaxed(rval, camif_base + CAMIF_V10X_MODULE_CFG);
+}
+
+const struct ife_hw_camif_ops ife_camif_v10x_ops = {
+	.config  = camif_v10x_config,
+	.enable  = camif_v10x_enable,
+	.disable = camif_v10x_disable,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife-camif-v10x.h b/drivers/media/platform/qcom/camss/camss-ife-camif-v10x.h
new file mode 100644
index 000000000000..87faa5776c31
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-camif-v10x.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#ifndef _CAMSS_IFE_CAMIF_V10X_H_
+#define _CAMSS_IFE_CAMIF_V10X_H_
+
+#include <linux/bits.h>
+
+#include "camss-ife.h"
+
+/* Per-RDI register offset */
+#define IFE_CAMIF_V10X_RDIX_OFFS(n) ((n) * 0x200)
+
+/* PP config bits */
+#define CAMIF_V10X_PP_CFG_EN		BIT(0)
+#define CAMIF_V10X_PP_CFG_OUTPUT_EN	BIT(8)
+
+/* RDI config bit */
+#define CAMIF_V10X_RDI_CFG_EN		BIT(0)
+
+/* EPOCH line fields */
+#define CAMIF_V10X_CFG_EPOCH0_LINE		GENMASK(13, 0)
+#define CAMIF_V10X_CFG_EPOCH1_LINE		GENMASK(29, 16)
+
+/* Module config register offset */
+#define CAMIF_V10X_MODULE_CFG		0x00000060
+
+/* RDI register offsets */
+#define CAMIF_V10X_RDI_PERIOD_CFG	0x00000068
+#define CAMIF_V10X_RDI_PATTERN_CFG	0x0000006c
+#define CAMIF_V10X_RDI_EPOCH_IRQ_CFG	0x00000070
+
+/* PP (pixel path) register offsets */
+#define CAMIF_V10X_PP_PERIOD_CFG	0x00000078
+#define CAMIF_V10X_PP_PATTERN_CFG	0x0000007c
+#define CAMIF_V10X_PP_EPOCH_IRQ_CFG	0x00000080
+
+extern const struct ife_hw_camif_ops ife_camif_v10x_ops;
+
+#endif /* _CAMSS_IFE_CAMIF_V10X_H_ */
-- 
2.34.1


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

* [PATCH 09/45] media: qcom: camss: sm8250: add IFE scaler v10x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (7 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 08/45] media: qcom: camss: sm8250: add IFE CamIF v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 10/45] media: qcom: camss: sm8250: add IFE module engine v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (36 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the M/N downscaler used by the SM8250 IFE pixel path.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../qcom/camss/camss-ife-scale-v10x.c         | 148 ++++++++++++++++++
 .../qcom/camss/camss-ife-scale-v10x.h         |  15 ++
 3 files changed, 164 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 2da998444554..dc0f307894d8 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -16,6 +16,7 @@ qcom-camss-objs += \
 		camss-format.o \
 		camss-ife-bus-v20x.o \
 		camss-ife-camif-v10x.o \
+		camss-ife-scale-v10x.o \
 		camss-ispif.o \
 		camss-tpg.o \
 		camss-tpg-gen1.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c
new file mode 100644
index 000000000000..a8939114cb4d
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/math.h>
+
+#include "camss-ife.h"
+#include "camss-ife-scale-v10x.h"
+
+/* DS M/N scaler register offsets */
+#define CAMIF_V10X_DS_MN_MODULE_CFG		0x00000060
+#define CAMIF_V10X_DS_MN_CFG			0x00000064
+#define CAMIF_V10X_DS_MN_IMAGE_SIZE_CFG		0x00000068
+#define CAMIF_V10X_DS_MN_H_CFG			0x0000006c
+#define CAMIF_V10X_DS_MN_V_CFG			0x00000074
+#define CAMIF_V10X_DS_MN_CROP_LINE_CFG		0x0000007c
+#define CAMIF_V10X_DS_MN_CROP_PIXEL_CFG		0x00000080
+
+#define PP_V10X_Q21_FORMAT (21)
+
+#define PP_V10X_MODULE_DS_MN_CFG_CROP_EN		BIT(8)
+#define PP_V10X_MODULE_DS_MN_CFG_EN			BIT(0)
+#define PP_V10X_DS_MN_CFG_VERTICAL_SCALE_EN		BIT(10)
+#define PP_V10X_DS_MN_CFG_HORIZONTAL_SCALE_EN		BIT(9)
+#define PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_WIDTH	GENMASK(29, 16)
+#define PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_HEIGHT	GENMASK(13, 0)
+#define PP_V10X_DS_MN_CROP_LINE_CFG_FIRST_LINE		GENMASK(29, 16)
+#define PP_V10X_DS_MN_CROP_LINE_CFG_LAST_LINE		GENMASK(13, 0)
+#define PP_V10X_DS_MN_CROP_PIXEL_CFG_FIRST_PIXEL	GENMASK(29, 16)
+#define PP_V10X_DS_MN_CROP_PIXEL_CFG_LAST_PIXEL		GENMASK(13, 0)
+
+#define PP_V10X_DS_MN_H_CFG_INTERP_RESO	GENMASK(31, 30)
+#define PP_V10X_DS_MN_V_CFG_INTERP_RESO	GENMASK(31, 30)
+
+#define INTERP_RES_Q2_0	(0)	/* 1/128 <= M/N <= 1/16	*/
+#define INTERP_RES_Q2_1	(1)	/* 1/16  <= M/N <= 1/8	*/
+#define INTERP_RES_Q2_2	(2)	/* 1/8   <= M/N <= 1/4	*/
+#define INTERP_RES_Q2_3	(3)	/* 1/4   <= M/N <= 1	*/
+
+static u32 camif_v10x_calc_ds_q2(u32 src_size, u32 dst_size)
+{
+	if ((u64)src_size >= 16 * (u64)dst_size)
+		return INTERP_RES_Q2_0;
+	else if ((u64)src_size >= 8 * (u64)dst_size)
+		return INTERP_RES_Q2_1;
+	else if ((u64)src_size >= 4 * (u64)dst_size)
+		return INTERP_RES_Q2_2;
+
+	return INTERP_RES_Q2_3;
+}
+
+static u32 camif_v10x_calc_ds_q21(u32 src_size, u32 dst_size)
+{
+	u64 rq21;
+
+	rq21 = (u64)src_size << PP_V10X_Q21_FORMAT;
+	do_div(rq21, dst_size);
+
+	return rq21;
+}
+
+static void camif_v10x_crop_config(void __iomem *mod_base, const struct camss_ife_pipeline_cfg *cfg)
+{
+	u32 rval;
+
+	rval = FIELD_PREP(PP_V10X_DS_MN_CROP_PIXEL_CFG_FIRST_PIXEL, cfg->ds_mn_crop->left) |
+	       FIELD_PREP(PP_V10X_DS_MN_CROP_PIXEL_CFG_LAST_PIXEL, cfg->ds_mn_crop->width - 1);
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CROP_PIXEL_CFG);
+
+	rval = FIELD_PREP(PP_V10X_DS_MN_CROP_LINE_CFG_FIRST_LINE, cfg->ds_mn_crop->top) |
+	       FIELD_PREP(PP_V10X_DS_MN_CROP_LINE_CFG_LAST_LINE, cfg->ds_mn_crop->height - 1);
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CROP_LINE_CFG);
+}
+
+void camif_v10x_scale_config(void __iomem *mod_base, const struct camss_ife_pipeline_cfg *cfg,
+			     const struct camss_ife_wm_config *wm_cfg)
+{
+	u32 rval, h_q21, v_q21, h_q2, v_q2, width, height;
+
+	if (cfg->ds_mn_crop) {
+		camif_v10x_crop_config(mod_base, cfg);
+		width	= cfg->ds_mn_crop->width;
+		height	= cfg->ds_mn_crop->height;
+
+		rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+		rval |= PP_V10X_MODULE_DS_MN_CFG_CROP_EN;
+		writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+
+	} else {
+		width	= cfg->in_width;
+		height	= cfg->in_height;
+
+		rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+		rval &= ~PP_V10X_MODULE_DS_MN_CFG_CROP_EN;
+		writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+	}
+
+	rval = FIELD_PREP(PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_WIDTH, width - 1) |
+	       FIELD_PREP(PP_V10X_DS_MN_IMAGE_SIZE_CFG_INPUT_HEIGHT, height - 1);
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_IMAGE_SIZE_CFG);
+
+	h_q2 = camif_v10x_calc_ds_q2(width, wm_cfg->width);
+	h_q21 = camif_v10x_calc_ds_q21(width, wm_cfg->width);
+	rval = FIELD_PREP(PP_V10X_DS_MN_H_CFG_INTERP_RESO, h_q2) | h_q21;
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_H_CFG);
+
+	rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_CFG);
+	if (wm_cfg->width < width)
+		rval |= PP_V10X_DS_MN_CFG_HORIZONTAL_SCALE_EN;
+	else
+		rval &= ~PP_V10X_DS_MN_CFG_HORIZONTAL_SCALE_EN;
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CFG);
+
+	v_q2 = camif_v10x_calc_ds_q2(height, wm_cfg->height);
+	v_q21 = camif_v10x_calc_ds_q21(height, wm_cfg->height);
+	rval = FIELD_PREP(PP_V10X_DS_MN_V_CFG_INTERP_RESO, v_q2) | v_q21;
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_V_CFG);
+
+	rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_CFG);
+	if (wm_cfg->height < height)
+		rval |= PP_V10X_DS_MN_CFG_VERTICAL_SCALE_EN;
+	else
+		rval &= ~PP_V10X_DS_MN_CFG_VERTICAL_SCALE_EN;
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_CFG);
+}
+
+int camif_v10x_scale_enable(void __iomem *mod_base)
+{
+	u32 rval;
+
+	rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+	rval |= PP_V10X_MODULE_DS_MN_CFG_EN;
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+
+	return 0;
+}
+
+int camif_v10x_scale_disable(void __iomem *mod_base)
+{
+	u32 rval;
+
+	rval = readl_relaxed(mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+	rval &= ~PP_V10X_MODULE_DS_MN_CFG_EN;
+	writel_relaxed(rval, mod_base + CAMIF_V10X_DS_MN_MODULE_CFG);
+
+	return 0;
+}
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h
new file mode 100644
index 000000000000..cb548e69f321
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v10x.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2022, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _CAMSS_PP_V10X_SCALE_H_
+#define _CAMSS_PP_V10X_SCALE_H_
+
+void camif_v10x_scale_config(void __iomem *mod_base, const struct camss_ife_pipeline_cfg *cfg,
+			     const struct camss_ife_wm_config *wm_cfg);
+
+int camif_v10x_scale_enable(void __iomem *top);
+
+int camif_v10x_scale_disable(void __iomem *top);
+
+#endif /* _CAMSS_PP_V10X_SCALE_H_ */
-- 
2.34.1


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

* [PATCH 10/45] media: qcom: camss: sm8250: add IFE module engine v10x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (8 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 09/45] media: qcom: camss: sm8250: add IFE scaler v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 11/45] media: qcom: camss: sm8250: add IFE top v20x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (35 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the demosaic, color transform and AWB statistics modules used by
the SM8250 IFE pixel path.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-mod-v10x.c  | 263 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   1 +
 3 files changed, 265 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index dc0f307894d8..92f1938a693d 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -16,6 +16,7 @@ qcom-camss-objs += \
 		camss-format.o \
 		camss-ife-bus-v20x.o \
 		camss-ife-camif-v10x.o \
+		camss-ife-mod-v10x.o \
 		camss-ife-scale-v10x.o \
 		camss-ispif.o \
 		camss-tpg.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
new file mode 100644
index 000000000000..ebc97203595f
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+
+#include "camss-ife.h"
+
+/* Module base offsets and register offsets */
+#define IFE_MOD_V10X_DEMOSAIC_OFFSET	(0x0ACB7800 - 0x0ACB4000)
+#define IFE_MOD_V10X_XFORM_OFFSET	(0x0ACB8000 - 0x0ACB4000)
+#define IFE_MOD_V10X_AWB_BG_OFFSET	0x8400
+
+/* Demosaic registers */
+#define IFE_MOD_V10X_DEM_MODULE_CFG	0x00000060
+#define IFE_MOD_V10X_DEM_WB_GAIN_CFG_0	0x00000068
+#define IFE_MOD_V10X_DEM_WB_GAIN_CFG_1	0x0000006c
+
+/* Color transform register */
+#define IFE_MOD_V10X_XFORM_MODULE_CFG	0x00000060
+
+/* AWB-BG registers */
+#define IFE_MOD_V10X_AWB_BG_MODULE_CFG		0x00000060
+#define IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_0	0x0000006c
+#define IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_1	0x00000070
+#define IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_0	0x00000074
+#define IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_1	0x00000078
+#define IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_0	0x00000080
+#define IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_1	0x00000084
+#define IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_0	0x00000088
+#define IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_1	0x0000008c
+
+/* Demosaic bitfields */
+#define IFE_MOD_V10X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN	BIT(15)
+#define IFE_MOD_V10X_DEMOSAIC_CFG_DYN_G_CLAMP_EN	BIT(14)
+#define IFE_MOD_V10X_DEMOSAIC_CFG_EN			BIT(0)
+#define IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_B_GAIN	GENMASK(30, 16)
+#define IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_G_GAIN	GENMASK(14, 0)
+#define IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG1_R_GAIN	GENMASK(14, 0)
+
+/* Color transform bitfield */
+#define IFE_MOD_V10X_COLOR_XFORM_CFG_EN		BIT(0)
+
+/* AWB-BG bitfields */
+#define IFE_MOD_V10X_AWB_BG_MODULE_EN		BIT(0)
+#define IFE_MOD_V10X_AWB_BG_SAT_STATS_EN	BIT(8)
+#define IFE_MOD_V10X_AWB_BG_HORZ_CFG0_H_NUM	GENMASK(23, 16)
+#define IFE_MOD_V10X_AWB_BG_HORZ_CFG1_WIDTH	GENMASK(8, 0)
+#define IFE_MOD_V10X_AWB_BG_VERT_CFG0_V_NUM	GENMASK(22, 16)
+#define IFE_MOD_V10X_AWB_BG_VERT_CFG1_HEIGHT	GENMASK(9, 0)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG0_GR	GENMASK(29, 16)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG0_R	GENMASK(13, 0)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG1_GB	GENMASK(29, 16)
+#define IFE_MOD_V10X_AWB_BG_THRESH_CFG1_B	GENMASK(13, 0)
+
+/* AWB-BG default grid: 64 horizontal x 48 vertical regions */
+#define IFE_MOD_V10X_AWB_BG_MAX_H_NUM		64
+#define IFE_MOD_V10X_AWB_BG_MAX_V_NUM		48
+
+/* Default saturation threshold applied to all channels */
+#define IFE_MOD_V10X_AWB_BG_THRESHOLD		0x3C3F
+
+/* Default WB gains applied at config time (U15Q10 format) */
+#define IFE_MOD_V10X_DEMOSAIC_G_GAIN_DEF		1024	/* 1.0 U15Q10 */
+#define IFE_MOD_V10X_DEMOSAIC_B_GAIN_DEF		1664	/* 1.625 U15Q10 */
+#define IFE_MOD_V10X_DEMOSAIC_R_GAIN_DEF		2176	/* 2.125 U15Q10 */
+
+/*
+ * Default RGB-to-YUV color transform coefficients.
+ * Each entry is a { value, register-offset } pair relative to xform base.
+ */
+static const struct { u32 val; u32 offs; } mod_v10x_xform_defaults[] = {
+	{ 0x00750259, 0x00000068 },
+	{ 0x00000132, 0x0000006c },
+	{ 0x03ff0000, 0x00000074 },
+	{ 0x01fe1eae, 0x00000078 },
+	{ 0x00001f54, 0x0000007c },
+	{ 0x02000000, 0x00000080 },
+	{ 0x03ff0000, 0x00000084 },
+	{ 0x1fad1e55, 0x00000088 },
+	{ 0x000001fe, 0x0000008c },
+	{ 0x02000000, 0x00000090 },
+	{ 0x03ff0000, 0x00000094 },
+};
+
+static void mod_v10x_demosaic_config(void __iomem *top)
+{
+	void __iomem *base = top + IFE_MOD_V10X_DEMOSAIC_OFFSET;
+	u32 rval;
+
+	rval = FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_B_GAIN,
+			  IFE_MOD_V10X_DEMOSAIC_B_GAIN_DEF) |
+	       FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_G_GAIN,
+			  IFE_MOD_V10X_DEMOSAIC_G_GAIN_DEF);
+	writel_relaxed(rval, base + IFE_MOD_V10X_DEM_WB_GAIN_CFG_0);
+
+	rval = FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG1_R_GAIN,
+			  IFE_MOD_V10X_DEMOSAIC_R_GAIN_DEF);
+	writel_relaxed(rval, base + IFE_MOD_V10X_DEM_WB_GAIN_CFG_1);
+}
+
+static void mod_v10x_demosaic_enable(void __iomem *top)
+{
+	void __iomem *base = top + IFE_MOD_V10X_DEMOSAIC_OFFSET;
+	u32 rval;
+
+	rval = readl_relaxed(base + IFE_MOD_V10X_DEM_MODULE_CFG);
+	rval |= IFE_MOD_V10X_DEMOSAIC_CFG_DYN_G_CLAMP_EN |
+		IFE_MOD_V10X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN |
+		IFE_MOD_V10X_DEMOSAIC_CFG_EN;
+	writel_relaxed(rval, base + IFE_MOD_V10X_DEM_MODULE_CFG);
+}
+
+static void mod_v10x_demosaic_disable(void __iomem *top)
+{
+	void __iomem *base = top + IFE_MOD_V10X_DEMOSAIC_OFFSET;
+	u32 rval;
+
+	rval = readl_relaxed(base + IFE_MOD_V10X_DEM_MODULE_CFG);
+	rval &= ~(IFE_MOD_V10X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN |
+		  IFE_MOD_V10X_DEMOSAIC_CFG_DYN_G_CLAMP_EN  |
+		  IFE_MOD_V10X_DEMOSAIC_CFG_EN);
+	writel_relaxed(rval, base + IFE_MOD_V10X_DEM_MODULE_CFG);
+}
+
+static void mod_v10x_xform_config(void __iomem *top)
+{
+	void __iomem *base = top + IFE_MOD_V10X_XFORM_OFFSET;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mod_v10x_xform_defaults); i++)
+		writel_relaxed(mod_v10x_xform_defaults[i].val,
+			       base + mod_v10x_xform_defaults[i].offs);
+}
+
+static void mod_v10x_xform_enable(void __iomem *top)
+{
+	void __iomem *base = top + IFE_MOD_V10X_XFORM_OFFSET;
+	u32 rval;
+
+	rval = readl_relaxed(base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+	rval |= IFE_MOD_V10X_COLOR_XFORM_CFG_EN;
+	writel_relaxed(rval, base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+}
+
+static void mod_v10x_xform_disable(void __iomem *top)
+{
+	void __iomem *base = top + IFE_MOD_V10X_XFORM_OFFSET;
+	u32 rval;
+
+	rval = readl_relaxed(base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+	rval &= ~IFE_MOD_V10X_COLOR_XFORM_CFG_EN;
+	writel_relaxed(rval, base + IFE_MOD_V10X_XFORM_MODULE_CFG);
+}
+
+static void mod_v10x_awb_bg_config(void __iomem *base,
+				   const struct camss_pp_cfg *cfg)
+{
+	void __iomem *mod_base = base + IFE_MOD_V10X_AWB_BG_OFFSET;
+	u32 width = (cfg->width / IFE_MOD_V10X_AWB_BG_MAX_H_NUM) & ~3u;
+	u32 height = (cfg->height / IFE_MOD_V10X_AWB_BG_MAX_V_NUM) & ~3u;
+	u32 val;
+
+	/* Horizontal: 64 regions, offset 0 */
+	val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_HORZ_CFG0_H_NUM, IFE_MOD_V10X_AWB_BG_MAX_H_NUM - 1);
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_0);
+
+	val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_HORZ_CFG1_WIDTH, width - 1);
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HORZ_RGN_CFG_1);
+
+	/* Vertical: 48 regions, offset 0 */
+	val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_VERT_CFG0_V_NUM, IFE_MOD_V10X_AWB_BG_MAX_V_NUM - 1);
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_0);
+
+	val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_VERT_CFG1_HEIGHT, height - 1);
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_VERT_RGN_CFG_1);
+
+	/* High thresholds: default saturation level for all channels */
+	val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG0_GR, IFE_MOD_V10X_AWB_BG_THRESHOLD) |
+	      FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG0_R, IFE_MOD_V10X_AWB_BG_THRESHOLD);
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_0);
+
+	val = FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG1_GB, IFE_MOD_V10X_AWB_BG_THRESHOLD) |
+	      FIELD_PREP(IFE_MOD_V10X_AWB_BG_THRESH_CFG1_B, IFE_MOD_V10X_AWB_BG_THRESHOLD);
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_HI_THRESH_CFG_1);
+
+	/* Low thresholds: 0 (accept all pixels) */
+	writel_relaxed(0, mod_base + IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_0);
+	writel_relaxed(0, mod_base + IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_1);
+}
+
+static void mod_v10x_awb_bg_enable(void __iomem *base)
+{
+	void __iomem *mod_base = base + IFE_MOD_V10X_AWB_BG_OFFSET;
+	u32 val;
+
+	val = readl_relaxed(mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+	val |= IFE_MOD_V10X_AWB_BG_MODULE_EN | IFE_MOD_V10X_AWB_BG_SAT_STATS_EN;
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+}
+
+static void mod_v10x_awb_bg_disable(void __iomem *base)
+{
+	void __iomem *mod_base = base + IFE_MOD_V10X_AWB_BG_OFFSET;
+	u32 val;
+
+	val = readl_relaxed(mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+	val &= ~(IFE_MOD_V10X_AWB_BG_MODULE_EN | IFE_MOD_V10X_AWB_BG_SAT_STATS_EN);
+	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
+}
+
+static void mod_v10x_modules_acquire(struct ife_device *ife,
+				     struct ife_pp_ctx *ctx,
+				     void __iomem *base)
+{
+	ctx->base = base;
+}
+
+static void mod_v10x_modules_release(struct ife_pp_ctx *ctx)
+{
+	ctx->base = NULL;
+}
+
+static void mod_v10x_modules_config(struct ife_pp_ctx *ctx,
+				    const struct camss_pp_cfg *cfg)
+{
+	void __iomem *base = ctx->base;
+
+	ctx->in_width = cfg->width;
+	ctx->in_height = cfg->height;
+
+	mod_v10x_demosaic_config(base);
+	mod_v10x_xform_config(base);
+	mod_v10x_awb_bg_config(base, cfg);
+}
+
+static void mod_v10x_modules_enable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	mod_v10x_demosaic_enable(base);
+	mod_v10x_xform_enable(base);
+	mod_v10x_awb_bg_enable(base);
+}
+
+static void mod_v10x_modules_disable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	mod_v10x_awb_bg_disable(base);
+	mod_v10x_xform_disable(base);
+	mod_v10x_demosaic_disable(base);
+}
+
+const struct camss_mod_ops ife_mod_v10x_ops = {
+	.acquire	= mod_v10x_modules_acquire,
+	.release	= mod_v10x_modules_release,
+	.config		= mod_v10x_modules_config,
+	.enable		= mod_v10x_modules_enable,
+	.disable	= mod_v10x_modules_disable,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 48701cb67e1e..233b0292c107 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -390,5 +390,6 @@ camss_ife_pipe_for_stream_id(int nstreams,
 			     u32 id);
 
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
+extern const struct camss_mod_ops ife_mod_v10x_ops;
 
 #endif /* CAMSS_IFE_SUB_DEV_H */
-- 
2.34.1


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

* [PATCH 11/45] media: qcom: camss: sm8250: add IFE top v20x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (9 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 10/45] media: qcom: camss: sm8250: add IFE module engine v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 12/45] media: qcom: camss: sm8250: add IFE Lite top v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (34 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the IFE top controller used by the SM8250 full pixel path.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-top-v20x.c  | 570 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   1 +
 3 files changed, 572 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-top-v20x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 92f1938a693d..f26806c69e14 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -18,6 +18,7 @@ qcom-camss-objs += \
 		camss-ife-camif-v10x.o \
 		camss-ife-mod-v10x.o \
 		camss-ife-scale-v10x.o \
+		camss-ife-top-v20x.o \
 		camss-ispif.o \
 		camss-tpg.o \
 		camss-tpg-gen1.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-top-v20x.c b/drivers/media/platform/qcom/camss/camss-ife-top-v20x.c
new file mode 100644
index 000000000000..bd1ce71c2dd4
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-top-v20x.c
@@ -0,0 +1,570 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include "camss-ife-camif-v10x.h"
+#include "camss-ife-scale-v10x.h"
+#include "camss-ife.h"
+#include "camss.h"
+
+/* Block base offsets */
+#define IFE_V20X_TOP_OFFS		0x0000
+#define IFE_V20X_CAMIF_PP_OFFSET	(IFE_V20X_TOP_OFFS + 0x2600)
+#define IFE_V20X_CAMIF_RDI_OFFSET	(IFE_V20X_TOP_OFFS + 0x9a00)
+
+/* Per-output DS M/N scaler base offsets */
+#define IFE_V20X_VID_DS_MN_Y_OFFSET	0x6400
+#define IFE_V20X_DISP_DS_MN_Y_OFFSET	0x4c00
+#define IFE_V20X_FD_DS_MN_Y_OFFSET	0x4400
+#define IFE_V20X_DS_MN_C_OFFSET(addr)	((addr) + 0x200)
+
+/* HW version fields */
+#define IFE_V20X_TOP_VER_MAJOR		GENMASK(31, 28)
+#define IFE_V20X_TOP_VER_MINOR		GENMASK(27, 16)
+#define IFE_V20X_TOP_VER_STEPS		GENMASK(15, 0)
+#define IFE_V20X_TOP_MAJOR_VERSION(val)	FIELD_GET(IFE_V20X_TOP_VER_MAJOR, val)
+#define IFE_V20X_TOP_MINOR_VERSION(val)	FIELD_GET(IFE_V20X_TOP_VER_MINOR, val)
+#define IFE_V20X_TOP_STEPS_VERSION(val)	FIELD_GET(IFE_V20X_TOP_VER_STEPS, val)
+
+/* CSID-level reset and register-update */
+#define IFE_V20X_TOP_RST_CMD_CORE_SW_RST	BIT(0)
+#define IFE_V20X_TOP_IRQ_CMD_CLEAR		BIT(0)
+
+/* IRQ_STATUS_0 bits - overflow, frame drop, reset */
+#define IFE_V20X_TOP_IRQ_S0_PP_OVERFLOW		BIT(31)
+#define IFE_V20X_TOP_IRQ_S0_DUAL_PD_OVERFLOW	BIT(30)
+#define IFE_V20X_TOP_IRQ_S0_RDI0_OVERFLOW	BIT(29)
+#define IFE_V20X_TOP_IRQ_S0_RDI1_OVERFLOW	BIT(28)
+#define IFE_V20X_TOP_IRQ_S0_RDI2_OVERFLOW	BIT(27)
+#define IFE_V20X_TOP_IRQ_S0_PP_FRAME_DROP	BIT(25)
+#define IFE_V20X_TOP_IRQ_S0_DUAL_PD_FRAME_DROP	BIT(24)
+#define IFE_V20X_TOP_IRQ_S0_RDI0_FRAME_DROP	BIT(23)
+#define IFE_V20X_TOP_IRQ_S0_RDI1_FRAME_DROP	BIT(22)
+#define IFE_V20X_TOP_IRQ_S0_RDI2_FRAME_DROP	BIT(21)
+#define IFE_V20X_TOP_IRQ_S0_DSP_OVERFLOW	BIT(9)
+#define IFE_V20X_TOP_IRQ_S0_BUSWR		BIT(7)
+#define IFE_V20X_TOP_IRQ_S0_RST_DONE		BIT(0)
+
+#define	IFE_V20X_TOP_IRQ_S0_ERRORS_MASK \
+	(IFE_V20X_TOP_IRQ_S0_PP_OVERFLOW	| \
+	 IFE_V20X_TOP_IRQ_S0_DUAL_PD_OVERFLOW	| \
+	 IFE_V20X_TOP_IRQ_S0_RDI0_OVERFLOW	| \
+	 IFE_V20X_TOP_IRQ_S0_RDI1_OVERFLOW	| \
+	 IFE_V20X_TOP_IRQ_S0_RDI2_OVERFLOW	| \
+	 IFE_V20X_TOP_IRQ_S0_PP_FRAME_DROP	| \
+	 IFE_V20X_TOP_IRQ_S0_DUAL_PD_FRAME_DROP	| \
+	 IFE_V20X_TOP_IRQ_S0_RDI0_FRAME_DROP	| \
+	 IFE_V20X_TOP_IRQ_S0_RDI1_FRAME_DROP	| \
+	 IFE_V20X_TOP_IRQ_S0_RDI2_FRAME_DROP	| \
+	 IFE_V20X_TOP_IRQ_S0_DSP_OVERFLOW)
+
+/* IRQ_STATUS_1 bits - per-pipe SOF/EOF/EPOCH */
+#define IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH1		BIT(15)
+#define IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH0		BIT(14)
+#define IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EOF		BIT(13)
+#define IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_SOF		BIT(12)
+#define IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH1		BIT(11)
+#define IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH0		BIT(10)
+#define IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EOF		BIT(9)
+#define IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_SOF		BIT(8)
+#define IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH1		BIT(7)
+#define IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH0		BIT(6)
+#define IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EOF		BIT(5)
+#define IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_SOF		BIT(4)
+#define IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EPOCH1		BIT(3)
+#define IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EPOCH0		BIT(2)
+#define IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EOF		BIT(1)
+#define IFE_V20X_TOP_IRQ_S1_PP_CAMIF_SOF		BIT(0)
+
+#define IFE_V20X_TOP_IRQ_S1_RDI0 \
+	(IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH1	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH0	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EOF	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_SOF)
+
+#define IFE_V20X_TOP_IRQ_S1_RDI1 \
+	(IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH1	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH0	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EOF	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_SOF)
+
+#define IFE_V20X_TOP_IRQ_S1_RDI2 \
+	(IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH1	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH0	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EOF	| \
+	 IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_SOF)
+
+#define IFE_V20X_TOP_IRQ_S1_PP \
+	(IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EPOCH1	| \
+	 IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EPOCH0	| \
+	 IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EOF	| \
+	 IFE_V20X_TOP_IRQ_S1_PP_CAMIF_SOF)
+
+/* IRQ_STATUS_2 bits - violations */
+#define IFE_V20X_TOP_IRQ_S2_RDI2_CAMIF_VIOLATION	BIT(19)
+#define IFE_V20X_TOP_IRQ_S2_RDI1_CAMIF_VIOLATION	BIT(18)
+#define IFE_V20X_TOP_IRQ_S2_RDI0_CAMIF_VIOLATION	BIT(17)
+#define IFE_V20X_TOP_IRQ_S2_PP_VIOLATION		BIT(12)
+#define IFE_V20X_TOP_IRQ_S2_PP_CAMIF_VIOLATION		BIT(11)
+#define IFE_V20X_TOP_IRQ_S2_PP_PREPROCESS_VIOLATION	BIT(10)
+
+#define	IFE_V20X_TOP_IRQ_S2_ERRORS_MASK \
+	(IFE_V20X_TOP_IRQ_S2_RDI2_CAMIF_VIOLATION	| \
+	 IFE_V20X_TOP_IRQ_S2_RDI1_CAMIF_VIOLATION	| \
+	 IFE_V20X_TOP_IRQ_S2_RDI0_CAMIF_VIOLATION	| \
+	 IFE_V20X_TOP_IRQ_S2_PP_VIOLATION		| \
+	 IFE_V20X_TOP_IRQ_S2_PP_CAMIF_VIOLATION		| \
+	 IFE_V20X_TOP_IRQ_S2_PP_PREPROCESS_VIOLATION)
+
+/* Pixel-path write-master IDs */
+#define IFE_V20X_CFG0_PP_FULL_Y_WM	0
+#define IFE_V20X_CFG0_PP_FULL_C_WM	1
+#define IFE_V20X_CFG0_PP_DISP_Y_WM	4
+#define IFE_V20X_CFG0_PP_DISP_C_WM	5
+#define IFE_V20X_CFG0_PP_FD_Y_WM	8
+#define IFE_V20X_CFG0_PP_FD_C_WM	9
+
+/* CORE_CFG_0 fields */
+#define IFE_V20X_CFG0_VID_DS16_R2PD_DISABLE		BIT(30)
+#define IFE_V20X_CFG0_VID_DS4_R2PD_DISABLE		BIT(29)
+#define IFE_V20X_CFG0_DISP_DS16_R2PD_DISABLE		BIT(28)
+#define IFE_V20X_CFG0_DISP_DS4_R2PD_DISABLE		BIT(27)
+#define IFE_V20X_CFG0_DUAL_PD_OPERATING_MODE		BIT(13)
+#define IFE_V20X_CFG0_PP_OPERATING_MODE_OFFLINE		1
+#define IFE_V20X_CFG0_PP_OPERATING_MODE			GENMASK(12, 11)
+
+/* CORE_CFG_1 fields */
+#define IFE_V20X_CFG1_BAYER_PLAIN16	1
+#define IFE_V20X_CFG1_PIXEL_RAW_FMT	GENMASK(7, 4)
+
+/* Register-update command bits */
+#define IFE_V20X_TOP_RUP_CMD_RDI2_UPDATE	BIT(3)
+#define IFE_V20X_TOP_RUP_CMD_RDI1_UPDATE	BIT(2)
+#define IFE_V20X_TOP_RUP_CMD_RDI0_UPDATE	BIT(1)
+#define IFE_V20X_TOP_RUP_CMD_PP_UPDATE		BIT(0)
+
+/* Top register offsets */
+#define IFE_V20X_HW_VERSION		0x00000000
+#define IFE_V20X_RST_CMD		0x0000001c
+#define IFE_V20X_CGC_OVERRIDE_0		0x00000020
+#define IFE_V20X_CGC_OVERRIDE_1		0x00000024
+#define IFE_V20X_CGC_OVERRIDE_2		0x00000028
+#define IFE_V20X_CGC_OVERRIDE_3		0x00000094
+#define IFE_V20X_CORE_CFG_0		0x0000002c
+#define IFE_V20X_CORE_CFG_1		0x00000030
+#define IFE_V20X_REG_UPDATE_CMD		0x00000034
+#define IFE_V20X_IRQ_CMD		0x00000038
+#define IFE_V20X_IRQ_MASK_S0		0x0000003c
+#define IFE_V20X_IRQ_MASK_S1		0x00000040
+#define IFE_V20X_IRQ_MASK_S2		0x00000044
+#define IFE_V20X_IRQ_CLEAR_S0		0x00000048
+#define IFE_V20X_IRQ_CLEAR_S1		0x0000004c
+#define IFE_V20X_IRQ_CLEAR_S2		0x00000050
+#define IFE_V20X_IRQ_STATUS_S0		0x00000054
+#define IFE_V20X_IRQ_STATUS_S1		0x00000058
+#define IFE_V20X_IRQ_STATUS_S2		0x0000005c
+
+static inline void __iomem *ife_v20x_get_vid_base(void __iomem *base)
+{
+	return base + IFE_V20X_VID_DS_MN_Y_OFFSET;
+}
+
+static inline void __iomem *ife_v20x_get_disp_base(void __iomem *base)
+{
+	return base + IFE_V20X_DISP_DS_MN_Y_OFFSET;
+}
+
+static inline void __iomem *ife_v20x_get_fd_base(void __iomem *base)
+{
+	return base + IFE_V20X_FD_DS_MN_Y_OFFSET;
+}
+
+static int ife_top_v20x_acquire(struct ife_device *ife, void __iomem *base,
+				void __iomem *csid_base)
+{
+	struct ife_top_ctx *ctx = &ife->top_ctx;
+
+	ctx->base	= base + IFE_V20X_TOP_OFFS;
+	ctx->rdi_base	= base + IFE_V20X_CAMIF_RDI_OFFSET;
+	ctx->pp_base	= base + IFE_V20X_CAMIF_PP_OFFSET;
+	ctx->camif_ops	= ife->res->camif_ops;
+	ctx->dev	= ife->camss->dev;
+
+	return 0;
+}
+
+static void ife_top_v20x_release(struct ife_top_ctx *ctx)
+{
+	ctx->base      = NULL;
+	ctx->camif_ops = NULL;
+}
+
+static int ife_top_v20x_pipe_config(struct ife_top_ctx *ctx,
+				    const struct ife_pad_desc *desc,
+				    const struct camss_ife_pipeline_cfg *pipeline_cfg,
+				    const struct camss_ife_wm_config *wm_cfg)
+{
+	const struct ife_hw_camif_ops *camif_ops = ctx->camif_ops;
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+	void __iomem *base = ctx->base;
+	void __iomem *scale_base;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		camif_ops->config(ctx->rdi_base + IFE_CAMIF_V10X_RDIX_OFFS(hw_pipe),
+				  hw_pipe, pipeline_cfg);
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		/* Config CAMIF hardware pipeline */
+		camif_ops->config(ctx->pp_base, hw_pipe, pipeline_cfg);
+
+		/* Config FULL M/N DS scalers */
+		if (wm_cfg[Y_PLANE].wm_id == IFE_V20X_CFG0_PP_FULL_Y_WM &&
+		    wm_cfg[C_PLANE].wm_id == IFE_V20X_CFG0_PP_FULL_C_WM) {
+			scale_base = ife_v20x_get_vid_base(base);
+			camif_v10x_scale_config(scale_base, pipeline_cfg, &wm_cfg[Y_PLANE]);
+			camif_v10x_scale_config(IFE_V20X_DS_MN_C_OFFSET(scale_base), pipeline_cfg,
+						&wm_cfg[C_PLANE]);
+		}
+
+		/* Config DISP M/N DS scalers */
+		if (wm_cfg[Y_PLANE].wm_id == IFE_V20X_CFG0_PP_DISP_Y_WM &&
+		    wm_cfg[C_PLANE].wm_id == IFE_V20X_CFG0_PP_DISP_C_WM) {
+			scale_base = ife_v20x_get_disp_base(base);
+			camif_v10x_scale_config(scale_base, pipeline_cfg, &wm_cfg[Y_PLANE]);
+			camif_v10x_scale_config(IFE_V20X_DS_MN_C_OFFSET(scale_base), pipeline_cfg,
+						&wm_cfg[C_PLANE]);
+		}
+
+		/* Config FD M/N DS scalers */
+		if (wm_cfg[Y_PLANE].wm_id == IFE_V20X_CFG0_PP_FD_Y_WM &&
+		    wm_cfg[C_PLANE].wm_id == IFE_V20X_CFG0_PP_FD_C_WM) {
+			scale_base = ife_v20x_get_fd_base(base);
+			camif_v10x_scale_config(scale_base, pipeline_cfg, &wm_cfg[Y_PLANE]);
+			camif_v10x_scale_config(IFE_V20X_DS_MN_C_OFFSET(scale_base), pipeline_cfg,
+						&wm_cfg[C_PLANE]);
+		}
+
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid HW pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ife_top_v20x_pipe_enable(struct ife_top_ctx *ctx,
+				    const struct ife_pad_desc *desc)
+{
+	const struct ife_hw_camif_ops *camif_ops = ctx->camif_ops;
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+	void __iomem *base = ctx->base;
+	void __iomem *scale_base;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		camif_ops->enable(ctx->rdi_base + IFE_CAMIF_V10X_RDIX_OFFS(hw_pipe),
+				  hw_pipe);
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		/* Enable FULL M/N DS scalers */
+		scale_base = ife_v20x_get_vid_base(base);
+		camif_v10x_scale_enable(scale_base);
+		camif_v10x_scale_enable(IFE_V20X_DS_MN_C_OFFSET(scale_base));
+
+		/* Enable DISP M/N DS scalers */
+		scale_base = ife_v20x_get_disp_base(base);
+		camif_v10x_scale_enable(scale_base);
+		camif_v10x_scale_enable(IFE_V20X_DS_MN_C_OFFSET(scale_base));
+
+		/* Enable FD M/N DS scalers */
+		scale_base = ife_v20x_get_fd_base(base);
+		camif_v10x_scale_enable(scale_base);
+		camif_v10x_scale_enable(IFE_V20X_DS_MN_C_OFFSET(scale_base));
+
+		/* Enable CAMIF hardware pipeline */
+		camif_ops->enable(ctx->pp_base, hw_pipe);
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid HW pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ife_top_v20x_pipe_disable(struct ife_top_ctx *ctx,
+				     const struct ife_pad_desc *desc)
+{
+	const struct ife_hw_camif_ops *camif_ops = ctx->camif_ops;
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+	void __iomem *base = ctx->base;
+	void __iomem *scale_base;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		camif_ops->disable(ctx->rdi_base + IFE_CAMIF_V10X_RDIX_OFFS(hw_pipe),
+				   hw_pipe);
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		/* Disable FULL M/N DS scalers */
+		scale_base = ife_v20x_get_vid_base(base);
+		camif_v10x_scale_disable(scale_base);
+		camif_v10x_scale_disable(IFE_V20X_DS_MN_C_OFFSET(scale_base));
+
+		/* Disable DISP M/N DS scalers */
+		scale_base = ife_v20x_get_disp_base(base);
+		camif_v10x_scale_disable(scale_base);
+		camif_v10x_scale_disable(IFE_V20X_DS_MN_C_OFFSET(scale_base));
+
+		/* Disable FD M/N DS scalers */
+		scale_base = ife_v20x_get_fd_base(base);
+		camif_v10x_scale_disable(scale_base);
+		camif_v10x_scale_disable(IFE_V20X_DS_MN_C_OFFSET(scale_base));
+
+		/* Disable CAMIF hardware pipeline */
+		camif_ops->disable(ctx->pp_base, hw_pipe);
+
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid HW pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static u32 ife_top_20x_version(struct ife_top_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+	u32 reg = readl_relaxed(base + IFE_V20X_HW_VERSION);
+	struct camss_hw_version hw_ver;
+
+	hw_ver.ids.major = IFE_V20X_TOP_MAJOR_VERSION(reg);
+	hw_ver.ids.minor = IFE_V20X_TOP_MINOR_VERSION(reg);
+	hw_ver.ids.steps = IFE_V20X_TOP_STEPS_VERSION(reg);
+
+	return hw_ver.hw_version;
+}
+
+static void ife_top_20x_reset(struct ife_top_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	writel_relaxed(~0, base + IFE_V20X_IRQ_CLEAR_S0);
+	writel_relaxed(~0, base + IFE_V20X_IRQ_CLEAR_S1);
+	writel_relaxed(~0, base + IFE_V20X_IRQ_CLEAR_S2);
+	writel_relaxed(IFE_V20X_TOP_IRQ_S0_RST_DONE,
+		       base + IFE_V20X_IRQ_MASK_S0);
+	writel_relaxed(IFE_V20X_TOP_RST_CMD_CORE_SW_RST, base + IFE_V20X_RST_CMD);
+}
+
+static void ife_top_20x_reg_update(struct ife_top_ctx *ctx,
+				   const struct ife_pad_desc *dsc)
+{
+	void __iomem *base = ctx->base;
+
+	switch (dsc->hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		writel_relaxed(IFE_V20X_TOP_RUP_CMD_RDI0_UPDATE, base + IFE_V20X_REG_UPDATE_CMD);
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		writel_relaxed(IFE_V20X_TOP_RUP_CMD_RDI1_UPDATE, base + IFE_V20X_REG_UPDATE_CMD);
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		writel_relaxed(IFE_V20X_TOP_RUP_CMD_RDI2_UPDATE, base + IFE_V20X_REG_UPDATE_CMD);
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		writel_relaxed(IFE_V20X_TOP_RUP_CMD_PP_UPDATE, base + IFE_V20X_REG_UPDATE_CMD);
+		break;
+	default:
+		dev_warn(ctx->dev, "The requested RUP is invalid: 0x%x\n", dsc->hw_pipe);
+	}
+}
+
+static void ife_top_20x_prepare(struct ife_top_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+	u32 rval;
+
+	writel_relaxed(~0, base + IFE_V20X_CGC_OVERRIDE_0);
+	writel_relaxed(~0, base + IFE_V20X_CGC_OVERRIDE_1);
+	writel_relaxed(~0, base + IFE_V20X_CGC_OVERRIDE_2);
+	writel_relaxed(~0, base + IFE_V20X_CGC_OVERRIDE_3);
+
+	rval  = IFE_V20X_CFG0_VID_DS16_R2PD_DISABLE;
+	rval |= IFE_V20X_CFG0_VID_DS4_R2PD_DISABLE;
+	rval |= IFE_V20X_CFG0_DUAL_PD_OPERATING_MODE;
+	rval |= IFE_V20X_CFG0_DISP_DS16_R2PD_DISABLE;
+	rval |= IFE_V20X_CFG0_DISP_DS4_R2PD_DISABLE;
+	rval |= FIELD_PREP(IFE_V20X_CFG0_PP_OPERATING_MODE,
+			   IFE_V20X_CFG0_PP_OPERATING_MODE_OFFLINE);
+	writel_relaxed(rval, base + IFE_V20X_CORE_CFG_0);
+
+	rval = FIELD_PREP(IFE_V20X_CFG1_PIXEL_RAW_FMT, IFE_V20X_CFG1_BAYER_PLAIN16);
+	writel_relaxed(rval, base + IFE_V20X_CORE_CFG_1);
+
+	/* Clear pending interrupts */
+	writel_relaxed(~0, base + IFE_V20X_IRQ_CLEAR_S0);
+	writel_relaxed(~0, base + IFE_V20X_IRQ_CLEAR_S1);
+	writel_relaxed(~0, base + IFE_V20X_IRQ_CLEAR_S2);
+	writel_relaxed(IFE_V20X_TOP_IRQ_CMD_CLEAR, base + IFE_V20X_IRQ_CMD);
+
+	/* Enable common, frame drops and overflow interrupts */
+	rval = IFE_V20X_TOP_IRQ_S0_BUSWR | IFE_V20X_TOP_IRQ_S0_ERRORS_MASK;
+	writel_relaxed(rval, base + IFE_V20X_IRQ_MASK_S0);
+
+	/* Enable violation error interrupts */
+	rval = IFE_V20X_TOP_IRQ_S2_ERRORS_MASK;
+	writel_relaxed(rval, base + IFE_V20X_IRQ_MASK_S2);
+}
+
+static void ife_top_20x_enable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *base = ctx->base;
+	u32 mask;
+	u32 rval;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		mask = IFE_V20X_TOP_IRQ_S1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		mask = IFE_V20X_TOP_IRQ_S1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		mask = IFE_V20X_TOP_IRQ_S1_RDI2;
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		mask = IFE_V20X_TOP_IRQ_S1_PP;
+		break;
+	default:
+		WARN_ONCE(true, "The hw_pipe is invalid: 0x%x\n", hw_pipe);
+		return;
+	}
+
+	rval = readl_relaxed(base + IFE_V20X_IRQ_MASK_S1);
+	writel_relaxed(rval | mask, base + IFE_V20X_IRQ_MASK_S1);
+}
+
+static void ife_top_20x_disable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *base = ctx->base;
+	u32 mask;
+	u32 rval;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		mask = IFE_V20X_TOP_IRQ_S1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		mask = IFE_V20X_TOP_IRQ_S1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		mask = IFE_V20X_TOP_IRQ_S1_RDI2;
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		mask = IFE_V20X_TOP_IRQ_S1_PP;
+		break;
+	default:
+		WARN_ONCE(true, "The hw_pipe is invalid: 0x%x\n", hw_pipe);
+		return;
+	}
+
+	rval = readl_relaxed(base + IFE_V20X_IRQ_MASK_S1);
+	writel_relaxed(rval & ~mask, base + IFE_V20X_IRQ_MASK_S1);
+}
+
+static void ife_top_20x_irq_status(struct ife_top_ctx *ctx, u32 isr_stat[IFE_TOP_STATUS_MAX])
+{
+	void __iomem *base = ctx->base;
+	u32 top_stat;
+
+	isr_stat[IFE_TOP_STATUS_ID0] = 0;
+
+	top_stat = readl_relaxed(base + IFE_V20X_IRQ_STATUS_S0);
+	writel_relaxed(top_stat, base + IFE_V20X_IRQ_CLEAR_S0);
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S0_RST_DONE)
+		isr_stat[IFE_TOP_STATUS_ID0] = IFE_HW_TOP_IRQ0_RST_DONE;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S0_BUSWR)
+		isr_stat[IFE_TOP_STATUS_ID0] |= IFE_HW_TOP_IRQ0_BUSWR;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S0_ERRORS_MASK)
+		isr_stat[IFE_TOP_STATUS_ID0] |= top_stat & IFE_V20X_TOP_IRQ_S0_ERRORS_MASK;
+
+	isr_stat[IFE_TOP_STATUS_ID1] = 0;
+
+	top_stat = readl_relaxed(base + IFE_V20X_IRQ_STATUS_S1);
+	writel_relaxed(top_stat, base + IFE_V20X_IRQ_CLEAR_S1);
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI0_SOF;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI1_SOF;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI2_SOF;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_PP_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_PP_SOF;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH0)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI0_EPOCH0;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH1)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI0_EPOCH1;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH0)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI1_EPOCH0;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH1)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI1_EPOCH1;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH0)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI2_EPOCH0;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH1)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI2_EPOCH1;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EPOCH0)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_PP_EPOCH0;
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S1_PP_CAMIF_EPOCH1)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_PP_EPOCH1;
+
+	isr_stat[IFE_TOP_STATUS_ID2] = 0;
+
+	top_stat = readl_relaxed(base + IFE_V20X_IRQ_STATUS_S2);
+	writel_relaxed(top_stat, base + IFE_V20X_IRQ_CLEAR_S2);
+
+	if (top_stat & IFE_V20X_TOP_IRQ_S2_ERRORS_MASK)
+		isr_stat[IFE_TOP_STATUS_ID2] |= top_stat & IFE_V20X_TOP_IRQ_S2_ERRORS_MASK;
+
+	writel_relaxed(IFE_V20X_TOP_IRQ_CMD_CLEAR, base + IFE_V20X_IRQ_CMD);
+}
+
+const struct ife_hw_top_ops ife_top_20x_ops = {
+	.acquire	= ife_top_v20x_acquire,
+	.release	= ife_top_v20x_release,
+	.version	= ife_top_20x_version,
+	.prepare	= ife_top_20x_prepare,
+	.enable		= ife_top_20x_enable,
+	.disable	= ife_top_20x_disable,
+	.pipe_config	= ife_top_v20x_pipe_config,
+	.pipe_enable	= ife_top_v20x_pipe_enable,
+	.pipe_disable	= ife_top_v20x_pipe_disable,
+	.reg_update	= ife_top_20x_reg_update,
+	.reset		= ife_top_20x_reset,
+	.irq_status	= ife_top_20x_irq_status,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 233b0292c107..258af76c94ea 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -389,6 +389,7 @@ camss_ife_pipe_for_stream_id(int nstreams,
 			     const struct camss_ife_stream *streams,
 			     u32 id);
 
+extern const struct ife_hw_top_ops ife_top_20x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
 extern const struct camss_mod_ops ife_mod_v10x_ops;
 
-- 
2.34.1


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

* [PATCH 12/45] media: qcom: camss: sm8250: add IFE Lite top v10x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (10 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 11/45] media: qcom: camss: sm8250: add IFE top v20x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 13/45] media: qcom: camss: sm8250: add IFE pad descriptors Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (33 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the IFE top controller used by the SM8250 lite pixel path.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../qcom/camss/camss-ife-lite-top-v10x.c      | 387 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   2 +
 3 files changed, 390 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-lite-top-v10x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index f26806c69e14..22df889656ba 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -16,6 +16,7 @@ qcom-camss-objs += \
 		camss-format.o \
 		camss-ife-bus-v20x.o \
 		camss-ife-camif-v10x.o \
+		camss-ife-lite-top-v10x.o \
 		camss-ife-mod-v10x.o \
 		camss-ife-scale-v10x.o \
 		camss-ife-top-v20x.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-lite-top-v10x.c b/drivers/media/platform/qcom/camss/camss-ife-lite-top-v10x.c
new file mode 100644
index 000000000000..79b270b3a712
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-lite-top-v10x.c
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include "camss-ife-camif-v10x.h"
+#include "camss-ife.h"
+#include "camss.h"
+
+#define IFE_LT_V10X_TOP_OFFS		0x0000
+#define IFE_LT_V10X_CAMIF_RDI_OFFSET	(IFE_LT_V10X_TOP_OFFS + 0x1200)
+
+#define IFE_LT_V10X_MOD_VER_MAJOR		GENMASK(31, 28)
+#define IFE_LT_V10X_MOD_VER_MINOR		GENMASK(27, 16)
+#define IFE_LT_V10X_MOD_VER_STEPS		GENMASK(15, 0)
+#define IFE_LT_V10X_MOD_MAJOR_VERSION(val)	FIELD_GET(IFE_LT_V10X_MOD_VER_MAJOR, val)
+#define IFE_LT_V10X_MOD_MINOR_VERSION(val)	FIELD_GET(IFE_LT_V10X_MOD_VER_MINOR, val)
+#define IFE_LT_V10X_MOD_STEPS_VERSION(val)	FIELD_GET(IFE_LT_V10X_MOD_VER_STEPS, val)
+
+/* CSID-level reset and register-update (via csid_base) */
+#define IFE_LT_V10X_TOP_RST_CMD_CORE_SW_RST	BIT(1)
+#define IFE_LT_V10X_IIF_IRQ_RUPD_GROUP0	BIT(0)
+#define IFE_LT_V10X_IIF_IRQ_RUPD_GROUP1	BIT(1)
+#define IFE_LT_V10X_IIF_IRQ_RUPD_GROUP2	BIT(2)
+#define IFE_LT_V10X_IIF_IRQ_RUPD_GROUP3	BIT(3)
+#define IFE_LT_V10X_TOP_IRQ_CMD_CLEAR	BIT(0)
+#define IFE_LT_V10X_TOP_IRQ_S0_RST_DONE		BIT(17)
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI3_FRAME_DROP	BIT(8)
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI2_FRAME_DROP	BIT(7)
+
+/* TOP functional IRQ registers */
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI1_FRAME_DROP	BIT(6)
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI0_FRAME_DROP	BIT(5)
+#define IFE_LT_V10X_TOP_IRQ_S0_BUSWR		BIT(4)
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI3_OVERFLOW	BIT(3)
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI2_OVERFLOW	BIT(2)
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI1_OVERFLOW	BIT(1)
+#define IFE_LT_V10X_TOP_IRQ_S0_RDI0_OVERFLOW	BIT(0)
+
+#define	IFE_LT_V10X_TOP_IRQ_S0_ERRORS_MASK	\
+	(IFE_LT_V10X_TOP_IRQ_S0_RDI3_FRAME_DROP	| \
+	 IFE_LT_V10X_TOP_IRQ_S0_RDI2_FRAME_DROP	| \
+	 IFE_LT_V10X_TOP_IRQ_S0_RDI1_FRAME_DROP	| \
+	 IFE_LT_V10X_TOP_IRQ_S0_RDI0_FRAME_DROP	| \
+	 IFE_LT_V10X_TOP_IRQ_S0_RDI3_OVERFLOW	| \
+	 IFE_LT_V10X_TOP_IRQ_S0_RDI2_OVERFLOW	| \
+	 IFE_LT_V10X_TOP_IRQ_S0_RDI1_OVERFLOW	| \
+	 IFE_LT_V10X_TOP_IRQ_S0_RDI0_OVERFLOW)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_EPOCH1	BIT(15)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_EPOCH0	BIT(14)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_EOF		BIT(13)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_SOF		BIT(12)
+
+/* IRQ_STATUS_1 bits - per-RDI SOF/EOF/EPOCH */
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH1	BIT(11)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH0	BIT(10)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_EOF		BIT(9)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_SOF		BIT(8)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH1	BIT(7)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH0	BIT(6)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_EOF		BIT(5)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_SOF		BIT(4)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH1	BIT(3)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH0	BIT(2)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_EOF		BIT(1)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_SOF		BIT(0)
+
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI0 \
+	(IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH1	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_EPOCH0	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_EOF		| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_SOF)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI1 \
+	(IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH1	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_EPOCH0	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_EOF		| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_SOF)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI2 \
+	(IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH1	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_EPOCH0	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_EOF		| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_SOF)
+#define IFE_LT_V10X_TOP_IRQ_S1_RDI3 \
+	(IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_EPOCH1	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_EPOCH0	| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_EOF		| \
+	 IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_SOF)
+
+#define IFE_LT_V10X_TOP_IRQ_S2_DIAG_VIOLATION_IRQ	BIT(12)
+#define IFE_LT_V10X_TOP_IRQ_S2_RDI3_CAMIF_VIOLATION	BIT(11)
+#define IFE_LT_V10X_TOP_IRQ_S2_RDI2_CAMIF_VIOLATION	BIT(10)
+#define IFE_LT_V10X_TOP_IRQ_S2_RDI1_CAMIF_VIOLATION	BIT(9)
+#define IFE_LT_V10X_TOP_IRQ_S2_RDI0_CAMIF_VIOLATION	BIT(8)
+
+#define	IFE_LT_V10X_TOP_IRQ_S2_ERRORS_MASK	\
+	(IFE_LT_V10X_TOP_IRQ_S2_DIAG_VIOLATION_IRQ	| \
+	 IFE_LT_V10X_TOP_IRQ_S2_RDI3_CAMIF_VIOLATION	| \
+	 IFE_LT_V10X_TOP_IRQ_S2_RDI2_CAMIF_VIOLATION	| \
+	 IFE_LT_V10X_TOP_IRQ_S2_RDI1_CAMIF_VIOLATION	| \
+	 IFE_LT_V10X_TOP_IRQ_S2_RDI0_CAMIF_VIOLATION)
+
+#define IFE_LT_V10X_HW_VERSION		0x00000000
+#define IFE_LT_V10X_RST_CMD		0x0000000c
+#define IFE_LT_V10X_REG_UPDATE_CMD	0x00000020
+#define IFE_LT_V10X_IRQ_CMD		0x00000024
+#define IFE_LT_V10X_IRQ_MASK_S0		0x00000028
+#define IFE_LT_V10X_IRQ_MASK_S1		0x0000002c
+#define IFE_LT_V10X_IRQ_MASK_S2		0x00000030
+#define IFE_LT_V10X_IRQ_CLEAR_S0	0x00000034
+#define IFE_LT_V10X_IRQ_CLEAR_S1	0x00000038
+#define IFE_LT_V10X_IRQ_CLEAR_S2	0x0000003c
+#define IFE_LT_V10X_IRQ_STATUS_S0	0x00000040
+#define IFE_LT_V10X_IRQ_STATUS_S1	0x00000044
+#define IFE_LT_V10X_IRQ_STATUS_S2	0x00000048
+
+static int ife_lite_top_10x_acquire(struct ife_device *ife, void __iomem *base,
+				    void __iomem *csid_base)
+{
+	struct ife_top_ctx *ctx = &ife->top_ctx;
+
+	ctx->base	= base + IFE_LT_V10X_TOP_OFFS;
+	ctx->rdi_base	= base + IFE_LT_V10X_CAMIF_RDI_OFFSET;
+	ctx->camif_ops	= ife->res->camif_ops;
+	ctx->dev	= ife->camss->dev;
+
+	return 0;
+}
+
+static void ife_lite_top_10x_release(struct ife_top_ctx *ctx)
+{
+	ctx->base      = NULL;
+	ctx->camif_ops = NULL;
+}
+
+static int ife_lite_top_10x_pipe_config(struct ife_top_ctx *ctx,
+					const struct ife_pad_desc *desc,
+					const struct camss_ife_pipeline_cfg *pipeline_cfg,
+					const struct camss_ife_wm_config *wm_cfg)
+{
+	const struct ife_hw_camif_ops *camif_ops = ctx->camif_ops;
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		camif_ops->config(ctx->rdi_base +
+				  IFE_CAMIF_V10X_RDIX_OFFS(hw_pipe), hw_pipe, pipeline_cfg);
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ife_lite_top_10x_pipe_enable(struct ife_top_ctx *ctx,
+					const struct ife_pad_desc *desc)
+{
+	const struct ife_hw_camif_ops *camif_ops = ctx->camif_ops;
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		camif_ops->enable(ctx->rdi_base + IFE_CAMIF_V10X_RDIX_OFFS(hw_pipe),
+				  hw_pipe);
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ife_lite_top_10x_pipe_disable(struct ife_top_ctx *ctx,
+					 const struct ife_pad_desc *desc)
+{
+	const struct ife_hw_camif_ops *camif_ops = ctx->camif_ops;
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		camif_ops->disable(ctx->rdi_base + IFE_CAMIF_V10X_RDIX_OFFS(hw_pipe),
+				   hw_pipe);
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static u32 ife_lite_top_10x_version(struct ife_top_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+	u32 reg = readl_relaxed(base + IFE_LT_V10X_HW_VERSION);
+	struct camss_hw_version hw_ver;
+
+	hw_ver.ids.major = IFE_LT_V10X_MOD_MAJOR_VERSION(reg);
+	hw_ver.ids.minor = IFE_LT_V10X_MOD_MINOR_VERSION(reg);
+	hw_ver.ids.steps = IFE_LT_V10X_MOD_STEPS_VERSION(reg);
+
+	return hw_ver.hw_version;
+}
+
+static void ife_lite_top_10x_reset(struct ife_top_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	writel_relaxed(~0, base + IFE_LT_V10X_IRQ_CLEAR_S0);
+	writel_relaxed(~0, base + IFE_LT_V10X_IRQ_CLEAR_S1);
+	writel_relaxed(~0, base + IFE_LT_V10X_IRQ_CLEAR_S2);
+	writel_relaxed(IFE_LT_V10X_TOP_IRQ_S0_RST_DONE,
+		       base + IFE_LT_V10X_IRQ_MASK_S0);
+	writel_relaxed(IFE_LT_V10X_TOP_RST_CMD_CORE_SW_RST, base + IFE_LT_V10X_RST_CMD);
+}
+
+static void ife_lite_top_10x_reg_update(struct ife_top_ctx *ctx,
+					const struct ife_pad_desc *dsc)
+{
+	void __iomem *base = ctx->base;
+
+	switch (dsc->hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		writel_relaxed(IFE_LT_V10X_IIF_IRQ_RUPD_GROUP0, base + IFE_LT_V10X_REG_UPDATE_CMD);
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		writel_relaxed(IFE_LT_V10X_IIF_IRQ_RUPD_GROUP1, base + IFE_LT_V10X_REG_UPDATE_CMD);
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		writel_relaxed(IFE_LT_V10X_IIF_IRQ_RUPD_GROUP2, base + IFE_LT_V10X_REG_UPDATE_CMD);
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		writel_relaxed(IFE_LT_V10X_IIF_IRQ_RUPD_GROUP3, base + IFE_LT_V10X_REG_UPDATE_CMD);
+		break;
+	default:
+		dev_warn(ctx->dev, "The requested RUP is invalid: 0x%x\n", dsc->hw_pipe);
+	}
+}
+
+static void ife_lite_top_10x_prepare(struct ife_top_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+	u32 rval;
+
+	/* Clear pending interrupts */
+	writel_relaxed(~0, base + IFE_LT_V10X_IRQ_CLEAR_S0);
+	writel_relaxed(~0, base + IFE_LT_V10X_IRQ_CLEAR_S1);
+	writel_relaxed(~0, base + IFE_LT_V10X_IRQ_CLEAR_S2);
+	writel_relaxed(IFE_LT_V10X_TOP_IRQ_CMD_CLEAR, base + IFE_LT_V10X_IRQ_CMD);
+
+	/* Enable common, frame drops and overflow interrupts */
+	rval = IFE_LT_V10X_TOP_IRQ_S0_BUSWR | IFE_LT_V10X_TOP_IRQ_S0_ERRORS_MASK;
+	writel_relaxed(rval, base + IFE_LT_V10X_IRQ_MASK_S0);
+
+	/* Enable violation error interrupts */
+	rval = IFE_LT_V10X_TOP_IRQ_S2_ERRORS_MASK;
+	writel_relaxed(rval, base + IFE_LT_V10X_IRQ_MASK_S2);
+}
+
+static void ife_lite_top_10x_enable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *base = ctx->base;
+	u32 mask;
+	u32 rval;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI2;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI3;
+		break;
+	default:
+		WARN_ONCE(true, "The hw_pipe is invalid: 0x%x\n", hw_pipe);
+		return;
+	}
+
+	rval = readl_relaxed(base + IFE_LT_V10X_IRQ_MASK_S1);
+	writel_relaxed(rval | mask, base + IFE_LT_V10X_IRQ_MASK_S1);
+}
+
+static void ife_lite_top_10x_disable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *base = ctx->base;
+	u32 mask;
+	u32 rval;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI2;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		mask = IFE_LT_V10X_TOP_IRQ_S1_RDI3;
+		break;
+	default:
+		WARN_ONCE(true, "The hw_pipe is invalid: 0x%x\n", hw_pipe);
+		return;
+	}
+
+	rval = readl_relaxed(base + IFE_LT_V10X_IRQ_MASK_S1);
+	writel_relaxed(rval & ~mask, base + IFE_LT_V10X_IRQ_MASK_S1);
+}
+
+static void ife_lite_top_10x_irq_status(struct ife_top_ctx *ctx, u32 isr_stat[IFE_TOP_STATUS_MAX])
+{
+	void __iomem *base = ctx->base;
+	u32 top_stat;
+
+	top_stat = readl_relaxed(base + IFE_LT_V10X_IRQ_STATUS_S0);
+	writel_relaxed(top_stat, base + IFE_LT_V10X_IRQ_CLEAR_S0);
+
+	isr_stat[IFE_TOP_STATUS_ID0] = 0;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S0_RST_DONE)
+		isr_stat[IFE_TOP_STATUS_ID0] = IFE_HW_TOP_IRQ0_RST_DONE;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S0_BUSWR)
+		isr_stat[IFE_TOP_STATUS_ID0] |= IFE_HW_TOP_IRQ0_BUSWR;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S0_ERRORS_MASK)
+		isr_stat[IFE_TOP_STATUS_ID0] |= top_stat & IFE_LT_V10X_TOP_IRQ_S0_ERRORS_MASK;
+
+	top_stat = readl_relaxed(base + IFE_LT_V10X_IRQ_STATUS_S1);
+	writel_relaxed(top_stat, base + IFE_LT_V10X_IRQ_CLEAR_S1);
+
+	isr_stat[IFE_TOP_STATUS_ID1] = 0;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S1_RDI0_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI0_SOF;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S1_RDI1_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI1_SOF;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S1_RDI2_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI2_SOF;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S1_RDI3_CAMIF_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI3_SOF;
+
+	top_stat = readl_relaxed(base + IFE_LT_V10X_IRQ_STATUS_S2);
+	writel_relaxed(top_stat, base + IFE_LT_V10X_IRQ_CLEAR_S2);
+
+	isr_stat[IFE_TOP_STATUS_ID2] = 0;
+
+	if (top_stat & IFE_LT_V10X_TOP_IRQ_S2_ERRORS_MASK)
+		isr_stat[IFE_TOP_STATUS_ID2] |= top_stat & IFE_LT_V10X_TOP_IRQ_S2_ERRORS_MASK;
+
+	writel_relaxed(IFE_LT_V10X_TOP_IRQ_CMD_CLEAR, base + IFE_LT_V10X_IRQ_CMD);
+}
+
+const struct ife_hw_top_ops ife_lite_top_10x_ops = {
+	.acquire	= ife_lite_top_10x_acquire,
+	.release	= ife_lite_top_10x_release,
+	.version	= ife_lite_top_10x_version,
+	.prepare	= ife_lite_top_10x_prepare,
+	.enable		= ife_lite_top_10x_enable,
+	.disable	= ife_lite_top_10x_disable,
+	.pipe_config	= ife_lite_top_10x_pipe_config,
+	.pipe_enable	= ife_lite_top_10x_pipe_enable,
+	.pipe_disable	= ife_lite_top_10x_pipe_disable,
+	.reg_update	= ife_lite_top_10x_reg_update,
+	.reset		= ife_lite_top_10x_reset,
+	.irq_status	= ife_lite_top_10x_irq_status,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 258af76c94ea..a36efd8a6352 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -393,4 +393,6 @@ extern const struct ife_hw_top_ops ife_top_20x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
 extern const struct camss_mod_ops ife_mod_v10x_ops;
 
+extern const struct ife_hw_top_ops ife_lite_top_10x_ops;
+
 #endif /* CAMSS_IFE_SUB_DEV_H */
-- 
2.34.1


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

* [PATCH 13/45] media: qcom: camss: sm8250: add IFE pad descriptors
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (11 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 12/45] media: qcom: camss: sm8250: add IFE Lite top v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 14/45] media: qcom: camss: formats: add pixel format lookup helpers Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (32 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the pad, format, size and write-master tables for the SM8250 full
and lite IFE blocks.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-pads.c      | 306 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   2 +
 3 files changed, 309 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-pads.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 22df889656ba..547da75b24b4 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -20,6 +20,7 @@ qcom-camss-objs += \
 		camss-ife-mod-v10x.o \
 		camss-ife-scale-v10x.o \
 		camss-ife-top-v20x.o \
+		camss-ife-pads.o \
 		camss-ispif.o \
 		camss-tpg.o \
 		camss-tpg-gen1.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-pads.c b/drivers/media/platform/qcom/camss/camss-ife-pads.c
new file mode 100644
index 000000000000..926ba96a7603
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-pads.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/videodev2.h>
+#include <media/v4l2-mediabus.h>
+
+#include "camss-format.h"
+#include "camss-ife.h"
+
+static const struct camss_format_info ife_sink_fmts_v20x[] = {
+	{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+	{ MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+	{ MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+	{ MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+	{ MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+	{ MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+	{ MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+	{ MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+	{ MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+	{ MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+	{ MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+	{ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+	{ MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+	{ MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+	{ MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+	{ MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+	{ MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+	{ MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+	{ MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+	{ MEDIA_BUS_FMT_Y8_1X8, 8, V4L2_PIX_FMT_GREY, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+	{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+};
+
+static const struct camss_format_info ife_vid_pix_fmts_v20x[] = {
+	{ MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+};
+
+static const struct ife_pad_limits ife_vid_limits_v20x = {
+	.min_width	= 320,
+	.min_height	= 240,
+	.max_width	= 4928,
+	.max_height	= 8192,
+	.max_h_ratio	= 16,
+	.max_v_ratio	= 16,
+};
+
+static const struct ife_pad_limits ife_fd_limits = {
+	.min_width	= 320,
+	.min_height	= 240,
+	.max_width	= 1920,
+	.max_height	= 1080,
+	.max_h_ratio	= 16,
+	.max_v_ratio	= 16,
+};
+
+static const struct ife_pad_desc ife_lite_pad_desc_v20x[] = {
+	{
+		.pad_flags = MEDIA_PAD_FL_SINK,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "rdi0",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI0,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI0,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(0),
+		.comp_group_mask = BIT(4),
+		.num_wms = 1,
+		.wm = { 0 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "rdi1",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI1,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI1,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(1),
+		.comp_group_mask = BIT(5),
+		.num_wms = 1,
+		.wm = { 1 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "rdi2",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI2,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI2,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(2),
+		.comp_group_mask = BIT(6),
+		.num_wms = 1,
+		.wm = { 2 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "rdi3",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI3,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI3,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(3),
+		.comp_group_mask = BIT(7),
+		.num_wms = 1,
+		.wm = { 3 },
+	},
+};
+
+static const struct ife_pad_desc ife_pad_desc_v20x[] = {
+	{
+		.pad_flags = MEDIA_PAD_FL_SINK,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "rdi0",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI0,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI0,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(3),
+		.comp_group_mask = BIT(17),
+		.num_wms = 1,
+		.wm = { 23 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "rdi1",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI1,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI1,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(4),
+		.comp_group_mask = BIT(18),
+		.num_wms = 1,
+		.wm = { 24 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "rdi2",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI2,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI2,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(5),
+		.comp_group_mask = BIT(19),
+		.num_wms = 1,
+		.wm = { 25 },
+	},
+
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_vid_pix_fmts_v20x),
+			.formats = ife_vid_pix_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "vid_full",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_VID_FULL,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+		.packing_fmt = 0x1,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_LINE_BASED,
+		.rup_group_mask = BIT(0),
+		.comp_group_mask = BIT(6),
+		.num_wms = 2,
+		.wm = { 0, 1 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_vid_pix_fmts_v20x),
+			.formats = ife_vid_pix_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v20x,
+		.video_name = "disp_full",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_DISP_FULL,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+		.packing_fmt = 0x1,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_LINE_BASED,
+		.rup_group_mask = BIT(0),
+		.comp_group_mask = BIT(7),
+		.num_wms = 2,
+		.wm = { 4, 5 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_vid_pix_fmts_v20x),
+			.formats = ife_vid_pix_fmts_v20x,
+		},
+		.limits = &ife_fd_limits,
+		.video_name = "fd",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_FD,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+		.packing_fmt = 0x1,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_LINE_BASED,
+		.rup_group_mask = BIT(0),
+		.comp_group_mask = BIT(8),
+		.num_wms = 2,
+		.wm[0] = 8,
+		.wm[1] = 9,
+	},
+};
+
+const struct ife_pads_descriptors ife_lite_pads_v20x = {
+	.num_desc = ARRAY_SIZE(ife_lite_pad_desc_v20x),
+	.desc = ife_lite_pad_desc_v20x,
+};
+
+const struct ife_pads_descriptors ife_pads_v20x = {
+	.num_desc = ARRAY_SIZE(ife_pad_desc_v20x),
+	.desc = ife_pad_desc_v20x,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index a36efd8a6352..45fce7e33c4a 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -389,10 +389,12 @@ camss_ife_pipe_for_stream_id(int nstreams,
 			     const struct camss_ife_stream *streams,
 			     u32 id);
 
+extern const struct ife_pads_descriptors ife_pads_v20x;
 extern const struct ife_hw_top_ops ife_top_20x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
 extern const struct camss_mod_ops ife_mod_v10x_ops;
 
+extern const struct ife_pads_descriptors ife_lite_pads_v20x;
 extern const struct ife_hw_top_ops ife_lite_top_10x_ops;
 
 #endif /* CAMSS_IFE_SUB_DEV_H */
-- 
2.34.1


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

* [PATCH 14/45] media: qcom: camss: formats: add pixel format lookup helpers
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (12 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 13/45] media: qcom: camss: sm8250: add IFE pad descriptors Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 15/45] media: qcom: camss: video: use helpers for line-based formats Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (31 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add helpers to find a memory pixel format for a media bus code and to
calculate the stored depth of a V4L2 pixel format plane. IFE format and
performance setup require both lookups.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../media/platform/qcom/camss/camss-format.c  | 31 +++++++++++++++++++
 .../media/platform/qcom/camss/camss-format.h  |  3 ++
 2 files changed, 34 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c
index 52cb01306ee0..147287e7f033 100644
--- a/drivers/media/platform/qcom/camss/camss-format.c
+++ b/drivers/media/platform/qcom/camss/camss-format.c
@@ -12,6 +12,8 @@
 #include <linux/errno.h>
 #include <linux/lcm.h>
 
+#include <media/v4l2-common.h>
+
 #include "camss-format.h"
 
 /*
@@ -103,3 +105,32 @@ int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_forma
 
 	return -EINVAL;
 }
+
+int camss_get_pixfmt_index(u32 code, const struct camss_format_info *formats,
+			   unsigned int nformats)
+{
+	unsigned int i;
+
+	for (i = 0; i < nformats; i++) {
+		if (formats[i].code == code && formats[i].pixelformat)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+int camss_get_pixfmt_depth(u32 pixelformat, u8 plane, u8 *bits_per_pixel)
+{
+	const struct v4l2_format_info *info;
+
+	info = v4l2_format_info(pixelformat);
+	if (!info)
+		return -EINVAL;
+
+	if (plane >= info->comp_planes || !info->bpp_div[plane])
+		return -EINVAL;
+
+	*bits_per_pixel = BITS_PER_BYTE * info->bpp[plane] / info->bpp_div[plane];
+
+	return 0;
+}
diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h
index 4f87ac8c4975..f1af2d3a2c54 100644
--- a/drivers/media/platform/qcom/camss/camss-format.h
+++ b/drivers/media/platform/qcom/camss/camss-format.h
@@ -59,5 +59,8 @@ unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *f);
 u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code);
 int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats,
 			     unsigned int nformats);
+int camss_get_pixfmt_index(u32 code, const struct camss_format_info *formats,
+			   unsigned int nformats);
+int camss_get_pixfmt_depth(u32 pixelformat, u8 plane, u8 *bits_per_pixel);
 
 #endif /* __CAMSS_FORMAT_H__ */
-- 
2.34.1


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

* [PATCH 15/45] media: qcom: camss: video: use helpers for line-based formats
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (13 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 14/45] media: qcom: camss: formats: add pixel format lookup helpers Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 16/45] media: qcom: camss: video: add managed streaming callbacks Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (30 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Use the V4L2 format helpers to calculate line-based plane sizes from
the selected format.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../media/platform/qcom/camss/camss-video.c   | 76 +++++++++++--------
 1 file changed, 44 insertions(+), 32 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index c570d7f9258c..803e5ce9b9fd 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -7,7 +7,9 @@
  * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
  * Copyright (C) 2015-2018 Linaro Ltd.
  */
+#include <linux/align.h>
 #include <linux/slab.h>
+
 #include <media/media-entity.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
@@ -513,29 +515,59 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
 	return 0;
 }
 
+static int video_try_line_based(struct camss_video *video, struct v4l2_format *f)
+{
+	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+	const struct v4l2_format_info *info;
+	unsigned int i;
+	u32 alignment = video->bpl_alignment ?: 1;
+
+	for (i = 0; i < video->nformats; i++) {
+		if (pix_mp->pixelformat == video->formats[i].pixelformat)
+			break;
+	}
+
+	if (i == video->nformats)
+		pix_mp->pixelformat = video->formats[0].pixelformat;
+
+	info = v4l2_format_info(pix_mp->pixelformat);
+	if (!info) {
+		dev_err(video->camss->dev, "No V4L2 info for requested format\n");
+		return -EINVAL;
+	}
+
+	pix_mp->width = clamp_t(u32, pix_mp->width, 1, CAMSS_FRAME_MAX_WIDTH);
+	pix_mp->height = clamp_t(u32, pix_mp->height, 1, CAMSS_FRAME_MAX_HEIGHT_PIX);
+
+	pix_mp->width = max_t(u32, round_down(pix_mp->width, info->hdiv), info->hdiv);
+	pix_mp->height = max_t(u32, round_down(pix_mp->height, info->vdiv), info->vdiv);
+
+	pix_mp->field = V4L2_FIELD_NONE;
+	pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
+	pix_mp->flags = 0;
+	pix_mp->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_mp->colorspace);
+	pix_mp->quantization =
+		V4L2_MAP_QUANTIZATION_DEFAULT(true, pix_mp->colorspace,
+					      pix_mp->ycbcr_enc);
+	pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
+
+	return v4l2_fill_pixfmt_mp_aligned(pix_mp, pix_mp->pixelformat,
+					   pix_mp->width, pix_mp->height, alignment);
+}
+
 static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
 {
 	unsigned int alignment = video->bpl_alignment;
 	struct v4l2_pix_format_mplane *pix_mp;
 	const struct camss_format_info *fi;
-	struct v4l2_plane_pix_format *p;
-	u32 bytesperline[3] = { 0 };
-	u32 sizeimage[3] = { 0 };
 	u32 width, height;
-	u32 bpl, lines;
+	u32 bpl;
 	int i, j;
 
 	pix_mp = &f->fmt.pix_mp;
 
 	if (video->line_based)
-		for (i = 0; i < pix_mp->num_planes && i < 3; i++) {
-			p = &pix_mp->plane_fmt[i];
-			bytesperline[i] = clamp_t(u32, p->bytesperline,
-						  1, 65528);
-			sizeimage[i] = clamp_t(u32, p->sizeimage,
-					       bytesperline[i],
-					       bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX);
-		}
+		return video_try_line_based(video, f);
 
 	for (j = 0; j < video->nformats; j++)
 		if (pix_mp->pixelformat == video->formats[j].pixelformat)
@@ -574,26 +606,6 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
 					pix_mp->colorspace, pix_mp->ycbcr_enc);
 	pix_mp->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_mp->colorspace);
 
-	if (video->line_based)
-		for (i = 0; i < pix_mp->num_planes; i++) {
-			p = &pix_mp->plane_fmt[i];
-			p->bytesperline = clamp_t(u32, p->bytesperline,
-						  1, 65528);
-			p->sizeimage = clamp_t(u32, p->sizeimage,
-					       p->bytesperline,
-					       p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX);
-			lines = p->sizeimage / p->bytesperline;
-
-			if (p->bytesperline < bytesperline[i])
-				p->bytesperline = roundup(bytesperline[i], alignment);
-
-			if (p->sizeimage < p->bytesperline * lines)
-				p->sizeimage = p->bytesperline * lines;
-
-			if (p->sizeimage < sizeimage[i])
-				p->sizeimage = sizeimage[i];
-		}
-
 	return 0;
 }
 
-- 
2.34.1


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

* [PATCH 16/45] media: qcom: camss: video: add managed streaming callbacks
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (14 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 15/45] media: qcom: camss: video: use helpers for line-based formats Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 17/45] media: qcom: camss: add common IFE subdevice core Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (29 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Allow a CAMSS video node to start and stop streaming and select its
buffer type. Keep the existing path when callbacks are absent.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss-video.c | 17 ++++++++++++++++-
 drivers/media/platform/qcom/camss/camss-video.h |  2 ++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 803e5ce9b9fd..cb9e08e9ff7e 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -159,6 +159,7 @@ static int video_buf_init(struct vb2_buffer *vb)
 	struct sg_table *sgt;
 	unsigned int i;
 
+	memset(buffer->addr, 0, sizeof(buffer->addr));
 	for (i = 0; i < format->num_planes; i++) {
 		sgt = vb2_dma_sg_plane_desc(vb, i);
 		if (!sgt)
@@ -303,6 +304,14 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
 	if (ret < 0)
 		goto error;
 
+	if (video->ops->start_streaming) {
+		ret = video->ops->start_streaming(video, count);
+		if (ret < 0)
+			goto error;
+
+		return 0;
+	}
+
 	src_pad = media_pad_remote_pad_first(sink_pad);
 	if (src_pad && is_media_entity_v4l2_subdev(src_pad->entity))
 		subdev = media_entity_to_v4l2_subdev(src_pad->entity);
@@ -370,6 +379,11 @@ static void video_stop_streaming(struct vb2_queue *q)
 	struct v4l2_subdev *subdev = NULL;
 	int ret;
 
+	if (video->ops->stop_streaming) {
+		video->ops->stop_streaming(video);
+		goto stop_pipeline;
+	}
+
 	src_pad = media_pad_remote_pad_first(sink_pad);
 	if (src_pad && is_media_entity_v4l2_subdev(src_pad->entity))
 		subdev = media_entity_to_v4l2_subdev(src_pad->entity);
@@ -382,6 +396,7 @@ static void video_stop_streaming(struct vb2_queue *q)
 	if (ret)
 		dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
 
+stop_pipeline:
 	video_device_pipeline_stop(vdev);
 
 	video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
@@ -766,7 +781,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
 	q->drv_priv = video;
 	q->mem_ops = &vb2_dma_sg_memops;
 	q->ops = &msm_video_vb2_q_ops;
-	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	q->type = video->type;
 	q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
 	q->buf_struct_size = sizeof(struct camss_buffer);
diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h
index d3e56e240a88..f62a72c29a5a 100644
--- a/drivers/media/platform/qcom/camss/camss-video.h
+++ b/drivers/media/platform/qcom/camss/camss-video.h
@@ -31,6 +31,8 @@ struct camss_video_ops {
 	int (*queue_buffer)(struct camss_video *vid, struct camss_buffer *buf);
 	int (*flush_buffers)(struct camss_video *vid,
 			     enum vb2_buffer_state state);
+	int (*start_streaming)(struct camss_video *vid, unsigned int count);
+	void (*stop_streaming)(struct camss_video *vid);
 };
 
 struct camss_video {
-- 
2.34.1


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

* [PATCH 17/45] media: qcom: camss: add common IFE subdevice core
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (15 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 16/45] media: qcom: camss: video: add managed streaming callbacks Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-07  9:42   ` Bryan O'Donoghue
  2026-10-06 22:39 ` [PATCH 18/45] media: qcom: camss: integrate IFE devices Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (28 subsequent siblings)
  45 siblings, 1 reply; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the common IFE subdevice core. It manages routed streams and
formats. It also handles buffers, hardware pipelines, and media
entities.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |    1 +
 drivers/media/platform/qcom/camss/camss-ife.c | 1617 +++++++++++++++++
 drivers/media/platform/qcom/camss/camss.h     |   46 +
 3 files changed, 1664 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 547da75b24b4..057c171e08b9 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -14,6 +14,7 @@ qcom-camss-objs += \
 		camss-csiphy-2ph-1-0.o \
 		camss-csiphy-3ph-1-0.o \
 		camss-format.o \
+		camss-ife.o \
 		camss-ife-bus-v20x.o \
 		camss-ife-camif-v10x.o \
 		camss-ife-lite-top-v10x.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife.c b/drivers/media/platform/qcom/camss/camss-ife.c
new file mode 100644
index 000000000000..cf1474e5bce4
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife.c
@@ -0,0 +1,1617 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/align.h>
+#include <linux/minmax.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+
+#include "camss.h"
+#include "camss-csid.h"
+#include "camss-ife.h"
+
+#define CAMSS_IFE_PREFIX "ife"
+#define CAMSS_IFE_IRQ_NAME "msm_vfe"
+
+#define IFE_IMAGE_SINK_PAD	0
+#define IFE_FIRST_SOURCE_PAD	1
+
+static bool is_rdi_hw_pipe(enum camss_ife_hw_pipe pipe)
+{
+	return pipe == CAMSS_IFE_HW_PIPE_RDI0 ||
+	       pipe == CAMSS_IFE_HW_PIPE_RDI1 ||
+	       pipe == CAMSS_IFE_HW_PIPE_RDI2 ||
+	       pipe == CAMSS_IFE_HW_PIPE_RDI3;
+}
+
+static bool is_pix_hw_pipe(enum camss_ife_hw_pipe pipe)
+{
+	return pipe == CAMSS_IFE_HW_PIPE_PP;
+}
+
+u32 camss_ife_stream_id_for_pipe(int nstreams,
+				 const struct camss_ife_stream *streams,
+				 enum camss_ife_hw_pipe hw_pipe)
+{
+	int i;
+
+	for (i = 0; i < nstreams; i++) {
+		if (streams[i].pipe == hw_pipe)
+			return streams[i].id;
+	}
+
+	return 0;
+}
+
+enum camss_ife_hw_pipe
+camss_ife_pipe_for_stream_id(int nstreams,
+			     const struct camss_ife_stream *streams,
+			     u32 id)
+{
+	int i;
+
+	for (i = 0; i < nstreams; i++) {
+		if (streams[i].id == id)
+			return streams[i].pipe;
+	}
+
+	return CAMSS_IFE_HW_PIPE_INVALID;
+}
+
+static bool is_stats_type(const struct ife_pad_desc *pad)
+{
+	return pad->buf_type == V4L2_BUF_TYPE_META_CAPTURE;
+}
+
+static bool is_meta_type(const struct ife_pad_desc *pad)
+{
+	return is_stats_type(pad);
+}
+
+static void ife_pp_notify_sof_event(struct ife_device *ife, u32 seq_number)
+{
+	struct v4l2_event event = {0};
+
+	event.type = V4L2_EVENT_FRAME_SYNC;
+	event.u.frame_sync.frame_sequence = seq_number;
+
+	v4l2_subdev_notify_event(&ife->sd, &event);
+}
+
+static int ife_event_subscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+			       struct v4l2_event_subscription *sub)
+{
+	if (sub->type != V4L2_EVENT_FRAME_SYNC)
+		return -EINVAL;
+
+	/* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */
+	if (sub->id != 0)
+		return -EINVAL;
+
+	return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
+static inline bool is_ife_hw_wm_fifo_full(struct ife_video_pad *vid_pad)
+{
+	const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+	const struct ife_hw_buswr_ops *buswr_ops = vid_pad->ife->res->buswr_ops;
+
+	return buswr_ops->is_fifo_full(&vid_pad->ife->buswr_ctx, dsc->wm[0]);
+}
+
+static struct camss_buffer *__ife_hw_wm_dqbuf(struct ife_video_pad *vid_pad)
+{
+	struct camss_buffer *buf;
+
+	lockdep_assert_held(&vid_pad->ife->bufs_lock);
+
+	if (list_empty(&vid_pad->processing_bufs))
+		return NULL;
+
+	/* Add check with last processing buffer in WM */
+	buf = list_first_entry(&vid_pad->processing_bufs, struct camss_buffer,
+			       queue);
+	list_del(&buf->queue);
+
+	return buf;
+}
+
+static void __ife_hw_wm_qbuf(struct ife_video_pad *vid_pad,
+			     struct camss_buffer *buf)
+{
+	const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+	const struct ife_hw_buswr_ops *buswr_ops = vid_pad->ife->res->buswr_ops;
+
+	lockdep_assert_held(&vid_pad->ife->bufs_lock);
+
+	buswr_ops->queue_buf(&vid_pad->ife->buswr_ctx, dsc->num_wms, dsc->wm, buf->addr);
+	INIT_LIST_HEAD(&buf->queue);
+	list_add_tail(&buf->queue, &vid_pad->processing_bufs);
+}
+
+static bool __ife_hw_wm_queue_next_pending_buf(struct ife_video_pad *vid_pad)
+{
+	struct camss_buffer *buf;
+
+	lockdep_assert_held(&vid_pad->ife->bufs_lock);
+
+	if (!list_empty(&vid_pad->pending_bufs) && !is_ife_hw_wm_fifo_full(vid_pad)) {
+		buf = list_first_entry(&vid_pad->pending_bufs,
+				       struct camss_buffer, queue);
+		list_del(&buf->queue);
+
+		__ife_hw_wm_qbuf(vid_pad, buf);
+
+		return true;
+	}
+
+	return false;
+}
+
+static bool __ife_hw_wm_queue_all_pending_bufs(struct ife_video_pad *vid_pad)
+{
+	bool buffers_queued = false;
+	bool next_buf_queued;
+
+	lockdep_assert_held(&vid_pad->ife->bufs_lock);
+
+	for ( ;; ) {
+		next_buf_queued = __ife_hw_wm_queue_next_pending_buf(vid_pad);
+		buffers_queued |= next_buf_queued;
+		if (!next_buf_queued || is_ife_hw_wm_fifo_full(vid_pad))
+			break;
+	}
+
+	return buffers_queued;
+}
+
+static void ife_hw_wm_queue_all_pending_bufs(struct ife_video_pad *vid_pad)
+{
+	struct ife_device *ife = vid_pad->ife;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ife->bufs_lock, flags);
+	__ife_hw_wm_queue_all_pending_bufs(vid_pad);
+	spin_unlock_irqrestore(&ife->bufs_lock, flags);
+}
+
+static void ife_hw_reg_update(struct ife_video_pad *vid_pad)
+{
+	const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+	const struct ife_hw_top_ops *top_ops = vid_pad->ife->res->top_ops;
+	struct ife_top_ctx *top_ctx = &vid_pad->ife->top_ctx;
+
+	top_ops->reg_update(top_ctx, dsc);
+}
+
+static void ife_hw_wm_add_pending_buf(struct ife_video_pad *vid_pad,
+				      struct camss_buffer *buf)
+{
+	struct ife_device *ife = vid_pad->ife;
+	bool buffers_queued = false;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ife->bufs_lock, flags);
+	INIT_LIST_HEAD(&buf->queue);
+	list_add_tail(&buf->queue, &vid_pad->pending_bufs);
+	if (vid_pad->streaming)
+		buffers_queued = __ife_hw_wm_queue_all_pending_bufs(vid_pad);
+	if (buffers_queued)
+		ife_hw_reg_update(vid_pad);
+	spin_unlock_irqrestore(&ife->bufs_lock, flags);
+}
+
+static void ife_hw_wm_flush_bufs(struct ife_video_pad *vid_pad,
+				 enum vb2_buffer_state state)
+{
+	struct ife_device *ife = vid_pad->ife;
+	struct camss_buffer *buf;
+	struct camss_buffer *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ife->bufs_lock, flags);
+
+	list_for_each_entry_safe(buf, tmp, &vid_pad->processing_bufs, queue) {
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
+		list_del(&buf->queue);
+	}
+
+	list_for_each_entry_safe(buf, tmp, &vid_pad->pending_bufs, queue) {
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
+		list_del(&buf->queue);
+	}
+
+	spin_unlock_irqrestore(&ife->bufs_lock, flags);
+}
+
+static void ife_hw_isr_reg_update(struct ife_video_pad *vid_pad)
+{
+	if (__ife_hw_wm_queue_all_pending_bufs(vid_pad))
+		ife_hw_reg_update(vid_pad);
+}
+
+static void ife_hw_isr_wm_done(struct ife_device *ife, struct ife_video_pad *vid_pad)
+{
+	const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+	u32 done_addr = ife->res->buswr_ops->consumed_addr(&ife->buswr_ctx, dsc->wm[0]);
+	struct camss_buffer *buf;
+	dma_addr_t buf_addr;
+
+	do {
+		buf = __ife_hw_wm_dqbuf(vid_pad);
+		if (!buf) {
+			dev_err_ratelimited(ife->camss->dev,
+					    "Ouch wm done without buffer! on video %s",
+					    dsc->video_name);
+			break;
+		}
+
+		buf_addr = buf->addr[0];
+
+		if (buf_addr == done_addr)
+			buf->vb.sequence = ife->camif_data[dsc->hw_pipe].next_seq - 1;
+		else
+			dev_err_ratelimited(ife->camss->dev,
+					    "Stall buffer found! %s  %x  %x  wm %d",
+					    dsc->video_name,
+					    (u32)buf_addr, done_addr, dsc->wm[0]);
+
+		buf->vb.vb2_buf.timestamp = ktime_get_ns();
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+	} while (buf_addr != done_addr);
+
+	if (__ife_hw_wm_queue_next_pending_buf(vid_pad))
+		ife_hw_reg_update(vid_pad);
+}
+
+static void ife_hw_isr_buswr(struct ife_device *ife, u32 bus_status)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&ife->bufs_lock, flags);
+
+	for (i = 0; i < ife->npads; i++) {
+		if (ife->pads[i].vid_pad && ife->pads[i].vid_pad->streaming) {
+			const struct ife_pad_desc *dsc = ife->pads[i].desc;
+			struct ife_video_pad *vpad = ife->pads[i].vid_pad;
+
+			if (bus_status & dsc->rup_group_mask)
+				ife_hw_isr_reg_update(vpad);
+
+			if (bus_status & dsc->comp_group_mask)
+				ife_hw_isr_wm_done(ife, vpad);
+		}
+	}
+
+	spin_unlock_irqrestore(&ife->bufs_lock, flags);
+}
+
+static irqreturn_t ife_hw_top_isr(int irq, void *dev)
+{
+	u32 status[IFE_TOP_STATUS_MAX] = {0};
+	struct ife_device *ife = dev;
+
+	ife->res->top_ops->irq_status(&ife->top_ctx, status);
+	if (status[IFE_TOP_STATUS_ID0] & IFE_HW_TOP_IRQ0_RST_DONE) {
+		complete(&ife->reset_complete);
+		return IRQ_HANDLED;
+	}
+
+	/* Handle first SOF, EPOCH and at the end bus IRQ's */
+	if (status[IFE_TOP_STATUS_ID1] & IFE_HW_TOP_IRQ1_RDI0_SOF)
+		ife->camif_data[CAMSS_IFE_HW_PIPE_RDI0].next_seq++;
+
+	if (status[IFE_TOP_STATUS_ID1] & IFE_HW_TOP_IRQ1_RDI1_SOF)
+		ife->camif_data[CAMSS_IFE_HW_PIPE_RDI1].next_seq++;
+
+	if (status[IFE_TOP_STATUS_ID1] & IFE_HW_TOP_IRQ1_RDI2_SOF)
+		ife->camif_data[CAMSS_IFE_HW_PIPE_RDI2].next_seq++;
+
+	if (status[IFE_TOP_STATUS_ID1] & IFE_HW_TOP_IRQ1_RDI3_SOF)
+		ife->camif_data[CAMSS_IFE_HW_PIPE_RDI3].next_seq++;
+
+	if (status[IFE_TOP_STATUS_ID1] & IFE_HW_TOP_IRQ1_PP_SOF) {
+		ife_pp_notify_sof_event(ife, ife->camif_data[CAMSS_IFE_HW_PIPE_PP].next_seq);
+		ife->camif_data[CAMSS_IFE_HW_PIPE_PP].next_seq++;
+	}
+
+	if (status[IFE_TOP_STATUS_ID0] & IFE_HW_TOP_IRQ0_BUSWR) {
+		u32 bus_status = ife->res->buswr_ops->irq_status(&ife->buswr_ctx);
+
+		ife_hw_isr_buswr(ife, bus_status);
+	}
+
+	if (status[IFE_TOP_STATUS_ID2]) {
+		dev_err_ratelimited(ife->camss->dev,
+				    "Violation on ife%d status 0x%x", ife->id,
+				    status[IFE_TOP_STATUS_ID2]);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void ife_del_pm_domain_link(struct ife_device *ife)
+{
+	struct camss *camss = ife->camss;
+
+	if (!camss->ife_genpd_link || ife->id >= camss->res->ife_num ||
+	    ife->id >= camss->genpd_num)
+		return;
+
+	device_link_del(camss->ife_genpd_link[ife->id]);
+}
+
+static int ife_add_pm_domain_link(struct ife_device *ife)
+{
+	struct camss *camss = ife->camss;
+
+	if (!camss->ife_genpd || ife->id >= camss->res->ife_num ||
+	    ife->id >= camss->genpd_num)
+		return 0;
+
+	camss->ife_genpd_link[ife->id] =
+		device_link_add(camss->dev, camss->ife_genpd[ife->id],
+				DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+				DL_FLAG_RPM_ACTIVE);
+	if (!camss->ife_genpd_link[ife->id])
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ife_set_clock_rates(struct ife_device *ife)
+{
+	struct camss *camss = ife->camss;
+	int ret, i;
+
+	for (i = 0; i < ife->nclocks; i++) {
+		struct camss_clock *clock = &ife->clock[i];
+		long round_rate;
+		u8 freq_idx;
+
+		if (!clock->nfreqs)
+			continue;
+
+		freq_idx = min_t(u8, camss->perf_mode, clock->nfreqs - 1);
+		round_rate = clk_round_rate(clock->clk, clock->freq[freq_idx]);
+		if (round_rate < 0) {
+			dev_err(camss->dev, "Clock round rate failed: %ld\n", round_rate);
+			return -EINVAL;
+		}
+
+		ret = clk_set_rate(clock->clk, round_rate);
+		if (ret < 0) {
+			dev_err(camss->dev, "Clock set rate failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int ife_clr_clock_rates(struct ife_device *ife)
+{
+	struct camss *camss = ife->camss;
+	int ret, i;
+
+	for (i = 0; i < ife->nclocks; i++) {
+		struct camss_clock *clock = &ife->clock[i];
+
+		if (!clock->nfreqs)
+			continue;
+
+		ret = clk_set_rate(clock->clk, clock->freq[0]);
+		if (ret < 0) {
+			dev_err(camss->dev, "Clock set rate failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+int ife_get(struct ife_device *ife)
+{
+	int ret;
+
+	ret = ife->camss->core_get(ife->camss, ife->id);
+	if (ret)
+		return ret;
+
+	ret = ife_set_clock_rates(ife);
+	if (ret)
+		return ret;
+
+	return camss_enable_clocks(ife->nclocks, ife->clock, ife->camss->dev);
+}
+
+void ife_put(struct ife_device *ife)
+{
+	camss_disable_clocks(ife->nclocks, ife->clock);
+	ife->camss->core_put(ife->camss);
+}
+
+static int ife_core_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	int ret;
+
+	if (on) {
+		ret = ife_get(ife);
+		if (ret < 0)
+			return ret;
+	} else {
+		ife_put(ife);
+		ife_clr_clock_rates(ife);
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops ife_core_ops = {
+	.s_power = ife_core_s_power,
+	.subscribe_event = ife_event_subscribe,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static int ife_setup_vid_full(struct ife_video_pad *vid_pad,
+			      struct v4l2_pix_format_mplane *pix_mp,
+			      struct camss_ife_wm_config *wm_cfg)
+{
+	const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+	const struct v4l2_format_info *info = v4l2_format_info(pix_mp->pixelformat);
+	u8 depth = 0;
+	int ret;
+
+	wm_cfg[Y_PLANE].mode_config = dsc->mode_config;
+	wm_cfg[Y_PLANE].packing_fmt = dsc->packing_fmt;
+	wm_cfg[C_PLANE].mode_config = dsc->mode_config;
+	wm_cfg[C_PLANE].packing_fmt = dsc->packing_fmt;
+
+	ret = camss_get_pixfmt_depth(pix_mp->pixelformat, Y_PLANE, &depth);
+	if (ret)
+		return ret;
+
+	wm_cfg[Y_PLANE].width	= pix_mp->width;
+	wm_cfg[Y_PLANE].height	= pix_mp->height;
+	wm_cfg[Y_PLANE].bytes	= wm_cfg[Y_PLANE].width * depth / BITS_PER_BYTE;
+	wm_cfg[Y_PLANE].stride	= pix_mp->plane_fmt[Y_PLANE].bytesperline;
+	wm_cfg[Y_PLANE].lines	= pix_mp->height;
+
+	ret = camss_get_pixfmt_depth(pix_mp->pixelformat, C_PLANE, &depth);
+	if (ret)
+		return ret;
+
+	wm_cfg[C_PLANE].width	= pix_mp->width / info->hdiv;
+	wm_cfg[C_PLANE].bytes	= wm_cfg[C_PLANE].width * depth / BITS_PER_BYTE;
+	wm_cfg[C_PLANE].stride	= pix_mp->plane_fmt[Y_PLANE].bytesperline;
+	wm_cfg[C_PLANE].height	= pix_mp->height  / info->vdiv;
+	wm_cfg[C_PLANE].lines	= wm_cfg[C_PLANE].height;
+
+	return 0;
+}
+
+static void ife_setup_vid_rdi(struct ife_video_pad *vid_pad,
+			      const struct camss_ife_pipeline_cfg *pipeline_cfg,
+			      struct camss_ife_wm_config *wm_cfg)
+{
+	const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+
+	wm_cfg[Y_PLANE].mode_config	= dsc->mode_config;
+	wm_cfg[Y_PLANE].packing_fmt	= dsc->packing_fmt;
+	wm_cfg[Y_PLANE].width		= pipeline_cfg->in_width;
+	wm_cfg[Y_PLANE].height		= pipeline_cfg->in_height;
+}
+
+static int ife_vid_pad_enable(struct ife_video_pad *vid_pad,
+			      const struct camss_ife_wm_config *wm_cfg,
+			      bool *first_ife_output)
+{
+	struct ife_device *ife = vid_pad->ife;
+	bool streaming_started = false;
+	int i;
+
+	mutex_lock(&ife->state_mutex);
+
+	if (!vid_pad->streaming) {
+		const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+		const struct ife_hw_buswr_ops *buswr_ops = ife->res->buswr_ops;
+
+		for (i = 0; i < dsc->num_wms; i++) {
+			buswr_ops->wm_config(&ife->buswr_ctx, dsc->wm[i], &wm_cfg[i]);
+			buswr_ops->wm_enable(&ife->buswr_ctx, dsc->wm[i]);
+		}
+
+		/* Enqueue all the buffers for processing on pending queue */
+		vid_pad->streaming = true;
+		ife->stream_count++;
+		*first_ife_output = ife->stream_count == 1;
+		streaming_started = true;
+	}
+
+	mutex_unlock(&ife->state_mutex);
+
+	if (streaming_started)
+		ife_hw_wm_queue_all_pending_bufs(vid_pad);
+
+	return 0;
+};
+
+static bool ife_vid_pad_disable(struct ife_video_pad *vid_pad,
+				bool *last_ife_output)
+{
+	struct ife_device *ife = vid_pad->ife;
+	bool output_stopped = false;
+	int i;
+
+	mutex_lock(&ife->state_mutex);
+
+	if (vid_pad->streaming) {
+		const struct ife_pad_desc *dsc = vid_pad->ife->pads[vid_pad->pad_index].desc;
+		const struct ife_hw_buswr_ops *buswr_ops = ife->res->buswr_ops;
+
+		for (i = 0; i < dsc->num_wms; i++)
+			buswr_ops->wm_disable(&ife->buswr_ctx, dsc->wm[i]);
+		/* Enqueue all the buffers for processing on pending queue */
+		vid_pad->streaming = false;
+		if (ife->stream_count)
+			ife->stream_count--;
+		*last_ife_output = !ife->stream_count;
+		output_stopped = true;
+	}
+
+	mutex_unlock(&ife->state_mutex);
+
+	return output_stopped;
+};
+
+static bool ife_hw_pipe_get(struct ife_device *ife, enum camss_ife_hw_pipe hw_pipe)
+{
+	bool first_output;
+
+	mutex_lock(&ife->state_mutex);
+	first_output = !ife->camif_data[hw_pipe].stream_count++;
+	mutex_unlock(&ife->state_mutex);
+
+	return first_output;
+}
+
+static bool ife_hw_pipe_put(struct ife_device *ife, enum camss_ife_hw_pipe hw_pipe)
+{
+	bool last_output = false;
+
+	mutex_lock(&ife->state_mutex);
+	if (ife->camif_data[hw_pipe].stream_count) {
+		ife->camif_data[hw_pipe].stream_count--;
+		last_output = !ife->camif_data[hw_pipe].stream_count;
+	}
+	mutex_unlock(&ife->state_mutex);
+
+	return last_output;
+}
+
+static int ife_hw_pipe_enable(struct ife_device *ife, const struct ife_pad_desc *dsc,
+			      const struct camss_ife_wm_config *wm_cfg, bool *first_output)
+{
+	const struct camss_ife_pipeline_cfg *pipeline_cfg = &ife->pipeline_cfg[dsc->hw_pipe];
+	const struct ife_hw_top_ops *top_ops = ife->res->top_ops;
+	int ret;
+
+	*first_output = ife_hw_pipe_get(ife, dsc->hw_pipe);
+
+	ret = top_ops->pipe_config(&ife->top_ctx, dsc, pipeline_cfg, wm_cfg);
+	if (ret)
+		goto error_pipe_put;
+
+	if (!*first_output)
+		return 0;
+
+	if (is_pix_hw_pipe(dsc->hw_pipe) && ife->res->mod_ops) {
+		const struct camss_pp_cfg pp_cfg = {
+			.width   = pipeline_cfg->in_width,
+			.height  = pipeline_cfg->in_height,
+			.in_code = pipeline_cfg->in_code,
+		};
+
+		ife->res->mod_ops->config(&ife->pp_ctx, &pp_cfg);
+	}
+
+	ret = top_ops->pipe_enable(&ife->top_ctx, dsc);
+	if (ret)
+		goto error_pipe_put;
+
+	if (is_pix_hw_pipe(dsc->hw_pipe) && ife->res->mod_ops)
+		ife->res->mod_ops->enable(&ife->pp_ctx);
+
+	ife->camif_data[dsc->hw_pipe].next_seq = 0;
+
+	return 0;
+
+error_pipe_put:
+	ife_hw_pipe_put(ife, dsc->hw_pipe);
+	return ret;
+}
+
+static void ife_hw_pipe_disable(struct ife_device *ife, const struct ife_pad_desc *dsc)
+{
+	if (is_pix_hw_pipe(dsc->hw_pipe) && ife->res->mod_ops)
+		ife->res->mod_ops->disable(&ife->pp_ctx);
+
+	ife->res->top_ops->pipe_disable(&ife->top_ctx, dsc);
+}
+
+static int ife_output_enable(struct ife_device *ife, struct ife_subdev_pads *pad,
+			     const struct camss_ife_wm_config *wm_cfg, bool first_pipe_output)
+{
+	bool first_stream = false;
+	int ret;
+
+	ret = ife_vid_pad_enable(pad->vid_pad, wm_cfg, &first_stream);
+	if (ret)
+		return ret;
+
+	if (first_stream) {
+		ife->res->buswr_ops->irq_enable(&ife->buswr_ctx);
+		ife->res->top_ops->prepare(&ife->top_ctx);
+	}
+
+	ife_hw_reg_update(pad->vid_pad);
+	if (first_pipe_output)
+		ife->res->top_ops->enable(&ife->top_ctx, pad->desc->hw_pipe);
+
+	return 0;
+}
+
+static void ife_stream_disable(struct ife_video_pad *vid_pad)
+{
+	struct ife_device *ife = vid_pad->ife;
+	const struct ife_pad_desc *dsc = ife->pads[vid_pad->pad_index].desc;
+	bool last_stream = false;
+
+	ife_vid_pad_disable(vid_pad, &last_stream);
+	ife_hw_pipe_disable(ife, dsc);
+	ife_hw_reg_update(vid_pad);
+	ife->res->top_ops->disable(&ife->top_ctx, dsc->hw_pipe);
+	if (last_stream)
+		ife->res->buswr_ops->irq_disable(&ife->buswr_ctx);
+}
+
+static bool is_ife_vid_pad_streaming(struct ife_video_pad *vid_pad)
+{
+	struct ife_device *ife = vid_pad->ife;
+	bool is_streaming;
+
+	mutex_lock(&ife->state_mutex);
+	is_streaming = vid_pad->streaming;
+	mutex_unlock(&ife->state_mutex);
+
+	return is_streaming;
+};
+
+static int ife_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	struct v4l2_subdev_krouting routing = { };
+	int i, ri;
+	int ret;
+
+	for (i = 0; i < ife->npads; i++) {
+		struct ife_video_pad *vpad = ife->pads[i].vid_pad;
+
+		if (vpad)
+			routing.num_routes++;
+	}
+
+	if (!routing.num_routes)
+		return 0;
+
+	routing.len_routes = routing.num_routes;
+	routing.routes = kcalloc(routing.num_routes, sizeof(*routing.routes), GFP_KERNEL);
+	if (!routing.routes)
+		return -ENOMEM;
+
+	for (i = 0, ri = 0; i < ife->npads; i++) {
+		struct ife_video_pad *vpad = ife->pads[i].vid_pad;
+		const struct ife_subdev_resources *res = ife->res;
+		struct v4l2_subdev_route *route;
+		u32 sid;
+
+		if (!vpad)
+			continue;
+
+		sid = camss_ife_stream_id_for_pipe(res->num_sink_streams, res->sink_streams,
+						   ife->pads[i].desc->hw_pipe);
+
+		route = &routing.routes[ri++];
+		route->sink_pad		= ife->sink_pad;
+		route->sink_stream	= sid;
+		route->source_pad	= vpad->pad_index;
+		route->source_stream	= 0;
+		route->flags		= V4L2_SUBDEV_ROUTE_FL_ACTIVE;
+	}
+
+	ret = v4l2_subdev_routing_validate(sd, &routing, V4L2_SUBDEV_ROUTING_NO_N_TO_1);
+	if (ret) {
+		dev_err(ife->camss->dev, "Fail to validate ife routing %d", ret);
+		goto out;
+	}
+
+	ret = v4l2_subdev_set_routing(sd, state, &routing);
+	if (ret) {
+		dev_err(ife->camss->dev, "Fail to set routing");
+		goto out;
+	}
+
+out:
+	kfree(routing.routes);
+
+	return ret;
+};
+
+static void ife_apply_constraints(u32 in_width, u32 in_height, u32 *out_width, u32 *out_height,
+				  const struct ife_pad_limits *limits)
+{
+	*out_width = clamp(*out_width, limits->min_width, limits->max_width);
+	if (limits->max_h_ratio)
+		*out_width = max(*out_width, in_width / limits->max_h_ratio);
+
+	*out_height = clamp(*out_height, limits->min_height, limits->max_height);
+	if (limits->max_v_ratio)
+		*out_height = max(*out_height, in_height / limits->max_v_ratio);
+}
+
+static inline void ife_apply_attributes(struct v4l2_mbus_framefmt *fmt, u32 field, u32 colorspace)
+{
+	fmt->field = field;
+	fmt->colorspace = colorspace;
+	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+	fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->colorspace, fmt->ycbcr_enc);
+	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+}
+
+static int ife_pad_try_format(struct ife_device *ife, const struct ife_pad_desc *desc,
+			      struct v4l2_mbus_framefmt *fmt)
+{
+	const struct camss_ife_pipeline_cfg *pipeline_cfg;
+	const struct camss_formats *sup_fmts = &desc->sup_fmts;
+	unsigned int i;
+	u32 in_height;
+	u32 in_width;
+
+	for (i = 0; i < sup_fmts->nformats; i++)
+		if (fmt->code == sup_fmts->formats[i].code)
+			break;
+
+	/* If not found set first format as default */
+	if (i >= sup_fmts->nformats)
+		i = 0;
+
+	fmt->code = sup_fmts->formats[i].code;
+
+	pipeline_cfg = &ife->pipeline_cfg[desc->hw_pipe];
+	in_width = desc->pad_flags & MEDIA_PAD_FL_SOURCE ? pipeline_cfg->in_width : fmt->width;
+	in_height = desc->pad_flags & MEDIA_PAD_FL_SOURCE ? pipeline_cfg->in_height : fmt->height;
+
+	ife_apply_constraints(in_width, in_height,
+			      &fmt->width, &fmt->height, desc->limits);
+
+	if (is_pix_hw_pipe(desc->hw_pipe)) {
+		const struct v4l2_format_info *info;
+		u32 min_height = desc->limits->min_height;
+		u32 min_width = desc->limits->min_width;
+
+		info = v4l2_format_info(sup_fmts->formats[i].pixelformat);
+		if (info) {
+			if (desc->limits->max_h_ratio)
+				min_width = max(min_width,
+						DIV_ROUND_UP(in_width,
+							     desc->limits->max_h_ratio));
+			if (desc->limits->max_v_ratio)
+				min_height = max(min_height,
+						 DIV_ROUND_UP(in_height,
+							      desc->limits->max_v_ratio));
+
+			min_width = roundup(min_width, info->hdiv);
+			min_height = roundup(min_height, info->vdiv);
+			fmt->width = max(round_down(fmt->width, info->hdiv), min_width);
+			fmt->height = max(round_down(fmt->height, info->vdiv), min_height);
+		}
+	}
+
+	ife_apply_attributes(fmt, V4L2_FIELD_NONE, V4L2_COLORSPACE_SRGB);
+
+	return i;
+}
+
+static int ife_pad_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+			   struct v4l2_subdev_format *sd_fmt)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	struct ife_subdev_pads *pad;
+	enum camss_ife_hw_pipe pipe;
+
+	if (sd_fmt->pad >= ife->npads)
+		return -EINVAL;
+
+	memset(&sd_fmt->reserved, 0, sizeof(sd_fmt->reserved));
+
+	pad = &ife->pads[sd_fmt->pad];
+
+	if (sd_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		ife_pad_try_format(ife, pad->desc, &sd_fmt->format);
+	} else {
+		ife_apply_attributes(&pad->fmt, V4L2_FIELD_NONE, V4L2_COLORSPACE_SRGB);
+		sd_fmt->format = pad->fmt;
+
+		if (sd_fmt->pad == ife->sink_pad && ife->res->num_sink_streams) {
+			pipe = camss_ife_pipe_for_stream_id(ife->res->num_sink_streams,
+							    ife->res->sink_streams,
+							     sd_fmt->stream);
+			if (pipe == CAMSS_IFE_HW_PIPE_INVALID)
+				return -EINVAL;
+
+			sd_fmt->format.code = ife->pipeline_cfg[pipe].in_code;
+			sd_fmt->format.width = ife->pipeline_cfg[pipe].in_width;
+			sd_fmt->format.height = ife->pipeline_cfg[pipe].in_height;
+		}
+	}
+
+	memset(&sd_fmt->reserved, 0, sizeof(sd_fmt->reserved));
+
+	return 0;
+}
+
+static void ife_update_src_pads_fmt(struct ife_device *ife, struct v4l2_mbus_framefmt *sink_fmt,
+				    enum camss_ife_hw_pipe pipe)
+{
+	int i;
+
+	for (i = 0; i < ife->npads; i++) {
+		struct ife_video_pad *vpad = ife->pads[i].vid_pad;
+		struct v4l2_mbus_framefmt new_fmt = *sink_fmt;
+		struct ife_subdev_pads *pad = &ife->pads[i];
+		int fi;
+
+		if (!(pad->desc->pad_flags & MEDIA_PAD_FL_SOURCE) || is_meta_type(pad->desc))
+			continue;
+
+		if (pipe != CAMSS_IFE_HW_PIPE_INVALID && pad->desc->hw_pipe != pipe)
+			continue;
+
+		fi = ife_pad_try_format(ife, pad->desc, &new_fmt);
+
+		if (vpad) {
+			struct v4l2_pix_format_mplane *pix_mp = &vpad->video.active_fmt.fmt.pix_mp;
+			const struct camss_format_info *info = &pad->desc->sup_fmts.formats[fi];
+
+			/* Update video pad V4L2 format */
+			v4l2_fill_pixfmt_mp(pix_mp, info->pixelformat, new_fmt.width,
+					    new_fmt.height);
+		}
+
+		/* Update source pad format with modified format */
+		pad->fmt = new_fmt;
+	}
+}
+
+static int ife_pad_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+			   struct v4l2_subdev_format *sd_fmt)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	struct ife_subdev_pads *pad;
+	enum camss_ife_hw_pipe pipe;
+	unsigned int i;
+
+	if (sd_fmt->pad >= ife->npads)
+		return -EINVAL;
+
+	memset(&sd_fmt->reserved, 0, sizeof(sd_fmt->reserved));
+
+	pad = &ife->pads[sd_fmt->pad];
+
+	ife_pad_try_format(ife, pad->desc, &sd_fmt->format);
+
+	if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		/* Update current pad format */
+		pad->fmt = sd_fmt->format;
+		/* If it is a sink pad, update the corresponding source formats. */
+		if (ife->mpads[sd_fmt->pad].flags == MEDIA_PAD_FL_SINK) {
+			if (!ife->res->num_sink_streams) {
+				for (i = 0; i < CAMSS_IFE_HW_PIPE_INVALID; i++) {
+					ife->pipeline_cfg[i].in_code = sd_fmt->format.code;
+					ife->pipeline_cfg[i].in_width = sd_fmt->format.width;
+					ife->pipeline_cfg[i].in_height = sd_fmt->format.height;
+				}
+				ife_update_src_pads_fmt(ife, &sd_fmt->format,
+							CAMSS_IFE_HW_PIPE_INVALID);
+			} else {
+				pipe = camss_ife_pipe_for_stream_id(ife->res->num_sink_streams,
+								    ife->res->sink_streams,
+								     sd_fmt->stream);
+				if (pipe == CAMSS_IFE_HW_PIPE_INVALID)
+					return -EINVAL;
+
+				ife->pipeline_cfg[pipe].in_code = sd_fmt->format.code;
+				ife->pipeline_cfg[pipe].in_width = sd_fmt->format.width;
+				ife->pipeline_cfg[pipe].in_height = sd_fmt->format.height;
+				ife_update_src_pads_fmt(ife, &sd_fmt->format, pipe);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int ife_pad_mbus_enum_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	struct ife_subdev_pads *pad;
+
+	if (code->pad >= ife->npads)
+		return -EINVAL;
+
+	memset(&code->reserved, 0, sizeof(code->reserved));
+
+	pad = &ife->pads[code->pad];
+
+	if (pad->desc->pad_flags == MEDIA_PAD_FL_SINK ||
+	    pad->desc->hw_pipe == CAMSS_IFE_HW_PIPE_PP) {
+		if (code->index >= pad->desc->sup_fmts.nformats)
+			return -EINVAL;
+
+		code->code = pad->desc->sup_fmts.formats[code->index].code;
+	} else {
+		/*
+		 * RDI pipeline does not have capabilities for format
+		 * conversions. It always follows the sink pad format.
+		 */
+		if (code->index > 0)
+			return -EINVAL;
+
+		code->code = ife->pipeline_cfg[pad->desc->hw_pipe].in_code;
+	}
+
+	return 0;
+}
+
+static int ife_pad_enum_framesizes(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	struct ife_subdev_pads *pad;
+	int i;
+
+	if (fse->pad >= ife->npads)
+		return -EINVAL;
+
+	if (fse->index != 0)
+		return -EINVAL;
+
+	pad = &ife->pads[fse->pad];
+
+	for (i = 0; i < pad->desc->sup_fmts.nformats; i++) {
+		if (pad->desc->sup_fmts.formats[i].code == fse->code)
+			break;
+	}
+	if (i >= pad->desc->sup_fmts.nformats)
+		return -EINVAL;
+
+	fse->max_width	= pad->desc->limits->max_width;
+	fse->max_height	= pad->desc->limits->max_height;
+
+	if (pad->desc->pad_flags == MEDIA_PAD_FL_SINK) {
+		fse->min_width	= pad->desc->limits->min_width;
+		fse->min_height	= pad->desc->limits->min_height;
+	} else {
+		const struct camss_ife_pipeline_cfg *pipeline_cfg;
+
+		pipeline_cfg = &ife->pipeline_cfg[pad->desc->hw_pipe];
+
+		/* Ife does not support upscaling. */
+		if (pipeline_cfg->in_width < fse->max_width)
+			fse->max_width = pipeline_cfg->in_width;
+
+		if (pipeline_cfg->in_height < fse->max_height)
+			fse->max_height = pipeline_cfg->in_height;
+
+		ife_apply_constraints(fse->max_width, fse->max_height,
+				      &fse->min_width, &fse->min_height, pad->desc->limits);
+	}
+
+	return 0;
+}
+
+static int ife_output_configure(struct ife_device *ife, struct ife_subdev_pads *pad,
+				struct camss_ife_wm_config *wm_cfg)
+{
+	const struct camss_ife_pipeline_cfg *pipeline_cfg;
+
+	pipeline_cfg = &ife->pipeline_cfg[pad->desc->hw_pipe];
+	wm_cfg[Y_PLANE].wm_id = pad->desc->wm[Y_PLANE];
+	wm_cfg[C_PLANE].wm_id = pad->desc->wm[C_PLANE];
+
+	if (is_rdi_hw_pipe(pad->desc->hw_pipe)) {
+		ife_setup_vid_rdi(pad->vid_pad, pipeline_cfg, wm_cfg);
+		return 0;
+	}
+
+	if (is_pix_hw_pipe(pad->desc->hw_pipe))
+		return ife_setup_vid_full(pad->vid_pad, &pad->vid_pad->video.active_fmt.fmt.pix_mp,
+					  wm_cfg);
+
+	return 0;
+}
+
+static int ife_enable_remote_streams(struct ife_device *ife, struct v4l2_subdev_state *state,
+				     struct media_pad *remote_pad, u32 pad, u64 streams_mask)
+{
+	u64 sink_streams;
+
+	sink_streams = v4l2_subdev_state_xlate_streams(state, pad, ife->sink_pad, &streams_mask);
+
+	return v4l2_subdev_enable_streams(media_entity_to_v4l2_subdev(remote_pad->entity),
+					  remote_pad->index, sink_streams);
+}
+
+static int ife_pad_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+				  u32 pad_index, u64 streams_mask)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	struct camss_ife_wm_config wm_cfg[IFE_HW_PLANES] = {0};
+	struct media_pad *remote_pad;
+	struct ife_subdev_pads *pad;
+	bool first_pipe_output;
+	int ret;
+
+	if (pad_index >= ife->npads)
+		return -EINVAL;
+
+	pad = &ife->pads[pad_index];
+
+	if (ife->mpads[pad_index].flags == MEDIA_PAD_FL_SINK)
+		return -EOPNOTSUPP;
+
+	remote_pad = media_pad_remote_pad_first(&ife->mpads[ife->sink_pad]);
+	if (!remote_pad)
+		return -ENOLINK;
+
+	if (!pad->vid_pad) {
+		dev_err(ife->camss->dev, "Invalid video node for source pad[%d]\n",
+			pad_index);
+		return -EIO;
+	}
+
+	ret = ife_output_configure(ife, pad, wm_cfg);
+	if (ret)
+		return ret;
+
+	ret = ife_hw_pipe_enable(ife, pad->desc, wm_cfg, &first_pipe_output);
+	if (ret)
+		return ret;
+
+	ret = ife_output_enable(ife, pad, wm_cfg, first_pipe_output);
+	if (ret)
+		goto error_pipe_disable;
+
+	if (first_pipe_output) {
+		ret = ife_enable_remote_streams(ife, state, remote_pad, pad_index, streams_mask);
+		if (ret)
+			goto error_stream_disable;
+	}
+
+	return 0;
+
+error_stream_disable:
+	ife_stream_disable(pad->vid_pad);
+	goto error_pipe_put;
+error_pipe_disable:
+	if (first_pipe_output)
+		ife_hw_pipe_disable(ife, pad->desc);
+error_pipe_put:
+	ife_hw_pipe_put(ife, pad->desc->hw_pipe);
+	return ret;
+}
+
+static int ife_pad_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+				   u32 pad_index, u64 streams_mask)
+{
+	struct ife_device *ife = v4l2_get_subdevdata(sd);
+	const struct ife_hw_buswr_ops *buswr_ops = ife->res->buswr_ops;
+	const struct ife_hw_top_ops *top_ops = ife->res->top_ops;
+	struct media_pad *remote_pad;
+	struct ife_subdev_pads *pad;
+	bool last_pipe_output;
+	bool last_stream = false;
+	u64 sink_streams;
+	int ret = 0;
+
+	if (pad_index >= ife->npads)
+		return -EINVAL;
+
+	pad = &ife->pads[pad_index];
+
+	if (ife->mpads[pad_index].flags == MEDIA_PAD_FL_SINK)
+		return -EOPNOTSUPP;
+
+	remote_pad = media_pad_remote_pad_first(&ife->mpads[ife->sink_pad]);
+	sink_streams = v4l2_subdev_state_xlate_streams(state, pad_index,
+						       ife->sink_pad, &streams_mask);
+
+	if (!pad->vid_pad || !ife_vid_pad_disable(pad->vid_pad, &last_stream))
+		return 0;
+
+	last_pipe_output = ife_hw_pipe_put(ife, pad->desc->hw_pipe);
+	if (last_pipe_output && remote_pad) {
+		ret = v4l2_subdev_disable_streams(media_entity_to_v4l2_subdev(remote_pad->entity),
+						  remote_pad->index, sink_streams);
+		if (ret)
+			dev_err(ife->camss->dev,
+				"Failed to disable stream on remote pad: %d\n", ret);
+	}
+
+	if (last_pipe_output) {
+		ife_hw_pipe_disable(ife, pad->desc);
+		top_ops->disable(&ife->top_ctx, pad->desc->hw_pipe);
+	}
+	if (last_stream)
+		buswr_ops->irq_disable(&ife->buswr_ctx);
+
+	return ret;
+}
+
+static const struct v4l2_subdev_pad_ops ife_pad_ops = {
+	.enum_mbus_code	 = ife_pad_mbus_enum_code,
+	.enum_frame_size = ife_pad_enum_framesizes,
+	.get_fmt	 = ife_pad_get_fmt,
+	.set_fmt	 = ife_pad_set_fmt,
+	.enable_streams	 = ife_pad_enable_streams,
+	.disable_streams = ife_pad_disable_streams,
+};
+
+static const struct v4l2_subdev_internal_ops ife_v4l2_internal_ops = {
+	.init_state = ife_init_state,
+};
+
+static const struct media_entity_operations ife_media_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_ops ife_v4l2_ops = {
+	.core	= &ife_core_ops,
+	.pad	= &ife_pad_ops,
+};
+
+static int ife_video_start_streaming(struct camss_video *vid,
+				     unsigned int count)
+{
+	struct media_pad *src_pad;
+
+	src_pad = media_pad_remote_pad_first(&vid->vdev.entity.pads[0]);
+	if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity))
+		return -ENOLINK;
+
+	return v4l2_subdev_enable_streams(media_entity_to_v4l2_subdev(src_pad->entity),
+					  src_pad->index, BIT_ULL(0));
+}
+
+static void ife_video_stop_streaming(struct camss_video *vid)
+{
+	struct ife_video_pad *vid_pad =
+		container_of(vid, struct ife_video_pad, video);
+	struct media_pad *src_pad;
+	int ret;
+
+	src_pad = media_pad_remote_pad_first(&vid->vdev.entity.pads[0]);
+	if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity))
+		return;
+
+	ret = v4l2_subdev_disable_streams(media_entity_to_v4l2_subdev(src_pad->entity),
+					  src_pad->index, BIT_ULL(0));
+	if (ret)
+		dev_err(vid_pad->ife->camss->dev,
+			"Video pipeline disable streams failed: %d\n", ret);
+}
+
+static int ife_queue_buffer(struct camss_video *vid, struct camss_buffer *buf)
+{
+	struct ife_video_pad *vid_pad =
+		container_of(vid, struct ife_video_pad, video);
+
+	ife_hw_wm_add_pending_buf(vid_pad, buf);
+
+	return 0;
+}
+
+static int ife_flush_buffers(struct camss_video *vid, enum vb2_buffer_state state)
+{
+	struct ife_video_pad *vid_pad =
+		container_of(vid, struct ife_video_pad, video);
+
+	if (is_ife_vid_pad_streaming(vid_pad))
+		return -EINVAL;
+
+	ife_hw_wm_flush_bufs(vid_pad, state);
+
+	return 0;
+}
+
+static const struct camss_video_ops camss_video_ops = {
+	.start_streaming = ife_video_start_streaming,
+	.stop_streaming	= ife_video_stop_streaming,
+	.queue_buffer	= ife_queue_buffer,
+	.flush_buffers	= ife_flush_buffers,
+};
+
+static int ife_init_pad_indices(struct ife_device *ife)
+{
+	const struct ife_pads_descriptors *pads = ife->res->pads;
+	bool found_extra_sink = false;
+	unsigned int i;
+
+	if (pads->num_desc < 2) {
+		dev_err(ife->camss->dev, "IFE resources define too few pads\n");
+		return -EINVAL;
+	}
+
+	if (!(pads->desc[IFE_IMAGE_SINK_PAD].pad_flags & MEDIA_PAD_FL_SINK)) {
+		dev_err(ife->camss->dev, "IFE resources define no image sink pad\n");
+		return -EINVAL;
+	}
+
+	if (!(pads->desc[IFE_FIRST_SOURCE_PAD].pad_flags & MEDIA_PAD_FL_SOURCE)) {
+		dev_err(ife->camss->dev, "IFE resources define no source pad after image sink\n");
+		return -EINVAL;
+	}
+
+	for (i = IFE_FIRST_SOURCE_PAD; i < pads->num_desc; i++) {
+		u16 flags = pads->desc[i].pad_flags;
+
+		if (flags & MEDIA_PAD_FL_SINK) {
+			found_extra_sink = true;
+			continue;
+		}
+
+		if (!(flags & MEDIA_PAD_FL_SOURCE)) {
+			dev_err(ife->camss->dev, "IFE resources define invalid pad %u\n", i);
+			return -EINVAL;
+		}
+
+		if (found_extra_sink) {
+			dev_err(ife->camss->dev,
+				"IFE resources define source pad %u after an extra sink pad\n", i);
+			return -EINVAL;
+		}
+	}
+
+	ife->sink_pad = IFE_IMAGE_SINK_PAD;
+
+	return 0;
+}
+
+static int camss_ife_first_source_pad(struct media_entity *entity)
+{
+	if (entity->num_pads < 2)
+		return -EINVAL;
+
+	if (!(entity->pads[IFE_IMAGE_SINK_PAD].flags & MEDIA_PAD_FL_SINK))
+		return -EINVAL;
+
+	if (!(entity->pads[IFE_FIRST_SOURCE_PAD].flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	return IFE_FIRST_SOURCE_PAD;
+}
+
+int camss_ife_subdev_init(struct camss *camss, struct ife_device *ife,
+			  const struct camss_subdev_resources *res, u8 id,
+			  void __iomem *csid_base)
+{
+	struct device *dev = camss->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	void __iomem *base;
+	int i, j;
+	int rc;
+
+	if (!camss->ife_total_num || id >= camss->ife_total_num)
+		return -EINVAL;
+
+	ife->res = &res->ife;
+	ife->camss = camss;
+	ife->id = id;
+
+	rc = ife_init_pad_indices(ife);
+	if (rc)
+		return rc;
+
+	init_completion(&ife->reset_complete);
+	mutex_init(&ife->state_mutex);
+	spin_lock_init(&ife->bufs_lock);
+
+	/* Memory */
+	base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
+	if (IS_ERR(base)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(base);
+	}
+
+	/*
+	 * Initialise per-engine contexts. Each ops backend stores
+	 * the MMIO base and any additional state it needs.
+	 */
+	res->ife.buswr_ops->acquire(ife, base);
+	rc = res->ife.top_ops->acquire(ife, base, csid_base);
+	if (rc)
+		return rc;
+	if (ife->res->type == CAMSS_IFE_SUBDEV_TYPE_IFE)
+		res->ife.mod_ops->acquire(ife, &ife->pp_ctx, base);
+
+	/* Interrupt */
+	rc = platform_get_irq_byname(pdev, res->interrupt[0]);
+	if (rc < 0)
+		return rc;
+
+	ife->irq = rc;
+	snprintf(ife->irq_name, sizeof(ife->irq_name), "%s_%s%d", dev_name(dev),
+		 CAMSS_IFE_IRQ_NAME, id);
+	rc = devm_request_irq(dev, ife->irq, ife_hw_top_isr,
+			      IRQF_TRIGGER_RISING, ife->irq_name, ife);
+	if (rc) {
+		dev_err(dev, "request_irq failed: %d\n", rc);
+		return rc;
+	}
+
+	/* Clocks */
+	ife->nclocks = 0;
+	while (res->clock[ife->nclocks])
+		ife->nclocks++;
+
+	ife->clock = devm_kcalloc(dev, ife->nclocks, sizeof(*ife->clock),
+				  GFP_KERNEL);
+	if (!ife->clock)
+		return -ENOMEM;
+
+	for (i = 0; i < ife->nclocks; i++) {
+		struct camss_clock *clock = &ife->clock[i];
+
+		clock->clk = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(clock->clk))
+			return PTR_ERR(clock->clk);
+
+		clock->name = res->clock[i];
+
+		clock->nfreqs = 0;
+		while (res->clock_rate[i][clock->nfreqs])
+			clock->nfreqs++;
+
+		if (!clock->nfreqs) {
+			clock->freq = NULL;
+			continue;
+		}
+
+		clock->freq = devm_kcalloc(dev, clock->nfreqs,
+					   sizeof(*clock->freq), GFP_KERNEL);
+		if (!clock->freq)
+			return -ENOMEM;
+
+		for (j = 0; j < clock->nfreqs; j++)
+			clock->freq[j] = res->clock_rate[i][j];
+	}
+
+	return 0;
+}
+
+int camss_ife_link_entities(struct camss *camss)
+{
+	int ife_id, csid_id;
+	int rc;
+
+	for (ife_id = 0; ife_id < camss->res->csiphy_num; ife_id++) {
+		for (csid_id = 0; csid_id < camss->res->csid_num; csid_id++) {
+			rc = media_create_pad_link(&camss->csiphy[ife_id].subdev.entity,
+						   MSM_CSIPHY_PAD_SRC,
+						   &camss->csid[csid_id].subdev.entity,
+						   MSM_CSID_PAD_SINK, 0);
+			if (rc) {
+				dev_err(camss->dev,
+					"Failed to link %s->%s entities: %d\n",
+					camss->csiphy[ife_id].subdev.entity.name,
+					camss->csid[csid_id].subdev.entity.name,
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	for (ife_id = 0; ife_id < camss->ife_total_num; ife_id++) {
+		struct v4l2_subdev *csid = &camss->csid[ife_id].subdev;
+		struct ife_device *ife = &camss->ife[ife_id];
+		struct v4l2_subdev *sd = &ife->sd;
+		int src_pad;
+
+		src_pad = camss_ife_first_source_pad(&csid->entity);
+		if (src_pad < 0) {
+			dev_err(camss->dev, "%s does not expose sink pad 0 and source pad 1\n",
+				csid->entity.name);
+			return src_pad;
+		}
+
+		rc = media_create_pad_link(&csid->entity, src_pad, &sd->entity,
+					   ife->sink_pad, 0);
+		if (rc) {
+			dev_err(camss->dev, "Failed to link %s -> %s entities: %d\n",
+				csid->entity.name, sd->entity.name, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+int ife_register_entities(struct ife_device *ife, struct v4l2_device *v4l2_dev)
+{
+	struct v4l2_subdev *sd = &ife->sd;
+	struct camss *camss = ife->camss;
+	struct device *dev = camss->dev;
+	int ret;
+	int i;
+
+	ife->npads = ife->res->pads->num_desc;
+
+	ife->pads = devm_kcalloc(dev, ife->npads, sizeof(*ife->pads), GFP_KERNEL);
+	if (!ife->pads)
+		return -ENOMEM;
+
+	ife->mpads = devm_kcalloc(dev, ife->npads, sizeof(*ife->mpads), GFP_KERNEL);
+	if (!ife->mpads)
+		return -ENOMEM;
+
+	v4l2_subdev_init(sd, &ife_v4l2_ops);
+	sd->internal_ops = &ife_v4l2_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS |
+		     V4L2_SUBDEV_FL_STREAMS;
+
+	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+	sd->entity.ops = &ife_media_ops;
+	snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", CAMSS_IFE_PREFIX, ife->id);
+
+	v4l2_set_subdevdata(sd, ife);
+
+	for (i = 0; i < ife->npads; i++) {
+		const struct ife_pad_desc *dsc = &ife->res->pads->desc[i];
+		struct ife_subdev_pads *pad = &ife->pads[i];
+
+		pad->desc = dsc;
+		ife->mpads[i].flags = dsc->pad_flags;
+
+		pad->fmt.code = dsc->sup_fmts.formats->code;
+		if (!dsc->limits) {
+			dev_err(dev, "Missing pad[%d] constraints\n", i);
+			return -EINVAL;
+		}
+
+		pad->fmt.width	= dsc->limits->max_width;
+		pad->fmt.height	= dsc->limits->max_height;
+
+		if (dsc->video_name) {
+			char name[sizeof(((struct camss_video *)0)->vdev.name)];
+
+			pad->vid_pad = devm_kzalloc(dev, sizeof(*pad->vid_pad), GFP_KERNEL);
+			if (!pad->vid_pad)
+				goto err_msm_video_unregister;
+
+			pad->vid_pad->ife = ife;
+			pad->vid_pad->pad_index = i;
+			pad->vid_pad->streaming = false;
+
+			INIT_LIST_HEAD(&pad->vid_pad->pending_bufs);
+			INIT_LIST_HEAD(&pad->vid_pad->processing_bufs);
+
+			pad->vid_pad->video.camss	= camss;
+			pad->vid_pad->video.nformats	= dsc->sup_fmts.nformats;
+			pad->vid_pad->video.formats	= dsc->sup_fmts.formats;
+			pad->vid_pad->video.type	= dsc->buf_type;
+			pad->vid_pad->video.ops		= &camss_video_ops;
+			pad->vid_pad->video.bpl_alignment = 16;
+
+			if (dsc->mode_config == BUSWR_CLIENT_CFG_MODE_LINE_BASED)
+				pad->vid_pad->video.line_based = 1;
+			else
+				pad->vid_pad->video.line_based = 0;
+
+			snprintf(name, ARRAY_SIZE(name), "%s_%s", ife->sd.name, dsc->video_name);
+			/* Register video node */
+			ret = msm_video_register(&pad->vid_pad->video, v4l2_dev, name);
+			if (ret) {
+				dev_err(dev, "Failed to register video: %d\n", ret);
+				goto err_msm_video_unregister;
+			}
+		}
+	}
+
+	for (i = 0; i < CAMSS_IFE_HW_PIPE_INVALID; i++) {
+		ife->pipeline_cfg[i].in_code = ife->pads[ife->sink_pad].fmt.code;
+		ife->pipeline_cfg[i].in_width = ife->pads[ife->sink_pad].fmt.width;
+		ife->pipeline_cfg[i].in_height = ife->pads[ife->sink_pad].fmt.height;
+	}
+
+	ret = media_entity_pads_init(&sd->entity, ife->npads, ife->mpads);
+	if (ret) {
+		dev_err(dev, "Failed to init media entity: %d\n", ret);
+		goto err_msm_video_unregister;
+	}
+
+	ret = v4l2_subdev_init_finalize(sd);
+	if (ret) {
+		dev_err(dev, "Failed to finalize subdev: %d\n", ret);
+		goto err_media_entity_cleanup;
+	}
+
+	ret = v4l2_device_register_subdev(v4l2_dev, sd);
+	if (ret) {
+		dev_err(dev, "Failed to register subdev: %d\n", ret);
+		goto err_v4l2_subdev_cleanup;
+	}
+
+	/* Link video nodes to subdevice */
+	for (i = 0; i < ife->npads; i++) {
+		struct ife_subdev_pads *pad = &ife->pads[i];
+		struct ife_video_pad *vpad = pad->vid_pad;
+
+		if (!vpad)
+			continue;
+
+		ret = media_create_pad_link(&sd->entity, vpad->pad_index,
+					    &vpad->video.vdev.entity, 0,
+					    pad->desc->link_flags);
+		if (ret < 0) {
+			dev_err(dev, "Failed to link '%s' and '%s' entities\n", sd->entity.name,
+				vpad->video.vdev.entity.name);
+			goto err_media_entity_remove_links;
+		}
+	}
+
+	ret = ife_add_pm_domain_link(ife);
+	if (ret)
+		goto err_media_entity_remove_links;
+
+	return 0;
+
+err_media_entity_remove_links:
+	media_entity_remove_links(&sd->entity);
+	v4l2_device_unregister_subdev(sd);
+err_v4l2_subdev_cleanup:
+	v4l2_subdev_cleanup(sd);
+err_media_entity_cleanup:
+	media_entity_cleanup(&sd->entity);
+err_msm_video_unregister:
+	for (i = 0; i < ife->npads; i++) {
+		if (ife->pads[i].vid_pad)
+			msm_video_unregister(&ife->pads[i].vid_pad->video);
+	}
+
+	return ret;
+}
+
+void ife_unregister_entities(struct ife_device *ife)
+{
+	int i;
+
+	ife_del_pm_domain_link(ife);
+
+	mutex_destroy(&ife->state_mutex);
+
+	for (i = 0; i < ife->npads; i++) {
+		if (ife->pads[i].vid_pad)
+			msm_video_unregister(&ife->pads[i].vid_pad->video);
+	}
+
+	media_entity_remove_links(&ife->sd.entity);
+	media_entity_cleanup(&ife->sd.entity);
+	v4l2_device_unregister_subdev(&ife->sd);
+}
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 1f63a32809ed..65582ff7db23 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -11,6 +11,7 @@
 #define QC_MSM_CAMSS_H
 
 #include <linux/device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/types.h>
 #include <media/v4l2-async.h>
 #include <media/v4l2-device.h>
@@ -20,6 +21,7 @@
 
 #include "camss-csid.h"
 #include "camss-csiphy.h"
+#include "camss-ife.h"
 #include "camss-ispif.h"
 #include "camss-tpg.h"
 #include "camss-vfe.h"
@@ -44,6 +46,28 @@
 #define CAMSS_RES_MAX 17
 #define CAMSS_INIT_BUF_COUNT 2
 
+/* Per-subdev host private data attached via v4l2_set_subdev_hostdata() */
+struct camss_host_priv_data {
+	bool is_sensor;
+	u32 id;
+};
+
+/*
+ * The Camera SoC has four fixed levels of performance,
+ * and each level defines clocks and power constraints.
+ * These levels are used to set frequencies and bandwidths
+ * without the need for calculation or adjustments for
+ * each engine.
+ */
+enum camss_perf_mode {
+	CAMSS_PERF_MODE_MINSVS = 0,
+	CAMSS_PERF_MODE_LOWSVS,
+	CAMSS_PERF_MODE_SVS,
+	CAMSS_PERF_MODE_SVS_L1,
+	CAMSS_PERF_MODE_NOM,
+	CAMSS_PERF_MAX_MODES
+};
+
 struct camss_subdev_resources {
 	struct regulator_bulk_data regulators[CAMSS_RES_MAX];
 	char *clock[CAMSS_RES_MAX];
@@ -56,6 +80,7 @@ struct camss_subdev_resources {
 		struct tpg_subdev_resources tpg;
 		struct csid_subdev_resources csid;
 		struct vfe_subdev_resources vfe;
+		struct ife_subdev_resources ife;
 	};
 };
 
@@ -113,6 +138,7 @@ struct camss_resources {
 	const struct camss_subdev_resources *ispif_res;
 	const struct camss_subdev_resources *vfe_res;
 	const struct resources_wrapper *csid_wrapper_res;
+	const struct camss_subdev_resources *ife_res;
 	const struct resources_icc *icc_res;
 	const unsigned int icc_path_num;
 	const unsigned int csiphy_num;
@@ -120,6 +146,10 @@ struct camss_resources {
 	const unsigned int csid_num;
 	const unsigned int vfe_num;
 	const bool csid_vfe_fixed_pairing;
+	const bool embedded_csid;
+	const unsigned int ife_num;
+	const unsigned int ife_lite_num;
+	int (*link_entities)(struct camss *camss);
 };
 
 struct camss {
@@ -133,12 +163,21 @@ struct camss {
 	struct ispif_device *ispif;
 	struct vfe_device *vfe;
 	void __iomem *csid_wrapper_base;
+	struct ife_device *ife;
+	enum camss_perf_mode perf_mode;
+	unsigned int ife_total_num;
 	atomic_t ref_count;
 	int genpd_num;
 	struct device *genpd;
 	struct device_link *genpd_link;
+	struct device **ife_genpd;
+	struct device_link **ife_genpd_link;
 	struct icc_path *icc_path[ICC_SM8250_COUNT];
 	const struct camss_resources *res;
+
+	int (*core_get)(struct camss *camss, u8 entity_id);
+	int (*core_put)(struct camss *camss);
+	atomic_t perf_ref_count;
 };
 
 struct camss_camera_interface {
@@ -171,6 +210,8 @@ void camss_disable_clocks(int nclocks, struct camss_clock *clock);
 struct media_pad *camss_find_sensor_pad(struct media_entity *entity);
 s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
 			unsigned int lanes);
+int camss_get_pixel_clock_bpp(struct media_entity *entity, unsigned int bpp,
+			      u64 *pixel_clock);
 int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock);
 int camss_pm_domain_on(struct camss *camss, int id);
 void camss_pm_domain_off(struct camss *camss, int id);
@@ -181,4 +222,9 @@ void camss_buf_done(struct camss *camss, int hw_id, int port_id);
 void camss_reg_update(struct camss *camss, int hw_id,
 		      int port_id, bool is_clear);
 
+int camss_attach_host_data(struct camss *camss, struct v4l2_subdev *sd,
+			   bool is_sensor, int id);
+
+void camss_delete(struct camss *camss);
+
 #endif /* QC_MSM_CAMSS_H */
-- 
2.34.1


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

* [PATCH 18/45] media: qcom: camss: integrate IFE devices
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (16 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 17/45] media: qcom: camss: add common IFE subdevice core Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 19/45] media: qcom: camss: sm8250: add pixel path resources Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (27 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Integrate IFE devices with CAMSS probe, power management, media entity
registration, and performance management. Keep the legacy VFE path
unchanged.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss.c | 471 +++++++++++++++++++++-
 1 file changed, 460 insertions(+), 11 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 1f662b1caf5a..7c355003c957 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -32,6 +32,9 @@
 #define CAMSS_CLOCK_MARGIN_NUMERATOR 105
 #define CAMSS_CLOCK_MARGIN_DENOMINATOR 100
 
+/* Number of pixels fetched per clock cycle */
+#define CAMSS_NUM_OF_PIXELS_PER_CYCLE	2
+
 static const struct parent_dev_ops vfe_parent_dev_ops;
 
 static const struct camss_subdev_resources csiphy_res_8x16[] = {
@@ -4653,6 +4656,108 @@ s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
 	return v4l2_get_link_freq(sensor_pad, bpp, 2 * lanes);
 }
 
+static int camss_get_num_lanes(struct media_entity *entity,
+			       unsigned int *lanes)
+{
+	struct media_pad *pad;
+
+	while (1) {
+		struct v4l2_subdev *sd;
+
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			return -ENODEV;
+
+		pad = media_pad_remote_pad_first(pad);
+		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+			return -ENODEV;
+
+		sd = media_entity_to_v4l2_subdev(pad->entity);
+		if (sd->grp_id == CSIPHY_GRP_ID) {
+			struct csiphy_lanes_cfg *lane_cfg;
+			struct csiphy_device *csiphy;
+
+			csiphy = v4l2_get_subdevdata(sd);
+			if (!csiphy->cfg.csi2)
+				return -ENODEV;
+
+			lane_cfg = &csiphy->cfg.csi2->lane_cfg;
+			if (!lane_cfg->num_data)
+				return -EINVAL;
+
+			*lanes = lane_cfg->num_data;
+			return 0;
+		}
+
+		entity = pad->entity;
+	}
+}
+
+static int camss_get_pixel_clock_from_link_freq(struct media_entity *entity,
+						unsigned int bpp,
+						u64 *pixel_clock)
+{
+	struct media_pad *sensor_pad;
+	unsigned int lanes;
+	s64 link_freq;
+	int ret;
+
+	if (!bpp)
+		return -EINVAL;
+
+	sensor_pad = camss_find_sensor_pad(entity);
+	if (!sensor_pad)
+		return -ENODEV;
+
+	ret = camss_get_num_lanes(entity, &lanes);
+	if (ret)
+		return ret;
+
+	link_freq = v4l2_get_link_freq(sensor_pad, bpp, 2 * lanes);
+	if (link_freq < 0)
+		return link_freq;
+
+	*pixel_clock = div_u64(link_freq * 2 * lanes, bpp);
+
+	return 0;
+}
+
+/*
+ * camss_get_pixel_clock - Get pixel clock rate from sensor
+ * @entity: Media entity in the current pipeline
+ * @bpp: Number of bits per pixel for the current format
+ * @pixel_clock: Received pixel clock value
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int camss_get_pixel_clock_bpp(struct media_entity *entity, unsigned int bpp,
+			      u64 *pixel_clock)
+{
+	struct media_pad *sensor_pad;
+	struct v4l2_subdev *subdev;
+	struct v4l2_ctrl *ctrl;
+	int ret;
+
+	ret = camss_get_pixel_clock_from_link_freq(entity, bpp, pixel_clock);
+	if (!ret)
+		return 0;
+
+	sensor_pad = camss_find_sensor_pad(entity);
+	if (!sensor_pad)
+		return -ENODEV;
+
+	subdev = media_entity_to_v4l2_subdev(sensor_pad->entity);
+
+	ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
+
+	if (!ctrl)
+		return -EINVAL;
+
+	*pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl);
+
+	return 0;
+}
+
 /*
  * camss_get_pixel_clock - Get pixel clock rate from sensor
  * @entity: Media entity in the current pipeline
@@ -4746,6 +4851,156 @@ static const struct parent_dev_ops vfe_parent_dev_ops = {
 	.get_base_address = vfe_parent_dev_ops_get_base_address
 };
 
+static const u32 cam_cc_sbi_clk_rates[] = {
+	19200000, 350000000, 475000000, 576000000, 680000000,
+};
+
+static int camss_set_perf_level(struct camss *camss, struct ife_device *ife)
+{
+	enum camss_perf_mode perf_max = CAMSS_PERF_MAX_MODES - 1;
+	u64 freq_min, pixel_clock = 0;
+	u32 sink_pad = ife->sink_pad;
+	int pixelformat, fmt_index, i;
+	u8 bits_per_pixel = 0;
+	int ret;
+
+	fmt_index = camss_get_pixfmt_index(ife->pads[sink_pad].fmt.code,
+					   ife->pads[sink_pad].desc->sup_fmts.formats,
+					   ife->pads[sink_pad].desc->sup_fmts.nformats);
+	if (fmt_index < 0) {
+		dev_err(camss->dev, "%s Can't find requested format index: 0x%x\n", __func__,
+			ife->pads[sink_pad].fmt.code);
+		return fmt_index;
+	}
+
+	pixelformat = ife->pads[sink_pad].desc->sup_fmts.formats[fmt_index].pixelformat;
+
+	ret = camss_get_pixfmt_depth(pixelformat, Y_PLANE, &bits_per_pixel);
+	if (ret) {
+		dev_err(camss->dev, "%s Unknown pixel format: 0x%x\n", __func__,
+			pixelformat);
+		return ret;
+	}
+
+	ret = camss_get_pixel_clock_bpp(&ife->sd.entity, bits_per_pixel,
+					&pixel_clock);
+	if (ret) {
+		camss->perf_mode = CAMSS_PERF_MODE_NOM;
+		return 0;
+	}
+
+	freq_min = pixel_clock * bits_per_pixel / CAMSS_NUM_OF_PIXELS_PER_CYCLE / BITS_PER_BYTE;
+	camss->perf_mode = perf_max;
+
+	for (i = 0; i < ARRAY_SIZE(cam_cc_sbi_clk_rates); i++) {
+		if (freq_min <= cam_cc_sbi_clk_rates[i]) {
+			camss->perf_mode = min_t(u8, i, perf_max);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int camss_core_get(struct camss *camss, u8 entity_id)
+{
+	int ret;
+
+	if (atomic_inc_return(&camss->perf_ref_count) > 1)
+		return 0;
+
+	ret = camss_set_perf_level(camss, &camss->ife[entity_id]);
+	if (ret)
+		dev_warn(camss->dev, "Cannot configure the performance level\n");
+
+	ret = pm_runtime_resume_and_get(camss->dev);
+	if (ret < 0) {
+		dev_err(camss->dev, "Core domain cannot be acquired\n");
+		atomic_dec(&camss->perf_ref_count);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int camss_core_put(struct camss *camss)
+{
+	int ret;
+
+	if (atomic_dec_if_positive(&camss->perf_ref_count))
+		return 0;
+
+	ret = pm_runtime_put_sync(camss->dev);
+	if (ret < 0) {
+		dev_err(camss->dev, "Core domain cannot be released\n");
+		return ret;
+	}
+
+	camss->perf_mode = CAMSS_PERF_MODE_MINSVS;
+
+	return 0;
+}
+
+static int ife_parent_dev_ops_get(struct camss *camss, int id)
+{
+	int ret = -EINVAL;
+
+	if (id < camss->res->ife_num + camss->res->ife_lite_num) {
+		struct ife_device *ife = &camss->ife[id];
+
+		ret = ife_get(ife);
+	}
+
+	return ret;
+}
+
+static int ife_parent_dev_ops_put(struct camss *camss, int id)
+{
+	if (id < camss->res->ife_num + camss->res->ife_lite_num) {
+		struct ife_device *ife = &camss->ife[id];
+
+		ife_put(ife);
+	}
+
+	return 0;
+}
+
+static void __iomem
+*ife_parent_dev_ops_get_base_address(struct camss *camss, int id)
+{
+	if (id < camss->res->ife_num + camss->res->ife_lite_num) {
+		struct ife_device *ife = &camss->ife[id];
+
+		return ife->top_ctx.base;
+	}
+
+	return NULL;
+}
+
+static const struct parent_dev_ops ife_parent_dev_ops __maybe_unused = {
+	.get = ife_parent_dev_ops_get,
+	.put = ife_parent_dev_ops_put,
+	.get_base_address = ife_parent_dev_ops_get_base_address
+};
+
+int camss_attach_host_data(struct camss *camss, struct v4l2_subdev *sd,
+			   bool is_sensor, int id)
+{
+	struct camss_host_priv_data *host_data;
+	struct device *dev = camss->dev;
+
+	host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL);
+	if (!host_data)
+		return -ENOMEM;
+
+	host_data->is_sensor = is_sensor;
+	host_data->id = id;
+
+	v4l2_set_subdev_hostdata(sd, host_data);
+
+	return 0;
+}
+
 /*
  * camss_parse_endpoint_node - Parse port endpoint node
  * @dev: CAMSS device
@@ -4833,6 +5088,46 @@ static int camss_parse_ports(struct camss *camss)
 	return ret;
 }
 
+static int camss_init_csid_subdevices(struct camss *camss)
+{
+	const struct camss_resources *res = camss->res;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < res->csid_num; i++) {
+		ret = msm_csid_subdev_init(camss, &camss->csid[i],
+					   &res->csid_res[i], i);
+		if (ret < 0) {
+			dev_err(camss->dev, "Failed to init csid%d sub-device: %d\n",
+				i, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int camss_init_ife_subdevices(struct camss *camss)
+{
+	const struct camss_resources *res = camss->res;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < camss->ife_total_num; i++) {
+		void __iomem *csid_base = camss->csid[i].base;
+
+		ret = camss_ife_subdev_init(camss, &camss->ife[i],
+					    &res->ife_res[i], i, csid_base);
+		if (ret < 0) {
+			dev_err(camss->dev, "Fail to init ife%d sub-device: %d\n",
+				i, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 /*
  * camss_init_subdevices - Initialize subdev structures and resources
  * @camss: CAMSS device
@@ -4882,6 +5177,11 @@ static int camss_init_subdevices(struct camss *camss)
 		}
 	}
 
+	if (camss->ife_total_num > res->csid_num) {
+		dev_err(camss->dev, "IFE resources require one CSID per IFE\n");
+		return -EINVAL;
+	}
+
 	/* Get optional CSID wrapper regs shared between CSID devices */
 	if (res->csid_wrapper_res) {
 		char *reg = res->csid_wrapper_res->reg;
@@ -4893,15 +5193,20 @@ static int camss_init_subdevices(struct camss *camss)
 		camss->csid_wrapper_base = base;
 	}
 
-	for (i = 0; i < camss->res->csid_num; i++) {
-		ret = msm_csid_subdev_init(camss, &camss->csid[i],
-					   &res->csid_res[i], i);
-		if (ret < 0) {
-			dev_err(camss->dev,
-				"Failed to init csid%d sub-device: %d\n",
-				i, ret);
+	if (res->embedded_csid) {
+		ret = camss_init_ife_subdevices(camss);
+		if (ret)
+			return ret;
+	}
+
+	ret = camss_init_csid_subdevices(camss);
+	if (ret)
+		return ret;
+
+	if (!res->embedded_csid) {
+		ret = camss_init_ife_subdevices(camss);
+		if (ret)
 			return ret;
-		}
 	}
 
 	ret = msm_ispif_subdev_init(camss, res->ispif_res);
@@ -4942,6 +5247,9 @@ static int camss_link_entities(struct camss *camss)
 	int i, j, k;
 	int ret;
 
+	if (camss->ife_total_num)
+		return camss->res->link_entities(camss);
+
 	for (i = 0; i < camss->res->csiphy_num; i++) {
 		for (j = 0; j < camss->res->csid_num; j++) {
 			ret = media_create_pad_link(&camss->csiphy[i].subdev.entity,
@@ -5126,8 +5434,24 @@ static int camss_register_entities(struct camss *camss)
 		}
 	}
 
+	for (i = 0; i < camss->ife_total_num; i++) {
+		ret = ife_register_entities(&camss->ife[i],
+					    &camss->v4l2_dev);
+		if (ret < 0) {
+			dev_err(camss->dev,
+				"Failed to register ife%d entities: %d\n",
+				i, ret);
+			goto err_reg_ife;
+		}
+	}
+
 	return 0;
 
+err_reg_ife:
+	for (i--; i >= 0; i--)
+		ife_unregister_entities(&camss->ife[i]);
+
+	i = camss->res->vfe_num;
 err_reg_vfe:
 	for (i--; i >= 0; i--)
 		msm_vfe_unregister_entities(&camss->vfe[i]);
@@ -5180,6 +5504,9 @@ static void camss_unregister_entities(struct camss *camss)
 
 	for (i = 0; i < camss->res->vfe_num; i++)
 		msm_vfe_unregister_entities(&camss->vfe[i]);
+
+	for (i = 0; i < camss->ife_total_num; i++)
+		ife_unregister_entities(&camss->ife[i]);
 }
 
 static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async,
@@ -5193,6 +5520,20 @@ static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async,
 	struct csiphy_device *csiphy = &camss->csiphy[id];
 
 	csiphy->cfg.csi2 = &csd->interface.csi2;
+
+	if (camss->ife_total_num) {
+		int ret;
+
+		ret = camss_attach_host_data(camss, subdev, true, id);
+		if (ret) {
+			dev_err(camss->dev,
+				"Failed to alloc host private data: %d\n", ret);
+			return ret;
+		}
+
+		return 0;
+	}
+
 	subdev->host_priv = csiphy;
 
 	return 0;
@@ -5210,6 +5551,16 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async)
 		unsigned int i;
 		int ret;
 
+		if (camss->ife_total_num) {
+			struct camss_host_priv_data *host_data;
+
+			host_data = v4l2_get_subdev_hostdata(sd);
+			if (!host_data || !host_data->is_sensor)
+				continue;
+
+			csiphy = &camss->csiphy[host_data->id];
+		}
+
 		if (!csiphy)
 			continue;
 
@@ -5247,6 +5598,53 @@ static const struct media_device_ops camss_media_ops = {
 	.link_notify = v4l2_pipeline_link_notify,
 };
 
+static int camss_configure_ife_pd(struct camss *camss)
+{
+	struct device *dev = camss->dev;
+	int i;
+	int ret;
+
+	camss->ife_genpd = devm_kcalloc(dev, camss->genpd_num,
+					sizeof(*camss->ife_genpd), GFP_KERNEL);
+	if (!camss->ife_genpd)
+		return -ENOMEM;
+
+	camss->ife_genpd_link = devm_kcalloc(dev, camss->genpd_num,
+					     sizeof(*camss->ife_genpd_link),
+					     GFP_KERNEL);
+	if (!camss->ife_genpd_link)
+		return -ENOMEM;
+
+	for (i = 0; i < camss->genpd_num; i++) {
+		camss->ife_genpd[i] = dev_pm_domain_attach_by_id(dev, i);
+		if (IS_ERR(camss->ife_genpd[i])) {
+			ret = PTR_ERR(camss->ife_genpd[i]);
+			goto fail_pm;
+		}
+	}
+
+	if (camss->genpd_num > camss->res->ife_num) {
+		i = camss->genpd_num - 1;
+		camss->ife_genpd_link[i] =
+			device_link_add(dev, camss->ife_genpd[i],
+					DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+					DL_FLAG_RPM_ACTIVE);
+		if (!camss->ife_genpd_link[i]) {
+			ret = -EINVAL;
+			i = camss->genpd_num;
+			goto fail_pm;
+		}
+	}
+
+	return 0;
+
+fail_pm:
+	while (i--)
+		dev_pm_domain_detach(camss->ife_genpd[i], true);
+
+	return ret;
+}
+
 static int camss_configure_pd(struct camss *camss)
 {
 	const struct camss_resources *res = camss->res;
@@ -5271,6 +5669,9 @@ static int camss_configure_pd(struct camss *camss)
 	if (camss->genpd_num == 1)
 		return 0;
 
+	if (camss->ife_total_num)
+		return camss_configure_ife_pd(camss);
+
 	/* count the # of VFEs which have flagged power-domain */
 	for (vfepd_num = i = 0; i < camss->res->vfe_num; i++) {
 		if (res->vfe_res[i].vfe.has_pd)
@@ -5351,6 +5752,20 @@ static void camss_genpd_subdevice_cleanup(struct camss *camss)
 		msm_vfe_genpd_cleanup(&camss->vfe[i]);
 }
 
+static void camss_ife_genpd_cleanup(struct camss *camss)
+{
+	int i;
+
+	if (camss->genpd_num > camss->res->ife_num) {
+		i = camss->genpd_num - 1;
+		if (camss->ife_genpd_link[i])
+			device_link_del(camss->ife_genpd_link[i]);
+	}
+
+	for (i = 0; i < camss->genpd_num; i++)
+		dev_pm_domain_detach(camss->ife_genpd[i], true);
+}
+
 static void camss_genpd_cleanup(struct camss *camss)
 {
 	if (camss->genpd_num == 1)
@@ -5358,6 +5773,11 @@ static void camss_genpd_cleanup(struct camss *camss)
 
 	camss_genpd_subdevice_cleanup(camss);
 
+	if (camss->ife_total_num) {
+		camss_ife_genpd_cleanup(camss);
+		return;
+	}
+
 	if (camss->genpd_link)
 		device_link_del(camss->genpd_link);
 
@@ -5382,8 +5802,23 @@ static int camss_probe(struct platform_device *pdev)
 
 	camss->res = of_device_get_match_data(dev);
 
+	if (camss->res->vfe_num &&
+	    (camss->res->ife_num || camss->res->ife_lite_num)) {
+		dev_err(dev, "VFE and IFE resources are mutually exclusive\n");
+		return -EINVAL;
+	}
+
+	if ((camss->res->ife_num || camss->res->ife_lite_num) &&
+	    !camss->res->link_entities) {
+		dev_err(dev, "IFE resources require entity link operations\n");
+		return -EINVAL;
+	}
+
 	atomic_set(&camss->ref_count, 0);
+	atomic_set(&camss->perf_ref_count, 0);
 	camss->dev = dev;
+	camss->core_get = camss_core_get;
+	camss->core_put = camss_core_put;
 	platform_set_drvdata(pdev, camss);
 
 	camss->csiphy = devm_kcalloc(dev, camss->res->csiphy_num,
@@ -5417,6 +5852,14 @@ static int camss_probe(struct platform_device *pdev)
 	if (!camss->vfe)
 		return -ENOMEM;
 
+	camss->ife_total_num = camss->res->ife_num + camss->res->ife_lite_num;
+	if (camss->ife_total_num) {
+		camss->ife = devm_kcalloc(dev, camss->ife_total_num,
+					  sizeof(*camss->ife), GFP_KERNEL);
+		if (!camss->ife)
+			return -ENOMEM;
+	}
+
 	ret = camss_icc_get(camss);
 	if (ret < 0) {
 		dev_err(dev, "Failed to configure ICC: %d\n", ret);
@@ -5430,8 +5873,10 @@ static int camss_probe(struct platform_device *pdev)
 	}
 
 	ret = camss_init_subdevices(camss);
-	if (ret < 0)
+	if (ret < 0) {
+		dev_err(dev, "Failed to init subdevices: %d\n", ret);
 		goto err_genpd_cleanup;
+	}
 
 	ret = dma_set_mask_and_coherent(dev, 0xffffffff);
 	if (ret) {
@@ -5463,12 +5908,16 @@ static int camss_probe(struct platform_device *pdev)
 	}
 
 	ret = camss_register_entities(camss);
-	if (ret < 0)
+	if (ret < 0) {
+		dev_err(dev, "Failed to register entities: %d\n", ret);
 		goto err_v4l2_device_unregister;
+	}
 
 	ret = camss_link_entities(camss);
-	if (ret < 0)
+	if (ret < 0) {
+		dev_err(dev, "Failed to link entities: %d\n", ret);
 		goto err_register_subdevs;
+	}
 
 	ret = media_device_register(&camss->media_dev);
 	if (ret < 0) {
-- 
2.34.1


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

* [PATCH 19/45] media: qcom: camss: sm8250: add pixel path resources
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (17 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 18/45] media: qcom: camss: integrate IFE devices Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 20/45] media: uapi: add Qualcomm IFE statistics Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (26 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add CSID, IFE, and IFE Lite resources for the SM8250 pixel path. Select
them with the qcom,sm8250pix-camss compatible.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss.c | 224 +++++++++++++++++++++-
 1 file changed, 222 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 7c355003c957..2dc7864ebe07 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -27,6 +27,7 @@
 #include <media/v4l2-mc.h>
 #include <media/v4l2-fwnode.h>
 
+#include "camss-ife-camif-v10x.h"
 #include "camss.h"
 
 #define CAMSS_CLOCK_MARGIN_NUMERATOR 105
@@ -36,6 +37,7 @@
 #define CAMSS_NUM_OF_PIXELS_PER_CYCLE	2
 
 static const struct parent_dev_ops vfe_parent_dev_ops;
+static const struct parent_dev_ops ife_parent_dev_ops;
 
 static const struct camss_subdev_resources csiphy_res_8x16[] = {
 	/* CSIPHY0 */
@@ -2269,6 +2271,208 @@ static const struct resources_icc icc_res_sm8250[] = {
 	},
 };
 
+static const struct camss_ife_stream csid_ife_streams_sm8250pix[] = {
+	{ .id = 0, .pipe = CAMSS_IFE_HW_PIPE_RDI0 },
+	{ .id = 1, .pipe = CAMSS_IFE_HW_PIPE_RDI1 },
+	{ .id = 2, .pipe = CAMSS_IFE_HW_PIPE_RDI2 },
+	{ .id = 3, .pipe = CAMSS_IFE_HW_PIPE_PP },
+};
+
+static const struct camss_ife_stream csid_ife_lite_streams_sm8250pix[] = {
+	{ .id = 0, .pipe = CAMSS_IFE_HW_PIPE_RDI0 },
+	{ .id = 1, .pipe = CAMSS_IFE_HW_PIPE_RDI1 },
+	{ .id = 2, .pipe = CAMSS_IFE_HW_PIPE_RDI2 },
+	{ .id = 3, .pipe = CAMSS_IFE_HW_PIPE_RDI3 },
+};
+
+static const struct camss_subdev_resources csid_res_sm8250pix[] = {
+	/* CSID0 */
+	{
+		.regulators = {},
+		.clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_areg", "vfe0_ahb" },
+		.clock_rate = { { 400000000 },
+				{ 400000000 },
+				{ 350000000, 475000000, 576000000, 720000000 },
+				{ 100000000, 200000000, 300000000, 400000000 },
+				{ 0 } },
+		.reg = { "csid0" },
+		.interrupt = { "csid0" },
+		.csid = {
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen2,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		}
+	},
+	/* CSID1 */
+	{
+		.regulators = {},
+		.clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_areg", "vfe1_ahb" },
+		.clock_rate = { { 400000000 },
+				{ 400000000 },
+				{ 350000000, 475000000, 576000000, 720000000 },
+				{ 100000000, 200000000, 300000000, 400000000 },
+				{ 0 } },
+		.reg = { "csid1" },
+		.interrupt = { "csid1" },
+		.csid = {
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen2,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		}
+	},
+	/* CSID2 */
+	{
+		.regulators = {},
+		.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" },
+		.clock_rate = { { 400000000 },
+				{ 400000000 },
+				{ 400000000, 480000000 },
+				{ 0 } },
+		.reg = { "csid2" },
+		.interrupt = { "csid2" },
+		.csid = {
+			.is_lite = true,
+			.streams_enable = true,
+			.hw_ops = &csid_ops_gen2,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		}
+	},
+	/* CSID3 */
+	{
+		.regulators = {},
+		.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" },
+		.clock_rate = { { 400000000 },
+				{ 400000000 },
+				{ 400000000, 480000000 },
+				{ 0 } },
+		.reg = { "csid3" },
+		.interrupt = { "csid3" },
+		.csid = {
+			.is_lite = true,
+			.streams_enable = true,
+			.hw_ops = &csid_ops_gen2,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		}
+	}
+};
+
+static const struct camss_subdev_resources ife_res_sm8250pix[] = {
+	/* IFE0 */
+	{
+		.regulators = {},
+		.clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+			   "camnoc_axi", "vfe0_ahb", "vfe0_areg", "vfe0",
+			   "vfe0_axi", "cam_hf_axi" },
+		.clock_rate = { { },
+				{ },
+				{ },
+				{ },
+				{ },
+				{ 19200000, 100000000, 200000000, 300000000, 400000000 },
+				{ 19200000, 350000000, 475000000, 576000000, 680000000 },
+				{ 19200000, 300000000, 400000000, 400000000, 400000000 },
+				{ } },
+		.reg = { "vfe0" },
+		.interrupt = { "vfe0" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE,
+			.pads = &ife_pads_v20x,
+			.top_ops = &ife_top_20x_ops,
+			.camif_ops = &ife_camif_v10x_ops,
+			.mod_ops = &ife_mod_v10x_ops,
+			.buswr_ops = &ife_buswr_20x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_streams_sm8250pix),
+			.sink_streams = csid_ife_streams_sm8250pix,
+		},
+	},
+	/* IFE1 */
+	{
+		.regulators = {},
+		.clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+			   "camnoc_axi", "vfe1_ahb", "vfe1_areg", "vfe1",
+			   "vfe1_axi", "cam_hf_axi" },
+		.clock_rate = { { },
+				{ },
+				{ },
+				{ },
+				{ },
+				{ 19200000, 100000000, 200000000, 300000000, 400000000 },
+				{ 19200000, 350000000, 475000000, 576000000, 680000000 },
+				{ 19200000, 300000000, 400000000, 400000000, 400000000 },
+				{ } },
+		.reg = { "vfe1" },
+		.interrupt = { "vfe1" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE,
+			.pads = &ife_pads_v20x,
+			.top_ops = &ife_top_20x_ops,
+			.camif_ops = &ife_camif_v10x_ops,
+			.mod_ops = &ife_mod_v10x_ops,
+			.buswr_ops = &ife_buswr_20x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_streams_sm8250pix),
+			.sink_streams = csid_ife_streams_sm8250pix,
+		},
+	},
+	/* IFE2 (lite) */
+	{
+		.regulators = {},
+		.clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+			   "camnoc_axi", "vfe_lite_ahb", "vfe_lite_axi",
+			   "vfe_lite", "cam_hf_axi" },
+		.clock_rate = { { },
+				{ },
+				{ },
+				{ },
+				{ },
+				{ 19200000, 300000000, 400000000, 400000000, 400000000 },
+				{ 19200000, 400000000, 480000000, 480000000, 480000000 },
+				{ } },
+		.reg = { "vfe_lite0" },
+		.interrupt = { "vfe_lite0" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+			.pads = &ife_lite_pads_v20x,
+			.top_ops = &ife_lite_top_10x_ops,
+			.camif_ops = &ife_camif_v10x_ops,
+			.buswr_ops = &ife_buswr_20x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_lite_streams_sm8250pix),
+			.sink_streams = csid_ife_lite_streams_sm8250pix,
+		}
+	},
+	/* IFE3 (lite) */
+	{
+		.regulators = {},
+		.clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+			   "camnoc_axi", "vfe_lite_ahb", "vfe_lite_axi",
+			   "vfe_lite", "cam_hf_axi" },
+		.clock_rate = { { },
+				{ },
+				{ },
+				{ },
+				{ },
+				{ 19200000, 300000000, 400000000, 400000000, 400000000 },
+				{ 19200000, 400000000, 480000000, 480000000, 480000000 },
+				{ } },
+		.reg = { "vfe_lite1" },
+		.interrupt = { "vfe_lite1" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+			.pads = &ife_lite_pads_v20x,
+			.top_ops = &ife_lite_top_10x_ops,
+			.camif_ops = &ife_camif_v10x_ops,
+			.buswr_ops = &ife_buswr_20x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_lite_streams_sm8250pix),
+			.sink_streams = csid_ife_lite_streams_sm8250pix,
+		}
+	},
+};
+
 static const struct camss_subdev_resources csiphy_res_7280[] = {
 	/* CSIPHY0 */
 	{
@@ -4977,10 +5181,10 @@ static void __iomem
 	return NULL;
 }
 
-static const struct parent_dev_ops ife_parent_dev_ops __maybe_unused = {
+static const struct parent_dev_ops ife_parent_dev_ops = {
 	.get = ife_parent_dev_ops_get,
 	.put = ife_parent_dev_ops_put,
-	.get_base_address = ife_parent_dev_ops_get_base_address
+	.get_base_address = ife_parent_dev_ops_get_base_address,
 };
 
 int camss_attach_host_data(struct camss *camss, struct v4l2_subdev *sd,
@@ -6142,6 +6346,21 @@ static const struct camss_resources sm8250_resources = {
 	.csid_vfe_fixed_pairing = true,
 };
 
+static const struct camss_resources sm8250pix_resources = {
+	.version = CAMSS_8250,
+	.csiphy_res = csiphy_res_8250,
+	.csid_res = csid_res_sm8250pix,
+	.ife_res = ife_res_sm8250pix,
+	.icc_res = icc_res_sm8250,
+	.icc_path_num = ARRAY_SIZE(icc_res_sm8250),
+	.csiphy_num = ARRAY_SIZE(csiphy_res_8250),
+	.csid_num = ARRAY_SIZE(csid_res_sm8250pix),
+	.embedded_csid = true,
+	.ife_num = 2,
+	.ife_lite_num = 2,
+	.link_entities = camss_ife_link_entities,
+};
+
 static const struct camss_resources sc8280xp_resources = {
 	.version = CAMSS_8280XP,
 	.pd_name = "top",
@@ -6229,6 +6448,7 @@ static const struct of_device_id camss_dt_match[] = {
 	{ .compatible = "qcom,sm6150-camss", .data = &sm6150_resources },
 	{ .compatible = "qcom,sm6350-camss", .data = &sm6350_resources },
 	{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
+	{ .compatible = "qcom,sm8250pix-camss", .data = &sm8250pix_resources },
 	{ .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
 	{ .compatible = "qcom,sm8650-camss", .data = &sm8650_resources },
 	{ .compatible = "qcom,x1e80100-camss", .data = &x1e80100_resources },
-- 
2.34.1


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

* [PATCH 20/45] media: uapi: add Qualcomm IFE statistics
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (18 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 19/45] media: qcom: camss: sm8250: add pixel path resources Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 21/45] media: v4l2: add Qualcomm IFE statistics format Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (25 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Define the Bayer grid statistics buffer produced by Qualcomm IFE
hardware.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 include/uapi/linux/qcom-camss-stats.h | 62 +++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 include/uapi/linux/qcom-camss-stats.h

diff --git a/include/uapi/linux/qcom-camss-stats.h b/include/uapi/linux/qcom-camss-stats.h
new file mode 100644
index 000000000000..52a9d3e8171f
--- /dev/null
+++ b/include/uapi/linux/qcom-camss-stats.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (C) 2023 Qualcomm Technologies, Inc. */
+
+#ifndef _UAPI_LINUX_QCOM_CAMSS_STATS_H
+#define _UAPI_LINUX_QCOM_CAMSS_STATS_H
+
+#include <linux/types.h>
+
+/* Max number of bayer grid regions available in statistics */
+#define CAMSS_IFE_MAX_BAYER_GRID_REGIONS (64 * 48)
+
+/**
+ * struct camss_ife_bg_region_stats - IFE bayer grid statistics region.
+ *
+ * The IFE bayer grid statistic region contain sum of pixels and saturated
+ * counters within the region. The region configuration should be available
+ * in the IFE params bayer grid configuration structure.
+ *
+ * @r_sum: Sum of the red pixels.
+ * @b_sum: Sum of the blue pixels.
+ * @gr_sum: Sum of the green red pixels.
+ * @gb_sum: Sum of the green blue pixels.
+ * @r_cnt: Red pixels count.
+ * @b_cnt: Blue pixels count.
+ * @gr_cnt: Green red pixels count.
+ * @gb_cnt: Green blue pixels count.
+ * @sat_r_sum: Saturated sum of the red pixels.
+ * @sat_b_sum: Saturated sum of the blue pixels.
+ * @sat_gr_sum: Saturated sum of the green red pixels.
+ * @sat_gb_sum: Saturated sum of the green blue pixels.
+ * @sat_r_cnt: Saturated red pixels count.
+ * @sat_b_cnt: Saturated blue pixels count.
+ * @sat_gr_cnt: Saturated green red pixels count.
+ * @sat_gb_cnt: Saturated green blue pixels count.
+ */
+struct camss_ife_bg_region_stats {
+	__u32 r_sum;
+	__u32 b_sum;
+	__u32 gr_sum;
+	__u32 gb_sum;
+
+	__u16 r_cnt;
+	__u16 b_cnt;
+	__u16 gr_cnt;
+	__u16 gb_cnt;
+
+	__u32 sat_r_sum;
+	__u32 sat_b_sum;
+	__u32 sat_gr_sum;
+	__u32 sat_gb_sum;
+
+	__u16 sat_r_cnt;
+	__u16 sat_b_cnt;
+	__u16 sat_gr_cnt;
+	__u16 sat_gb_cnt;
+} __attribute__((packed));
+
+struct camss_ife_stats {
+	struct camss_ife_bg_region_stats regions[CAMSS_IFE_MAX_BAYER_GRID_REGIONS];
+} __attribute__((packed));
+
+#endif /* _UAPI_LINUX_QCOM_CAMSS_STATS_H */
-- 
2.34.1


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

* [PATCH 21/45] media: v4l2: add Qualcomm IFE statistics format
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (19 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 20/45] media: uapi: add Qualcomm IFE statistics Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 22/45] media: qcom: camss: add IFE statistics output Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (24 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Describe Qualcomm IFE 3A statistics buffers with the QCF1 metadata
format.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
 include/uapi/linux/videodev2.h       | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index cef1593c1835..b7173a258225 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1467,6 +1467,9 @@ 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_QC_IFE_STAT_3A:
+		descr = "Qualcomm IFE 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/videodev2.h b/include/uapi/linux/videodev2.h
index 70208a9443d8..4a0748b60a15 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 Qualcomm IFE camera sub-system */
+#define V4L2_META_FMT_QC_IFE_STAT_3A	v4l2_fourcc('Q', 'C', 'F', '1') /* IFE 3A Statistics */
+
 /* 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.34.1


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

* [PATCH 22/45] media: qcom: camss: add IFE statistics output
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (20 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 21/45] media: v4l2: add Qualcomm IFE statistics format Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 23/45] media: uapi: add Qualcomm IFE parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (23 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add metadata capture support to CAMSS video nodes.
Expose the SM8250 IFE statistics output through write master 15.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-pads.c      |  34 +++++
 drivers/media/platform/qcom/camss/camss-ife.c |  44 ++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   3 +
 .../media/platform/qcom/camss/camss-video.c   | 141 ++++++++++++++++++
 .../media/platform/qcom/camss/camss-video.h   |   2 +
 5 files changed, 224 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-pads.c b/drivers/media/platform/qcom/camss/camss-ife-pads.c
index 926ba96a7603..bd9ac1a91d73 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-pads.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-pads.c
@@ -3,6 +3,7 @@
  * Copyright (c) 2023 Qualcomm Technologies, Inc.
  */
 
+#include <linux/qcom-camss-stats.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-mediabus.h>
 
@@ -63,6 +64,11 @@ static const struct camss_format_info ife_vid_pix_fmts_v20x[] = {
 	  { { 1, 1 } }, { { 2, 3 } }, { 8 } },
 };
 
+static const struct camss_format_info ife_stats_fmts_v20x[] = {
+	{ MEDIA_BUS_FMT_METADATA_FIXED, 8, V4L2_META_FMT_QC_IFE_STAT_3A, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+};
+
 static const struct ife_pad_limits ife_vid_limits_v20x = {
 	.min_width	= 320,
 	.min_height	= 240,
@@ -72,6 +78,13 @@ static const struct ife_pad_limits ife_vid_limits_v20x = {
 	.max_v_ratio	= 16,
 };
 
+static const struct ife_pad_limits ife_meta_limits_v20x = {
+	.min_width	= 16,
+	.min_height	= 16,
+	.max_width	= 16,
+	.max_height	= 16,
+};
+
 static const struct ife_pad_limits ife_fd_limits = {
 	.min_width	= 320,
 	.min_height	= 240,
@@ -293,6 +306,27 @@ static const struct ife_pad_desc ife_pad_desc_v20x[] = {
 		.wm[0] = 8,
 		.wm[1] = 9,
 	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_stats_fmts_v20x),
+			.formats = ife_stats_fmts_v20x,
+		},
+		.limits = &ife_meta_limits_v20x,
+		.video_name = "stats",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.meta_dataformat = V4L2_META_FMT_QC_IFE_STAT_3A,
+		.meta_buffersize = PAGE_ALIGN(sizeof(struct camss_ife_stats)),
+		.output = CAMSS_IFE_OUTPUT_STATS,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+		.packing_fmt = 0xa,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(6),
+		.comp_group_mask = BIT(12),
+		.num_wms = 1,
+		.wm[0] = 15,
+	},
 };
 
 const struct ife_pads_descriptors ife_lite_pads_v20x = {
diff --git a/drivers/media/platform/qcom/camss/camss-ife.c b/drivers/media/platform/qcom/camss/camss-ife.c
index cf1474e5bce4..f7ec86fb7f02 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.c
+++ b/drivers/media/platform/qcom/camss/camss-ife.c
@@ -66,6 +66,11 @@ static bool is_stats_type(const struct ife_pad_desc *pad)
 	return pad->buf_type == V4L2_BUF_TYPE_META_CAPTURE;
 }
 
+static bool is_ife_video_stats(const struct camss_video *video)
+{
+	return video->type == V4L2_BUF_TYPE_META_CAPTURE;
+}
+
 static bool is_meta_type(const struct ife_pad_desc *pad)
 {
 	return is_stats_type(pad);
@@ -467,6 +472,17 @@ static int ife_setup_vid_full(struct ife_video_pad *vid_pad,
 	u8 depth = 0;
 	int ret;
 
+	if (is_ife_video_stats(&vid_pad->video)) {
+		wm_cfg[Y_PLANE].mode_config = dsc->mode_config;
+		wm_cfg[Y_PLANE].packing_fmt = dsc->packing_fmt;
+		wm_cfg[Y_PLANE].width = 0;
+		wm_cfg[Y_PLANE].lines = 0;
+		wm_cfg[Y_PLANE].bytes = 1;
+		wm_cfg[Y_PLANE].stride = 1;
+
+		return 0;
+	}
+
 	wm_cfg[Y_PLANE].mode_config = dsc->mode_config;
 	wm_cfg[Y_PLANE].packing_fmt = dsc->packing_fmt;
 	wm_cfg[C_PLANE].mode_config = dsc->mode_config;
@@ -782,6 +798,9 @@ static int ife_pad_try_format(struct ife_device *ife, const struct ife_pad_desc
 	u32 in_height;
 	u32 in_width;
 
+	if (is_meta_type(desc))
+		return -ENOTTY;
+
 	for (i = 0; i < sup_fmts->nformats; i++)
 		if (fmt->code == sup_fmts->formats[i].code)
 			break;
@@ -841,6 +860,11 @@ static int ife_pad_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
 
 	pad = &ife->pads[sd_fmt->pad];
 
+	if (is_meta_type(pad->desc)) {
+		sd_fmt->format = pad->fmt;
+		return 0;
+	}
+
 	if (sd_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
 		ife_pad_try_format(ife, pad->desc, &sd_fmt->format);
 	} else {
@@ -884,6 +908,9 @@ static void ife_update_src_pads_fmt(struct ife_device *ife, struct v4l2_mbus_fra
 
 		fi = ife_pad_try_format(ife, pad->desc, &new_fmt);
 
+		if (vpad && is_ife_video_stats(&vpad->video))
+			vpad = NULL;
+
 		if (vpad) {
 			struct v4l2_pix_format_mplane *pix_mp = &vpad->video.active_fmt.fmt.pix_mp;
 			const struct camss_format_info *info = &pad->desc->sup_fmts.formats[fi];
@@ -913,6 +940,11 @@ static int ife_pad_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sta
 
 	pad = &ife->pads[sd_fmt->pad];
 
+	if (is_meta_type(pad->desc)) {
+		sd_fmt->format = pad->fmt;
+		return 0;
+	}
+
 	ife_pad_try_format(ife, pad->desc, &sd_fmt->format);
 
 	if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
@@ -959,6 +991,9 @@ static int ife_pad_mbus_enum_code(struct v4l2_subdev *sd, struct v4l2_subdev_sta
 
 	pad = &ife->pads[code->pad];
 
+	if (is_stats_type(pad->desc))
+		return -ENOTTY;
+
 	if (pad->desc->pad_flags == MEDIA_PAD_FL_SINK ||
 	    pad->desc->hw_pipe == CAMSS_IFE_HW_PIPE_PP) {
 		if (code->index >= pad->desc->sup_fmts.nformats)
@@ -994,6 +1029,13 @@ static int ife_pad_enum_framesizes(struct v4l2_subdev *sd, struct v4l2_subdev_st
 
 	pad = &ife->pads[fse->pad];
 
+	if (is_meta_type(pad->desc)) {
+		if (is_stats_type(pad->desc))
+			return -ENOTTY;
+
+		return -EINVAL;
+	}
+
 	for (i = 0; i < pad->desc->sup_fmts.nformats; i++) {
 		if (pad->desc->sup_fmts.formats[i].code == fse->code)
 			break;
@@ -1517,6 +1559,8 @@ int ife_register_entities(struct ife_device *ife, struct v4l2_device *v4l2_dev)
 			pad->vid_pad->video.formats	= dsc->sup_fmts.formats;
 			pad->vid_pad->video.type	= dsc->buf_type;
 			pad->vid_pad->video.ops		= &camss_video_ops;
+			pad->vid_pad->video.meta_dataformat = dsc->meta_dataformat;
+			pad->vid_pad->video.meta_buffersize = dsc->meta_buffersize;
 			pad->vid_pad->video.bpl_alignment = 16;
 
 			if (dsc->mode_config == BUSWR_CLIENT_CFG_MODE_LINE_BASED)
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 45fce7e33c4a..168d26d0664b 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -45,6 +45,7 @@ enum camss_ife_output {
 	CAMSS_IFE_OUTPUT_VID_FULL,
 	CAMSS_IFE_OUTPUT_DISP_FULL,
 	CAMSS_IFE_OUTPUT_FD,
+	CAMSS_IFE_OUTPUT_STATS,
 };
 
 enum camss_ife_hw_pipe {
@@ -275,6 +276,8 @@ struct ife_pad_desc {
 	u32 link_flags;
 
 	enum v4l2_buf_type buf_type;
+	u32 meta_dataformat;
+	u32 meta_buffersize;
 	enum camss_ife_output output;
 	u32 hw_pipe;
 
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index cb9e08e9ff7e..b5d78f84d6a1 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -216,6 +216,9 @@ static int video_check_format(struct camss_video *video)
 	struct v4l2_pix_format_mplane *sd_pix = &format.fmt.pix_mp;
 	int ret;
 
+	if (video->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return 0;
+
 	sd_pix->pixelformat = pix->pixelformat;
 	ret = video_get_subdev_format(video, &format);
 	if (ret < 0)
@@ -421,6 +424,73 @@ static const struct vb2_ops msm_video_vb2_q_ops = {
 	.unprepare_streaming = video_unprepare_streaming,
 };
 
+static int meta_queue_setup(struct vb2_queue *q,
+			    unsigned int *num_buffers, unsigned int *num_planes,
+			    unsigned int sizes[], struct device *alloc_devs[])
+{
+	struct camss_video *video = vb2_get_drv_priv(q);
+	u32 buffersize = video->active_fmt.fmt.meta.buffersize;
+
+	if (*num_planes) {
+		if (*num_planes != 1 || sizes[0] < buffersize)
+			return -EINVAL;
+
+		return 0;
+	}
+
+	*num_planes		= 1;
+	sizes[0]		= buffersize;
+	q->min_queued_buffers	= 1;
+
+	return 0;
+}
+
+static int meta_buf_init(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer, vb);
+	struct sg_table *sgt;
+
+	sgt = vb2_dma_sg_plane_desc(vb, 0);
+	if (!sgt)
+		return -EFAULT;
+
+	buffer->addr[0] = sg_dma_address(sgt->sgl);
+
+	return 0;
+}
+
+static int meta_buf_prepare(struct vb2_buffer *vb)
+{
+	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	if (vbuf->field == V4L2_FIELD_ANY)
+		vbuf->field = V4L2_FIELD_NONE;
+	if (vbuf->field != V4L2_FIELD_NONE) {
+		dev_err(video->camss->dev, "%s field isn't supported\n", __func__);
+		return -EINVAL;
+	}
+
+	if (vb2_plane_size(vb, 0) < video->active_fmt.fmt.meta.buffersize)
+		return -EINVAL;
+
+	vb2_set_plane_payload(vb, 0, video->active_fmt.fmt.meta.buffersize);
+
+	return 0;
+}
+
+static const struct vb2_ops msm_meta_vb2_q_ops = {
+	.queue_setup     = meta_queue_setup,
+	.buf_init        = meta_buf_init,
+	.buf_prepare     = meta_buf_prepare,
+	.buf_queue       = video_buf_queue,
+	.prepare_streaming = video_prepare_streaming,
+	.start_streaming = video_start_streaming,
+	.stop_streaming  = video_stop_streaming,
+	.unprepare_streaming = video_unprepare_streaming,
+};
+
 /* -----------------------------------------------------------------------------
  * V4L2 ioctls
  */
@@ -693,6 +763,52 @@ static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
 	.vidioc_s_input			= video_s_input,
 };
 
+static int meta_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct camss_video *video = video_drvdata(file);
+	struct v4l2_meta_format *meta = &f->fmt.meta;
+
+	if (f->type != video->type)
+		return -EINVAL;
+
+	memset(meta, 0, sizeof(*meta));
+	meta->dataformat = video->active_fmt.fmt.meta.dataformat;
+	meta->buffersize = video->active_fmt.fmt.meta.buffersize;
+
+	return 0;
+}
+
+static int meta_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+	struct camss_video *video = video_drvdata(file);
+
+	if (f->index)
+		return -EINVAL;
+
+	memset(&f->reserved, 0, sizeof(f->reserved));
+
+	f->pixelformat = video->active_fmt.fmt.meta.dataformat;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops msm_meta_ioctl_ops = {
+	.vidioc_querycap		= video_querycap,
+	.vidioc_enum_fmt_meta_cap	= meta_enum_fmt,
+	.vidioc_g_fmt_meta_cap		= meta_try_fmt,
+	.vidioc_s_fmt_meta_cap		= meta_try_fmt,
+	.vidioc_try_fmt_meta_cap	= meta_try_fmt,
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+};
+
 /* -----------------------------------------------------------------------------
  * V4L2 file operations
  */
@@ -744,6 +860,18 @@ static int msm_video_init_format(struct camss_video *video)
 		},
 	};
 
+	if (video->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		if (!video->meta_dataformat || !video->meta_buffersize)
+			return -EINVAL;
+
+		format.type = video->type;
+		format.fmt.meta.dataformat = video->meta_dataformat;
+		format.fmt.meta.buffersize = video->meta_buffersize;
+		video->active_fmt = format;
+
+		return 0;
+	}
+
 	ret = __video_try_fmt(video, &format);
 	if (ret < 0)
 		return ret;
@@ -787,6 +915,14 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
 	q->buf_struct_size = sizeof(struct camss_buffer);
 	q->dev = video->camss->dev;
 	q->lock = &video->q_lock;
+
+	if (video->type == V4L2_BUF_TYPE_META_CAPTURE) {
+		q->ops = &msm_meta_vb2_q_ops;
+		q->io_modes = VB2_DMABUF | VB2_MMAP;
+	} else if (video->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		dev_err(v4l2_dev->dev, "Invalid format type\n");
+		goto error_vb2_init;
+	}
 	ret = vb2_queue_init(q);
 	if (ret < 0) {
 		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
@@ -813,6 +949,11 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING
 			  | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
 	vdev->ioctl_ops = &msm_vid_ioctl_ops;
+	if (video->type == V4L2_BUF_TYPE_META_CAPTURE) {
+		vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING |
+				    V4L2_CAP_IO_MC;
+		vdev->ioctl_ops = &msm_meta_ioctl_ops;
+	}
 	vdev->release = msm_video_release;
 	vdev->v4l2_dev = v4l2_dev;
 	vdev->vfl_dir = VFL_DIR_RX;
diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h
index f62a72c29a5a..cfee619973ef 100644
--- a/drivers/media/platform/qcom/camss/camss-video.h
+++ b/drivers/media/platform/qcom/camss/camss-video.h
@@ -48,6 +48,8 @@ struct camss_video {
 	struct mutex q_lock;
 	unsigned int bpl_alignment;
 	unsigned int line_based;
+	u32 meta_dataformat;
+	u32 meta_buffersize;
 	const struct camss_format_info *formats;
 	unsigned int nformats;
 };
-- 
2.34.1


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

* [PATCH 23/45] media: uapi: add Qualcomm IFE parameters
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (21 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 22/45] media: qcom: camss: add IFE statistics output Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 24/45] media: v4l2: add Qualcomm IFE parameters format Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (22 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Define the parameter buffer used to configure Qualcomm IFE image
processing modules.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 include/uapi/linux/qcom-camss-config.h | 157 +++++++++++++++++++++++--
 1 file changed, 150 insertions(+), 7 deletions(-)

diff --git a/include/uapi/linux/qcom-camss-config.h b/include/uapi/linux/qcom-camss-config.h
index aa3d97cfcc78..b44f80bf850a 100644
--- a/include/uapi/linux/qcom-camss-config.h
+++ b/include/uapi/linux/qcom-camss-config.h
@@ -11,6 +11,16 @@
 #include <linux/types.h>
 #include <linux/media/v4l2-isp.h>
 
+/* Number of entries in each gamma channel LUT. */
+#define CAMSS_GAMMA_LUT_SIZE	256
+
+/* Max number of lsc blocks */
+#define CAMSS_IFE_MAX_LSC_BLOCKS (17 * 13)
+/* Gamma table size for CAMSS_IFE_PARAMS_GAMMA_64 (v10x native LUT width) */
+#define CAMSS_IFE_GAMMA_64_SIZE 64
+/* Gamma table size for CAMSS_IFE_PARAMS_GAMMA_256 (v41x native LUT width) */
+#define CAMSS_IFE_GAMMA_256_SIZE 256
+
 /**
  * enum camss_params_block_type - CAMSS ISP parameter block identifiers
  *
@@ -25,17 +35,34 @@
  *	&struct camss_params_color_correct
  * @CAMSS_PARAMS_GAMMA: per-channel gamma correction curves (CLC_GLUT),
  *	&struct camss_params_gamma
+ * @CAMSS_IFE_PARAMS_WB_GAIN: IFE per-channel white balance gains,
+ *	&struct camss_ife_params_wb_gain
+ * @CAMSS_IFE_PARAMS_COLOR_CORRECT: IFE color correction matrix,
+ *	&struct camss_ife_params_color_correct
+ * @CAMSS_IFE_PARAMS_LSC: IFE lens shading correction,
+ *	&struct camss_ife_params_lsc
+ * @CAMSS_IFE_PARAMS_GAMMA_64: IFE 64-entry gamma correction curves, used by
+ *	IFE hardware generations whose native GLUT LUT is 64 entries wide,
+ *	&struct camss_ife_params_gamma_64
+ * @CAMSS_IFE_PARAMS_GAMMA_256: IFE 256-entry gamma correction curves, used
+ *	by IFE hardware generations whose native GLUT LUT is 256 entries
+ *	wide, &struct camss_ife_params_gamma_256
+ * @CAMSS_IFE_PARAMS_BG_STATS: enable/disable the IFE bayer grid 3A
+ *	statistics engine, &struct camss_ife_params_bg_stats
  */
 enum camss_params_block_type {
-	CAMSS_PARAMS_WB_GAIN = 1,
-	CAMSS_PARAMS_CHROMA_ENHAN = 2,
-	CAMSS_PARAMS_COLOR_CORRECT = 3,
-	CAMSS_PARAMS_GAMMA = 4,
+	CAMSS_PARAMS_WB_GAIN		= 1,
+	CAMSS_PARAMS_CHROMA_ENHAN	= 2,
+	CAMSS_PARAMS_COLOR_CORRECT	= 3,
+	CAMSS_PARAMS_GAMMA		= 4,
+	CAMSS_IFE_PARAMS_WB_GAIN	= 5,
+	CAMSS_IFE_PARAMS_COLOR_CORRECT	= 6,
+	CAMSS_IFE_PARAMS_LSC		= 8,
+	CAMSS_IFE_PARAMS_GAMMA_64	= 9,
+	CAMSS_IFE_PARAMS_GAMMA_256	= 10,
+	CAMSS_IFE_PARAMS_BG_STATS	= 11,
 };
 
-/* Number of entries in each gamma channel LUT. */
-#define CAMSS_GAMMA_LUT_SIZE	256
-
 /**
  * struct camss_params_wb_gain - White Balance gains
  *
@@ -209,4 +236,120 @@ struct camss_params_gamma {
 	 sizeof(struct camss_params_color_correct)	+\
 	 sizeof(struct camss_params_gamma))
 
+/**
+ * struct camss_ife_params_wb_gain - IFE per channel gains configuration.
+ *
+ * @header: block header; @header.type = CAMSS_IFE_PARAMS_WB_GAIN
+ * @r: Red channel gain in U15Q10 format (max 0x7fff, i.e. ~31.999).
+ * @b: Blue channel gain in U15Q10 format (max 0x7fff, i.e. ~31.999).
+ * @g: Green channel gain in U15Q10 format (max 0x7fff, i.e. ~31.999).
+ * @_pad: padding for 64-bit alignment, must be zero.
+ */
+struct camss_ife_params_wb_gain {
+	struct v4l2_isp_params_block_header header;
+	__u16 r;
+	__u16 b;
+	__u16 g;
+	__u16 _pad;
+} __attribute__((aligned(8)));
+
+/**
+ * struct camss_ife_params_color_correct - IFE color correction configuration.
+ *
+ * @header: block header; @header.type = CAMSS_IFE_PARAMS_COLOR_CORRECT
+ * @coeff: 3x3 RGB to RGB matrix. Values are in S12Q8 format.
+ * @offset: Offset values in (S12Q8 format).
+ */
+struct camss_ife_params_color_correct {
+	struct v4l2_isp_params_block_header header;
+	__u16 coeff[3][3];
+	__u16 offset[3];
+} __attribute__((aligned(8)));
+
+/**
+ * struct camss_ife_params_gamma_64 - IFE gamma correction configuration
+ * (64-entry, v10x native).
+ *
+ * Each DMI table word packs two fields so the hardware can
+ * piecewise-linearly interpolate between adjacent LUT entries from a
+ * single DMI read:
+ *   bits [9:0]:   base  - this index's curve value (10-bit unsigned)
+ *   bits [19:10]: delta - signed difference to the *next* index's value
+ *                 (10-bit signed, two's complement)
+ *
+ * @header: block header; @header.type = CAMSS_IFE_PARAMS_GAMMA_64
+ * @table_r: Pre-calculated DMI table values for the red channel.
+ * @table_g: Pre-calculated DMI table values for the green channel.
+ * @table_b: Pre-calculated DMI table values for the blue channel.
+ */
+struct camss_ife_params_gamma_64 {
+	struct v4l2_isp_params_block_header header;
+	__u32 table_r[CAMSS_IFE_GAMMA_64_SIZE];
+	__u32 table_g[CAMSS_IFE_GAMMA_64_SIZE];
+	__u32 table_b[CAMSS_IFE_GAMMA_64_SIZE];
+} __attribute__((aligned(8)));
+
+/**
+ * struct camss_ife_params_gamma_256 - IFE gamma correction configuration
+ * (256-entry, v41x native).
+ *
+ * Same DMI table word packing as &struct camss_ife_params_gamma_64, just
+ * with a 256-entry table matching the wider GLUT DMI address field of this
+ * generation's hardware:
+ *   bits [9:0]:   base  - this index's curve value (10-bit unsigned)
+ *   bits [19:10]: delta - signed difference to the *next* index's value
+ *                 (10-bit signed, two's complement)
+ *
+ * @header: block header; @header.type = CAMSS_IFE_PARAMS_GAMMA_256
+ * @table_r: Pre-calculated DMI table values for the red channel.
+ * @table_g: Pre-calculated DMI table values for the green channel.
+ * @table_b: Pre-calculated DMI table values for the blue channel.
+ */
+struct camss_ife_params_gamma_256 {
+	struct v4l2_isp_params_block_header header;
+	__u32 table_r[CAMSS_IFE_GAMMA_256_SIZE];
+	__u32 table_g[CAMSS_IFE_GAMMA_256_SIZE];
+	__u32 table_b[CAMSS_IFE_GAMMA_256_SIZE];
+} __attribute__((aligned(8)));
+
+/**
+ * struct camss_ife_params_lsc - IFE lens shading configuration.
+ *
+ * @header: block header; @header.type = CAMSS_IFE_PARAMS_LSC
+ * @num_hor_blocks: Number of horizontal blocks.
+ * @num_ver_blocks: Number of vertical blocks.
+ * @_pad: padding for 64-bit alignment, must be zero.
+ * @table_r: Pre-calculated DMI table values for the red channel.
+ * @table_b: Pre-calculated DMI table values for the blue channel.
+ */
+struct camss_ife_params_lsc {
+	struct v4l2_isp_params_block_header header;
+	__u8 num_hor_blocks;
+	__u8 num_ver_blocks;
+	__u16 _pad[3];
+
+	__u32 table_r[CAMSS_IFE_MAX_LSC_BLOCKS];
+	__u32 table_b[CAMSS_IFE_MAX_LSC_BLOCKS];
+} __attribute__((aligned(8)));
+
+/**
+ * struct camss_ife_params_bg_stats - IFE bayer grid 3A statistics enable.
+ *
+ * @header: block header; @header.type = CAMSS_IFE_PARAMS_BG_STATS. Carries
+ *	no payload of its own: the engine is enabled or disabled purely via
+ *	V4L2_ISP_PARAMS_FL_BLOCK_ENABLE/_DISABLE in @header.flags, using the
+ *	grid geometry already configured for the active stream.
+ */
+struct camss_ife_params_bg_stats {
+	struct v4l2_isp_params_block_header header;
+} __attribute__((aligned(8)));
+
+#define CAMSS_IFE_PARAMS_MAX_PAYLOAD				\
+	(sizeof(struct camss_ife_params_wb_gain)		+\
+	 sizeof(struct camss_ife_params_color_correct)		+\
+	 sizeof(struct camss_ife_params_gamma_64)		+\
+	 sizeof(struct camss_ife_params_gamma_256)		+\
+	 sizeof(struct camss_ife_params_lsc)			+\
+	 sizeof(struct camss_ife_params_bg_stats))
+
 #endif /* _UAPI_LINUX_QCOM_CAMSS_CONFIG_H */
-- 
2.34.1


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

* [PATCH 24/45] media: v4l2: add Qualcomm IFE parameters format
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (22 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 23/45] media: uapi: add Qualcomm IFE parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 25/45] media: qcom: camss: sm8250: apply IFE parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (21 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Describe Qualcomm IFE parameter buffers with the QCF0 metadata format.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
 include/uapi/linux/videodev2.h       | 1 +
 2 files changed, 4 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index b7173a258225..581ed9d74b82 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1470,6 +1470,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_META_FMT_QC_IFE_STAT_3A:
 		descr = "Qualcomm IFE 3A Statistics";
 		break;
+	case V4L2_META_FMT_QC_IFE_PARAMS:
+		descr = "Qualcomm IFE 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/videodev2.h b/include/uapi/linux/videodev2.h
index 4a0748b60a15..52ad260e65c8 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 Qualcomm IFE camera sub-system */
 #define V4L2_META_FMT_QC_IFE_STAT_3A	v4l2_fourcc('Q', 'C', 'F', '1') /* IFE 3A Statistics */
+#define V4L2_META_FMT_QC_IFE_PARAMS	v4l2_fourcc('Q', 'C', 'F', '0') /* IFE 3A Parameters */
 
 /* Vendor specific - used for C3_ISP */
 #define V4L2_META_FMT_C3ISP_PARAMS	v4l2_fourcc('C', '3', 'P', 'M') /* Amlogic C3 ISP Parameters */
-- 
2.34.1


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

* [PATCH 25/45] media: qcom: camss: sm8250: apply IFE parameters
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (23 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 24/45] media: v4l2: add Qualcomm IFE parameters format Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 26/45] dt-bindings: media: qcom,sa8775p-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (20 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Apply per-frame WB, CCM, LSC, gamma, and Bayer grid settings in
the SM8250 IFE module backend.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Kconfig     |   1 +
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-mod-v10x.c  | 340 ++++++++++++++++++
 .../platform/qcom/camss/camss-ife-pads.c      |  20 ++
 .../platform/qcom/camss/camss-ife-params.c    |  74 ++++
 .../platform/qcom/camss/camss-ife-params.h    |  75 ++++
 drivers/media/platform/qcom/camss/camss-ife.c | 129 ++++++-
 drivers/media/platform/qcom/camss/camss-ife.h |   2 +
 .../media/platform/qcom/camss/camss-video.c   |  73 +++-
 .../media/platform/qcom/camss/camss-video.h   |   1 +
 10 files changed, 701 insertions(+), 15 deletions(-)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-params.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-params.h

diff --git a/drivers/media/platform/qcom/camss/Kconfig b/drivers/media/platform/qcom/camss/Kconfig
index 414dfd3d0ece..d8353b449738 100644
--- a/drivers/media/platform/qcom/camss/Kconfig
+++ b/drivers/media/platform/qcom/camss/Kconfig
@@ -7,5 +7,6 @@ config VIDEO_QCOM_CAMSS
 	select VIDEO_V4L2_SUBDEV_API
 	select VIDEOBUF2_DMA_SG
 	select V4L2_FWNODE
+	select V4L2_ISP
 
 source "drivers/media/platform/qcom/camss/ope/Kconfig"
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 057c171e08b9..500b056a906c 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -19,6 +19,7 @@ qcom-camss-objs += \
 		camss-ife-camif-v10x.o \
 		camss-ife-lite-top-v10x.o \
 		camss-ife-mod-v10x.o \
+		camss-ife-params.o \
 		camss-ife-scale-v10x.o \
 		camss-ife-top-v20x.o \
 		camss-ife-pads.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
index ebc97203595f..ecbd8b23faf8 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v10x.c
@@ -5,19 +5,40 @@
 
 #include <linux/bitfield.h>
 #include <linux/io.h>
+#include <linux/slab.h>
 
+#include <linux/qcom-camss-config.h>
+#include <media/v4l2-isp.h>
+
+#include "camss.h"
 #include "camss-ife.h"
+#include "camss-ife-params.h"
 
 /* Module base offsets and register offsets */
 #define IFE_MOD_V10X_DEMOSAIC_OFFSET	(0x0ACB7800 - 0x0ACB4000)
 #define IFE_MOD_V10X_XFORM_OFFSET	(0x0ACB8000 - 0x0ACB4000)
 #define IFE_MOD_V10X_AWB_BG_OFFSET	0x8400
+#define IFE_MOD_V10X_LSC_OFFSET		0x3600
+#define IFE_MOD_V10X_GLUT_OFFSET	0x3e00
+#define IFE_MOD_V10X_CCM_OFFSET		0x3a00
 
 /* Demosaic registers */
 #define IFE_MOD_V10X_DEM_MODULE_CFG	0x00000060
 #define IFE_MOD_V10X_DEM_WB_GAIN_CFG_0	0x00000068
 #define IFE_MOD_V10X_DEM_WB_GAIN_CFG_1	0x0000006c
 
+/* CCM registers */
+#define IFE_MOD_V10X_CCM_MODULE_CFG	0x00000060
+#define IFE_MOD_V10X_CCM_COEFF_A_CFG_0	0x00000068
+#define IFE_MOD_V10X_CCM_COEFF_A_CFG_1	0x0000006c
+#define IFE_MOD_V10X_CCM_COEFF_B_CFG_0	0x00000070
+#define IFE_MOD_V10X_CCM_COEFF_B_CFG_1	0x00000074
+#define IFE_MOD_V10X_CCM_COEFF_C_CFG_0	0x00000078
+#define IFE_MOD_V10X_CCM_COEFF_C_CFG_1	0x0000007c
+#define IFE_MOD_V10X_CCM_OFFSET_K_CFG_0	0x00000080
+#define IFE_MOD_V10X_CCM_OFFSET_K_CFG_1	0x00000084
+#define IFE_MOD_V10X_CCM_SHIFT_M_CFG		0x00000088
+
 /* Color transform register */
 #define IFE_MOD_V10X_XFORM_MODULE_CFG	0x00000060
 
@@ -32,6 +53,52 @@
 #define IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_0	0x00000088
 #define IFE_MOD_V10X_AWB_BG_LO_THRESH_CFG_1	0x0000008c
 
+/* LSC registers */
+#define IFE_MOD_V10X_LSC_MODULE_CFG	0x00000060
+#define IFE_MOD_V10X_LSC_DMI_CFG	0x00000008
+#define IFE_MOD_V10X_LSC_CFG_0		0x00000068
+#define IFE_MOD_V10X_LSC_CFG_1		0x0000006c
+#define IFE_MOD_V10X_LSC_CFG_2		0x00000070
+#define IFE_MOD_V10X_LSC_CFG_3		0x00000074
+
+/* Gamma LUT (GLUT) registers */
+#define IFE_MOD_V10X_GLUT_MODULE_CFG	0x00000060
+#define IFE_MOD_V10X_GLUT_DMI_CFG	0x00000008
+
+/* Module configuration bitfields */
+#define IFE_MOD_V10X_CCM_CFG_EN		BIT(0)
+#define IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_1		GENMASK(27, 16)
+#define IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_0		GENMASK(11, 0)
+#define IFE_MOD_V10X_CCM_COEFF_CFG1_MATRIX_2		GENMASK(11, 0)
+#define IFE_MOD_V10X_CCM_OFFSET_CFG0_K1		GENMASK(28, 16)
+#define IFE_MOD_V10X_CCM_OFFSET_CFG0_K0		GENMASK(12, 0)
+#define IFE_MOD_V10X_CCM_OFFSET_CFG1_K2		GENMASK(12, 0)
+#define IFE_MOD_V10X_CCM_SHIFT_M_PARAM			GENMASK(1, 0)
+/* M_PARAM select: 0=Q7, 1=Q8, 2=Q9, 3=Q10. UAPI documents S12Q8. */
+#define IFE_MOD_V10X_CCM_SHIFT_M_PARAM_Q8		1
+
+/* LSC bitfields */
+#define IFE_MOD_V10X_LSC_LUT_SEL_NONE		0x0
+#define IFE_MOD_V10X_LSC_LUT_SEL_RED		0x1
+#define IFE_MOD_V10X_LSC_LUT_SEL_BLUE		0x2
+#define IFE_MOD_V10X_LSC_NUM_BLOCKS_H		GENMASK(3, 0)
+#define IFE_MOD_V10X_LSC_NUM_BLOCKS_V		GENMASK(7, 4)
+#define IFE_MOD_V10X_LSC_BLOCK_WIDTH		GENMASK(10, 0)
+#define IFE_MOD_V10X_LSC_BLOCK_HEIGHT		GENMASK(25, 16)
+#define IFE_MOD_V10X_LSC_INV_SUBBLOCK_Q20	20
+#define IFE_MOD_V10X_LSC_INV_SUBBLOCK		GENMASK(28, 12)
+#define IFE_MOD_V10X_LSC_INIT_SUBBLOCK_Y	GENMASK(30, 28)
+#define IFE_MOD_V10X_LSC_NUM_SUBBLOCKS		GENMASK(13, 12)
+#define IFE_MOD_V10X_LSC_CFG_EN			BIT(0)
+#define IFE_MOD_V10X_LSC_INTP_FACTOR		1
+
+/* Gamma LUT (GLUT) bitfields */
+#define IFE_MOD_V10X_GLUT_LUT_SEL_R		0x3
+#define IFE_MOD_V10X_GLUT_LUT_SEL_G		0x1
+#define IFE_MOD_V10X_GLUT_LUT_SEL_B		0x2
+#define IFE_MOD_V10X_GLUT_LUT_SEL_NONE		0x0
+#define IFE_MOD_V10X_GLUT_CFG_EN		BIT(0)
+
 /* Demosaic bitfields */
 #define IFE_MOD_V10X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN	BIT(15)
 #define IFE_MOD_V10X_DEMOSAIC_CFG_DYN_G_CLAMP_EN	BIT(14)
@@ -211,16 +278,288 @@ static void mod_v10x_awb_bg_disable(void __iomem *base)
 	writel_relaxed(val, mod_base + IFE_MOD_V10X_AWB_BG_MODULE_CFG);
 }
 
+static void mod_v10x_dmi_write(void __iomem *dmi_base, u32 sel,
+			       const u32 *table, size_t count)
+{
+	size_t i;
+
+	writel_relaxed(sel, dmi_base + 4);
+	/* The DMI port advances its LUT index after each write. */
+	for (i = 0; i < count; i++)
+		writel_relaxed(table[i], dmi_base + 8);
+}
+
+static int mod_v10x_lsc_apply(struct ife_pp_ctx *ctx,
+			      const struct camss_ife_params_lsc *cfg, bool enable)
+{
+	void __iomem *top = ctx->base;
+	void __iomem *base = top + IFE_MOD_V10X_LSC_OFFSET;
+	void __iomem *dmi = top + IFE_MOD_V10X_LSC_OFFSET + IFE_MOD_V10X_LSC_DMI_CFG;
+	u32 subblock_w;
+	u32 subblock_h;
+	u32 num_h = cfg->num_hor_blocks;
+	u32 num_v = cfg->num_ver_blocks;
+	u32 val;
+
+	if (enable && (num_h < 2 || num_h > 17 || num_v < 2 || num_v > 13))
+		return -EINVAL;
+
+	val = readl_relaxed(base + IFE_MOD_V10X_LSC_MODULE_CFG);
+	if (enable)
+		val |= IFE_MOD_V10X_LSC_CFG_EN |
+		       FIELD_PREP(IFE_MOD_V10X_LSC_INIT_SUBBLOCK_Y, 0) |
+		       FIELD_PREP(IFE_MOD_V10X_LSC_NUM_SUBBLOCKS,
+				  IFE_MOD_V10X_LSC_INTP_FACTOR);
+	else
+		val &= ~IFE_MOD_V10X_LSC_CFG_EN;
+	writel_relaxed(val, base + IFE_MOD_V10X_LSC_MODULE_CFG);
+
+	if (!enable)
+		return 0;
+
+	val = FIELD_PREP(IFE_MOD_V10X_LSC_NUM_BLOCKS_H, num_h - 2) |
+	      FIELD_PREP(IFE_MOD_V10X_LSC_NUM_BLOCKS_V, num_v - 2);
+	writel_relaxed(val, base + IFE_MOD_V10X_LSC_CFG_0);
+
+	val = FIELD_PREP(IFE_MOD_V10X_LSC_BLOCK_WIDTH, (ctx->in_width / num_h) - 1) |
+	      FIELD_PREP(IFE_MOD_V10X_LSC_BLOCK_HEIGHT, (ctx->in_height / num_v) - 1);
+	writel_relaxed(val, base + IFE_MOD_V10X_LSC_CFG_1);
+
+	/* Inverse subblock width, Q20 */
+	subblock_w = (ctx->in_width / 4) / (num_h / 2);
+	val = subblock_w |
+	      FIELD_PREP(IFE_MOD_V10X_LSC_INV_SUBBLOCK,
+			 (1 << IFE_MOD_V10X_LSC_INV_SUBBLOCK_Q20) / (subblock_w + 1));
+	writel_relaxed(val, base + IFE_MOD_V10X_LSC_CFG_2);
+
+	/* Inverse subblock height, Q20 */
+	subblock_h = (ctx->in_height / 4) / (num_v / 2);
+	val = subblock_h |
+	      FIELD_PREP(IFE_MOD_V10X_LSC_INV_SUBBLOCK,
+			 (1 << IFE_MOD_V10X_LSC_INV_SUBBLOCK_Q20) / (subblock_h + 1));
+	writel_relaxed(val, base + IFE_MOD_V10X_LSC_CFG_3);
+
+	mod_v10x_dmi_write(dmi, IFE_MOD_V10X_LSC_LUT_SEL_RED,
+			   cfg->table_r, CAMSS_IFE_MAX_LSC_BLOCKS);
+	mod_v10x_dmi_write(dmi, IFE_MOD_V10X_LSC_LUT_SEL_BLUE,
+			   cfg->table_b, CAMSS_IFE_MAX_LSC_BLOCKS);
+
+	/*
+	 * The last table's auto-incremented DMI_DATA writes only land in LUT
+	 * RAM once DMI_LUT_CFG.LUT_SEL changes value again - without this,
+	 * the Blue LUT silently never commits.
+	 */
+	writel_relaxed(IFE_MOD_V10X_LSC_LUT_SEL_NONE, dmi + 4);
+
+	return 0;
+}
+
+static void mod_v10x_gamma_apply(void __iomem *top,
+				 const struct camss_ife_params_gamma_64 *cfg, bool enable)
+{
+	void __iomem *base = top + IFE_MOD_V10X_GLUT_OFFSET;
+	void __iomem *dmi = top + IFE_MOD_V10X_GLUT_OFFSET + IFE_MOD_V10X_GLUT_DMI_CFG;
+	u32 val;
+
+	val = readl_relaxed(base + IFE_MOD_V10X_GLUT_MODULE_CFG);
+	if (enable)
+		val |= IFE_MOD_V10X_GLUT_CFG_EN;
+	else
+		val &= ~IFE_MOD_V10X_GLUT_CFG_EN;
+	writel_relaxed(val, base + IFE_MOD_V10X_GLUT_MODULE_CFG);
+
+	if (!enable)
+		return;
+
+	mod_v10x_dmi_write(dmi, IFE_MOD_V10X_GLUT_LUT_SEL_R,
+			   cfg->table_r, CAMSS_IFE_GAMMA_64_SIZE);
+	mod_v10x_dmi_write(dmi, IFE_MOD_V10X_GLUT_LUT_SEL_G,
+			   cfg->table_g, CAMSS_IFE_GAMMA_64_SIZE);
+	mod_v10x_dmi_write(dmi, IFE_MOD_V10X_GLUT_LUT_SEL_B,
+			   cfg->table_b, CAMSS_IFE_GAMMA_64_SIZE);
+
+	/*
+	 * The last table's auto-incremented DMI_DATA writes only land in LUT
+	 * RAM once DMI_LUT_CFG.LUT_SEL changes value again - without this,
+	 * the final (B) table silently never commits and GLUT reads back
+	 * black on that channel. Deselect the LUT once more to flush it.
+	 */
+	writel_relaxed(IFE_MOD_V10X_GLUT_LUT_SEL_NONE, dmi + 4);
+}
+
+static void mod_v10x_wb_apply(void __iomem *top,
+			      const struct camss_ife_params_wb_gain *cfg)
+{
+	void __iomem *base = top + IFE_MOD_V10X_DEMOSAIC_OFFSET;
+	u32 val;
+
+	val = FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_B_GAIN, cfg->b) |
+	      FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG0_G_GAIN, cfg->g);
+	writel_relaxed(val, base + IFE_MOD_V10X_DEM_WB_GAIN_CFG_0);
+
+	val = FIELD_PREP(IFE_MOD_V10X_DEMOSAIC_WB_GAIN_CFG1_R_GAIN, cfg->r);
+	writel_relaxed(val, base + IFE_MOD_V10X_DEM_WB_GAIN_CFG_1);
+}
+
+static void mod_v10x_ccm_apply(void __iomem *top,
+			       const struct camss_ife_params_color_correct *cfg, bool enable)
+{
+	void __iomem *base = top + IFE_MOD_V10X_CCM_OFFSET;
+	u32 val;
+
+	val = readl_relaxed(base + IFE_MOD_V10X_CCM_MODULE_CFG);
+	if (enable)
+		val |= IFE_MOD_V10X_CCM_CFG_EN;
+	else
+		val &= ~IFE_MOD_V10X_CCM_CFG_EN;
+	writel_relaxed(val, base + IFE_MOD_V10X_CCM_MODULE_CFG);
+
+	if (enable) {
+		/*
+		 * Hardware coefficients/offsets are Q-format selectable via
+		 * SHIFT_M_CFG.M_PARAM (0=Q7, 1=Q8, 2=Q9, 3=Q10); the UAPI
+		 * documents S12Q8, so force Q8 here to match.
+		 */
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_SHIFT_M_PARAM,
+					  IFE_MOD_V10X_CCM_SHIFT_M_PARAM_Q8),
+			       base + IFE_MOD_V10X_CCM_SHIFT_M_CFG);
+
+		/*
+		 * HW banks A/B/C each multiply one fixed input channel
+		 * (A=G_in, B=B_in, C=R_in), and HW output indices 0/1/2 are
+		 * also G/B/R, not R/G/B (confirmed via TEST_BUS_CTRL.
+		 * TEST_BUS_SEL, which taps "G-channel"/"B-channel"/
+		 * "R-channel" at indices 0/1/2): "Out_ch0(G)=A0*G_in+B0*B_in
+		 * +C0*R_in+K0; Out_ch1(B)=A1*G_in+B1*B_in+C1*R_in+K1;
+		 * Out_ch2(R)=A2*G_in+B2*B_in+C2*R_in+K2". The UAPI's
+		 * coeff[out][in]/offset[out] are R,G,B-indexed on both axes
+		 * (0=R,1=G,2=B). Solving for A/B/C/K from the UAPI matrix:
+		 * bank X's slot N must take UAPI row (N+1)%3 (G,B,R output
+		 * order) and the column matching that bank's input channel.
+		 */
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_0, cfg->coeff[1][1]) |
+			       FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_1, cfg->coeff[2][1]),
+			       base + IFE_MOD_V10X_CCM_COEFF_A_CFG_0);
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG1_MATRIX_2, cfg->coeff[0][1]),
+			       base + IFE_MOD_V10X_CCM_COEFF_A_CFG_1);
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_0, cfg->coeff[1][2]) |
+			       FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_1, cfg->coeff[2][2]),
+			       base + IFE_MOD_V10X_CCM_COEFF_B_CFG_0);
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG1_MATRIX_2, cfg->coeff[0][2]),
+			       base + IFE_MOD_V10X_CCM_COEFF_B_CFG_1);
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_0, cfg->coeff[1][0]) |
+			       FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG0_MATRIX_1, cfg->coeff[2][0]),
+			       base + IFE_MOD_V10X_CCM_COEFF_C_CFG_0);
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_COEFF_CFG1_MATRIX_2, cfg->coeff[0][0]),
+			       base + IFE_MOD_V10X_CCM_COEFF_C_CFG_1);
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_OFFSET_CFG0_K0, cfg->offset[1]) |
+			       FIELD_PREP(IFE_MOD_V10X_CCM_OFFSET_CFG0_K1, cfg->offset[2]),
+			       base + IFE_MOD_V10X_CCM_OFFSET_K_CFG_0);
+		writel_relaxed(FIELD_PREP(IFE_MOD_V10X_CCM_OFFSET_CFG1_K2, cfg->offset[0]),
+			       base + IFE_MOD_V10X_CCM_OFFSET_K_CFG_1);
+	}
+}
+
+static void mod_v10x_params_apply_wb(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+
+	mod_v10x_wb_apply(ctx->base, &block->wb_gain);
+}
+
+static void mod_v10x_params_apply_ccm(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+
+	mod_v10x_ccm_apply(ctx->base, &block->color_correct, enable);
+}
+
+static void mod_v10x_params_apply_gamma(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+
+	mod_v10x_gamma_apply(ctx->base, &block->gamma_64, enable);
+}
+
+static void mod_v10x_params_apply_lsc(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+	int ret;
+
+	ret = mod_v10x_lsc_apply(ctx, &block->lsc, enable);
+	if (ret)
+		dev_dbg(ctx->dev, "LSC apply failed: %d\n", ret);
+}
+
+static void mod_v10x_params_apply_bg_stats(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+
+	if (enable)
+		mod_v10x_awb_bg_enable(ctx->base);
+	else
+		mod_v10x_awb_bg_disable(ctx->base);
+}
+
+static const struct v4l2_isp_params_block_type_info mod_v10x_params_type_info[] = {
+	[CAMSS_IFE_PARAMS_WB_GAIN] = {
+		.size = sizeof(struct camss_ife_params_wb_gain),
+	},
+	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = {
+		.size = sizeof(struct camss_ife_params_color_correct),
+	},
+	[CAMSS_IFE_PARAMS_GAMMA_64] = {
+		.size = sizeof(struct camss_ife_params_gamma_64),
+	},
+	[CAMSS_IFE_PARAMS_LSC] = {
+		.size = sizeof(struct camss_ife_params_lsc),
+	},
+	[CAMSS_IFE_PARAMS_BG_STATS] = {
+		.size = sizeof(struct camss_ife_params_bg_stats),
+	},
+};
+
+static const camss_ife_params_handler_fn mod_v10x_params_handlers[] = {
+	[CAMSS_IFE_PARAMS_WB_GAIN]       = mod_v10x_params_apply_wb,
+	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = mod_v10x_params_apply_ccm,
+	[CAMSS_IFE_PARAMS_GAMMA_64]      = mod_v10x_params_apply_gamma,
+	[CAMSS_IFE_PARAMS_LSC]           = mod_v10x_params_apply_lsc,
+	[CAMSS_IFE_PARAMS_BG_STATS]      = mod_v10x_params_apply_bg_stats,
+};
+
+static_assert(ARRAY_SIZE(mod_v10x_params_handlers) ==
+	      ARRAY_SIZE(mod_v10x_params_type_info));
+
+static int mod_v10x_modules_apply_params(struct ife_pp_ctx *ctx,
+					 struct vb2_buffer *vb)
+{
+	return camss_ife_params_apply(ctx->dev, vb, ctx->params_scratch,
+				      CAMSS_IFE_PARAMS_MAX_PAYLOAD,
+				      mod_v10x_params_type_info,
+				      mod_v10x_params_handlers,
+				      ARRAY_SIZE(mod_v10x_params_handlers),
+				      ctx);
+}
+
 static void mod_v10x_modules_acquire(struct ife_device *ife,
 				     struct ife_pp_ctx *ctx,
 				     void __iomem *base)
 {
+	ctx->dev = ife->camss->dev;
 	ctx->base = base;
+	ctx->params_scratch = devm_kzalloc(ctx->dev,
+					   v4l2_isp_buffer_size(CAMSS_IFE_PARAMS_MAX_PAYLOAD),
+					   GFP_KERNEL);
 }
 
 static void mod_v10x_modules_release(struct ife_pp_ctx *ctx)
 {
 	ctx->base = NULL;
+	ctx->params_scratch = NULL;
 }
 
 static void mod_v10x_modules_config(struct ife_pp_ctx *ctx,
@@ -260,4 +599,5 @@ const struct camss_mod_ops ife_mod_v10x_ops = {
 	.config		= mod_v10x_modules_config,
 	.enable		= mod_v10x_modules_enable,
 	.disable	= mod_v10x_modules_disable,
+	.apply_params	= mod_v10x_modules_apply_params,
 };
diff --git a/drivers/media/platform/qcom/camss/camss-ife-pads.c b/drivers/media/platform/qcom/camss/camss-ife-pads.c
index bd9ac1a91d73..7a4e41c9660b 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-pads.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-pads.c
@@ -3,8 +3,10 @@
  * Copyright (c) 2023 Qualcomm Technologies, Inc.
  */
 
+#include <linux/qcom-camss-config.h>
 #include <linux/qcom-camss-stats.h>
 #include <linux/videodev2.h>
+#include <media/v4l2-isp.h>
 #include <media/v4l2-mediabus.h>
 
 #include "camss-format.h"
@@ -69,6 +71,11 @@ static const struct camss_format_info ife_stats_fmts_v20x[] = {
 	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
 };
 
+static const struct camss_format_info ife_params_fmts_v20x[] = {
+	{ MEDIA_BUS_FMT_METADATA_FIXED, 8, V4L2_META_FMT_QC_IFE_PARAMS, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+};
+
 static const struct ife_pad_limits ife_vid_limits_v20x = {
 	.min_width	= 320,
 	.min_height	= 240,
@@ -327,6 +334,19 @@ static const struct ife_pad_desc ife_pad_desc_v20x[] = {
 		.num_wms = 1,
 		.wm[0] = 15,
 	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SINK,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_params_fmts_v20x),
+			.formats = ife_params_fmts_v20x,
+		},
+		.limits = &ife_meta_limits_v20x,
+		.video_name = "params",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.meta_dataformat = V4L2_META_FMT_QC_IFE_PARAMS,
+		.meta_buffersize = PAGE_ALIGN(v4l2_isp_buffer_size(CAMSS_IFE_PARAMS_MAX_PAYLOAD)),
+	},
 };
 
 const struct ife_pads_descriptors ife_lite_pads_v20x = {
diff --git a/drivers/media/platform/qcom/camss/camss-ife-params.c b/drivers/media/platform/qcom/camss/camss-ife-params.c
new file mode 100644
index 000000000000..ddd2911761fb
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-params.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * camss-ife-params.c
+ *
+ * Qualcomm MSM Camera Subsystem - IFE ISP parameter buffer parser
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <media/v4l2-isp.h>
+#include <media/videobuf2-core.h>
+
+#include "camss-ife-params.h"
+
+int camss_ife_params_apply(struct device *dev,
+			   struct vb2_buffer *vb,
+			   struct v4l2_isp_params_buffer *scratch,
+			   size_t max_size,
+			   const struct v4l2_isp_params_block_type_info *type_info,
+			   const camss_ife_params_handler_fn *handlers,
+			   unsigned int num_handlers,
+			   void *priv)
+{
+	const struct v4l2_isp_params_buffer *buf = scratch;
+	const void *src;
+	unsigned int remaining;
+	unsigned int offset = 0;
+	int ret;
+
+	ret = v4l2_isp_params_validate_buffer_size(dev, vb, v4l2_isp_buffer_size(max_size));
+	if (ret)
+		return ret;
+
+	src = vb2_plane_vaddr(vb, 0);
+	if (!src) {
+		dev_dbg(dev, "params: buffer has no kernel mapping\n");
+		return -EFAULT;
+	}
+
+	/*
+	 * The payload stays mapped to userspace while the buffer is queued,
+	 * so it must not be validated in place: copy it into kernel-only
+	 * memory first, then work exclusively on the copy. See the
+	 * v4l2_isp_params_validate_buffer() documentation.
+	 */
+	memcpy(scratch, src, vb2_get_plane_payload(vb, 0));
+
+	ret = v4l2_isp_params_validate_buffer(dev, vb, buf, type_info, num_handlers);
+	if (ret)
+		return ret;
+
+	remaining = buf->data_size;
+
+	while (remaining >= sizeof(struct v4l2_isp_params_block_header)) {
+		const union camss_ife_params_block *block =
+			(const union camss_ife_params_block *)&buf->data[offset];
+		u16 type = block->header.type;
+		u32 bsize = block->header.size;
+
+		if (type < num_handlers && handlers[type])
+			handlers[type](priv, block);
+		else
+			dev_dbg(dev, "params: no handler for block type %u\n", type);
+
+		offset += bsize;
+		remaining -= bsize;
+	}
+
+	return 0;
+}
diff --git a/drivers/media/platform/qcom/camss/camss-ife-params.h b/drivers/media/platform/qcom/camss/camss-ife-params.h
new file mode 100644
index 000000000000..50277ec01056
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-params.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * camss-ife-params.h
+ *
+ * Qualcomm MSM Camera Subsystem - IFE ISP parameter buffer parser
+ *
+ * Wraps the generic v4l2_isp_params_validate_buffer() validation and adds a
+ * dispatch layer: after validation each block is forwarded to a
+ * backend-supplied handler.
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#ifndef _CAMSS_IFE_PARAMS_H_
+#define _CAMSS_IFE_PARAMS_H_
+
+#include <linux/types.h>
+#include <media/v4l2-isp.h>
+
+#include <linux/qcom-camss-config.h>
+
+struct device;
+struct vb2_buffer;
+
+/**
+ * union camss_ife_params_block - Generalisation of an IFE parameter block
+ *
+ * Lets handlers access the shared header first, check its type, then read
+ * the block-specific fields through the matching union member without an
+ * explicit cast.
+ */
+union camss_ife_params_block {
+	struct v4l2_isp_params_block_header header;
+	struct camss_ife_params_wb_gain wb_gain;
+	struct camss_ife_params_color_correct color_correct;
+	struct camss_ife_params_gamma_64 gamma_64;
+	struct camss_ife_params_gamma_256 gamma_256;
+	struct camss_ife_params_lsc lsc;
+	struct camss_ife_params_bg_stats bg_stats;
+};
+
+typedef void (*camss_ife_params_handler_fn)(void *priv,
+					    const union camss_ife_params_block *block);
+
+/**
+ * camss_ife_params_apply - validate and dispatch an IFE params buffer
+ *
+ * @dev:          device for error logging
+ * @vb:           the vb2 buffer holding the params payload
+ * @scratch:      kernel-only bounce buffer, at least
+ *                v4l2_isp_buffer_size(@max_size) bytes
+ * @max_size:     maximum total size of the params blocks (excluding the
+ *                buffer header), e.g. CAMSS_IFE_PARAMS_MAX_PAYLOAD
+ * @type_info:    per-block-type validation info, indexed by block type
+ * @handlers:     per-block-type handlers, indexed by block type
+ * @num_handlers: number of entries in @type_info and @handlers
+ * @priv:         opaque pointer forwarded to each handler
+ *
+ * Copies the buffer payload into @scratch, validates it with
+ * v4l2_isp_params_validate_buffer_size() and v4l2_isp_params_validate_buffer(),
+ * then walks the validated block stream dispatching each block to its
+ * handler. Validation and dispatch operate only on @scratch, never on the
+ * userspace-visible mapping.
+ *
+ * Returns 0 on success, negative errno on validation failure.
+ */
+int camss_ife_params_apply(struct device *dev,
+			   struct vb2_buffer *vb,
+			   struct v4l2_isp_params_buffer *scratch,
+			   size_t max_size,
+			   const struct v4l2_isp_params_block_type_info *type_info,
+			   const camss_ife_params_handler_fn *handlers,
+			   unsigned int num_handlers,
+			   void *priv);
+
+#endif /* _CAMSS_IFE_PARAMS_H_ */
diff --git a/drivers/media/platform/qcom/camss/camss-ife.c b/drivers/media/platform/qcom/camss/camss-ife.c
index f7ec86fb7f02..cc5cebfd0862 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.c
+++ b/drivers/media/platform/qcom/camss/camss-ife.c
@@ -71,9 +71,19 @@ static bool is_ife_video_stats(const struct camss_video *video)
 	return video->type == V4L2_BUF_TYPE_META_CAPTURE;
 }
 
+static bool is_params_type(const struct ife_pad_desc *pad)
+{
+	return pad->buf_type == V4L2_BUF_TYPE_META_OUTPUT;
+}
+
 static bool is_meta_type(const struct ife_pad_desc *pad)
 {
-	return is_stats_type(pad);
+	return is_stats_type(pad) || is_params_type(pad);
+}
+
+static bool is_ife_video_params(const struct camss_video *video)
+{
+	return video->type == V4L2_BUF_TYPE_META_OUTPUT;
 }
 
 static void ife_pp_notify_sof_event(struct ife_device *ife, u32 seq_number)
@@ -272,6 +282,53 @@ static void ife_hw_isr_wm_done(struct ife_device *ife, struct ife_video_pad *vid
 		ife_hw_reg_update(vid_pad);
 }
 
+static void ife_hw_isr_pp_epoch0(struct ife_device *ife)
+{
+	const struct ife_pad_desc *pp_dsc = NULL;
+	enum vb2_buffer_state state = VB2_BUF_STATE_ERROR;
+	struct camss_buffer *params_buf;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&ife->bufs_lock, flags);
+
+	params_buf = ife->pending_params;
+	ife->pending_params = NULL;
+
+	for (i = 0; i < ife->npads; i++) {
+		struct ife_video_pad *vid_pad = ife->pads[i].vid_pad;
+
+		if (!vid_pad || !vid_pad->streaming)
+			continue;
+
+		if (is_pix_hw_pipe(ife->pads[i].desc->hw_pipe)) {
+			pp_dsc = ife->pads[i].desc;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&ife->bufs_lock, flags);
+
+	if (!params_buf)
+		return;
+
+	if (ife->res->mod_ops && ife->res->mod_ops->apply_params) {
+		int ret = ife->res->mod_ops->apply_params(&ife->pp_ctx, &params_buf->vb.vb2_buf);
+
+		if (ret) {
+			dev_err_ratelimited(ife->camss->dev,
+					    "Failed to apply IFE params: %d\n", ret);
+		} else {
+			state = VB2_BUF_STATE_DONE;
+		}
+	}
+
+	ife->res->top_ops->reg_update(&ife->top_ctx, pp_dsc);
+
+	params_buf->vb.vb2_buf.timestamp = ktime_get_ns();
+	vb2_buffer_done(&params_buf->vb.vb2_buf, state);
+}
+
 static void ife_hw_isr_buswr(struct ife_device *ife, u32 bus_status)
 {
 	unsigned long flags;
@@ -324,6 +381,9 @@ static irqreturn_t ife_hw_top_isr(int irq, void *dev)
 		ife->camif_data[CAMSS_IFE_HW_PIPE_PP].next_seq++;
 	}
 
+	if (status[IFE_TOP_STATUS_ID1] & IFE_HW_TOP_IRQ1_PP_EPOCH0)
+		ife_hw_isr_pp_epoch0(ife);
+
 	if (status[IFE_TOP_STATUS_ID0] & IFE_HW_TOP_IRQ0_BUSWR) {
 		u32 bus_status = ife->res->buswr_ops->irq_status(&ife->buswr_ctx);
 
@@ -718,7 +778,7 @@ static int ife_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *stat
 	for (i = 0; i < ife->npads; i++) {
 		struct ife_video_pad *vpad = ife->pads[i].vid_pad;
 
-		if (vpad)
+		if (vpad && !is_ife_video_params(&vpad->video))
 			routing.num_routes++;
 	}
 
@@ -739,6 +799,9 @@ static int ife_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *stat
 		if (!vpad)
 			continue;
 
+		if (is_ife_video_params(&vpad->video))
+			continue;
+
 		sid = camss_ife_stream_id_for_pipe(res->num_sink_streams, res->sink_streams,
 						   ife->pads[i].desc->hw_pipe);
 
@@ -1029,12 +1092,11 @@ static int ife_pad_enum_framesizes(struct v4l2_subdev *sd, struct v4l2_subdev_st
 
 	pad = &ife->pads[fse->pad];
 
-	if (is_meta_type(pad->desc)) {
-		if (is_stats_type(pad->desc))
-			return -ENOTTY;
-
+	if (is_params_type(pad->desc))
 		return -EINVAL;
-	}
+
+	if (is_stats_type(pad->desc))
+		return -ENOTTY;
 
 	for (i = 0; i < pad->desc->sup_fmts.nformats; i++) {
 		if (pad->desc->sup_fmts.formats[i].code == fse->code)
@@ -1233,6 +1295,9 @@ static int ife_video_start_streaming(struct camss_video *vid,
 {
 	struct media_pad *src_pad;
 
+	if (is_ife_video_params(vid))
+		return 0;
+
 	src_pad = media_pad_remote_pad_first(&vid->vdev.entity.pads[0]);
 	if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity))
 		return -ENOLINK;
@@ -1248,6 +1313,11 @@ static void ife_video_stop_streaming(struct camss_video *vid)
 	struct media_pad *src_pad;
 	int ret;
 
+	if (is_ife_video_params(vid)) {
+		vid->ops->flush_buffers(vid, VB2_BUF_STATE_ERROR);
+		return;
+	}
+
 	src_pad = media_pad_remote_pad_first(&vid->vdev.entity.pads[0]);
 	if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity))
 		return;
@@ -1263,6 +1333,22 @@ static int ife_queue_buffer(struct camss_video *vid, struct camss_buffer *buf)
 {
 	struct ife_video_pad *vid_pad =
 		container_of(vid, struct ife_video_pad, video);
+	struct ife_device *ife = vid_pad->ife;
+
+	if (is_ife_video_params(vid)) {
+		struct camss_buffer *old_buf;
+		unsigned long flags;
+
+		spin_lock_irqsave(&ife->bufs_lock, flags);
+		old_buf = ife->pending_params;
+		ife->pending_params = buf;
+		spin_unlock_irqrestore(&ife->bufs_lock, flags);
+
+		if (old_buf)
+			vb2_buffer_done(&old_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+
+		return 0;
+	}
 
 	ife_hw_wm_add_pending_buf(vid_pad, buf);
 
@@ -1274,6 +1360,22 @@ static int ife_flush_buffers(struct camss_video *vid, enum vb2_buffer_state stat
 	struct ife_video_pad *vid_pad =
 		container_of(vid, struct ife_video_pad, video);
 
+	if (is_ife_video_params(vid)) {
+		struct ife_device *ife = vid_pad->ife;
+		struct camss_buffer *buf;
+		unsigned long flags;
+
+		spin_lock_irqsave(&ife->bufs_lock, flags);
+		buf = ife->pending_params;
+		ife->pending_params = NULL;
+		spin_unlock_irqrestore(&ife->bufs_lock, flags);
+
+		if (buf)
+			vb2_buffer_done(&buf->vb.vb2_buf, state);
+
+		return 0;
+	}
+
 	if (is_ife_vid_pad_streaming(vid_pad))
 		return -EINVAL;
 
@@ -1610,9 +1712,16 @@ int ife_register_entities(struct ife_device *ife, struct v4l2_device *v4l2_dev)
 		if (!vpad)
 			continue;
 
-		ret = media_create_pad_link(&sd->entity, vpad->pad_index,
-					    &vpad->video.vdev.entity, 0,
-					    pad->desc->link_flags);
+		if (is_ife_video_params(&vpad->video)) {
+			ret = media_create_pad_link(&vpad->video.vdev.entity, 0,
+						    &sd->entity, vpad->pad_index,
+						    pad->desc->link_flags);
+		} else {
+			ret = media_create_pad_link(&sd->entity, vpad->pad_index,
+						    &vpad->video.vdev.entity, 0,
+						    pad->desc->link_flags);
+		}
+
 		if (ret < 0) {
 			dev_err(dev, "Failed to link '%s' and '%s' entities\n", sd->entity.name,
 				vpad->video.vdev.entity.name);
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 168d26d0664b..c163c336fcc3 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -367,6 +367,8 @@ struct ife_device {
 
 	struct camss_ife_pipeline_cfg pipeline_cfg[IFE_CAMIF_HW_PIPES_MAX];
 
+	struct camss_buffer *pending_params;
+
 	struct v4l2_subdev sd;
 	struct device *genpd;
 	struct device_link *genpd_link;
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index b5d78f84d6a1..2d203dc2e4be 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -255,6 +255,20 @@ static int video_prepare_streaming(struct vb2_queue *q)
 	return ret;
 }
 
+static bool video_is_meta_output(const struct camss_video *video)
+{
+	return video->type == V4L2_BUF_TYPE_META_OUTPUT;
+}
+
+static int video_meta_output_link_validate(struct media_link *link)
+{
+	return 0;
+}
+
+static const struct media_entity_operations video_meta_output_media_ops = {
+	.link_validate = video_meta_output_link_validate,
+};
+
 /*
  * video_start_streaming_legacy - Start a legacy subdev pipeline
  * @sink_pad: Video device's own (single) pad
@@ -456,6 +470,7 @@ static int meta_buf_init(struct vb2_buffer *vb)
 		return -EFAULT;
 
 	buffer->addr[0] = sg_dma_address(sgt->sgl);
+	buffer->vaddr = vb2_plane_vaddr(vb, 0);
 
 	return 0;
 }
@@ -475,19 +490,53 @@ static int meta_buf_prepare(struct vb2_buffer *vb)
 	if (vb2_plane_size(vb, 0) < video->active_fmt.fmt.meta.buffersize)
 		return -EINVAL;
 
-	vb2_set_plane_payload(vb, 0, video->active_fmt.fmt.meta.buffersize);
+	/*
+	 * For META_CAPTURE (stats) the hardware always fills the whole
+	 * fixed-size buffer, so force the payload to the full buffersize.
+	 * For META_OUTPUT (params) the payload is a variable-length
+	 * block-list and userspace has already set the correct bytesused
+	 * via VIDIOC_QBUF; don't clobber it.
+	 */
+	if (!video_is_meta_output(video))
+		vb2_set_plane_payload(vb, 0, video->active_fmt.fmt.meta.buffersize);
 
 	return 0;
 }
 
+static int meta_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct camss_video *video = vb2_get_drv_priv(q);
+
+	if (!video_is_meta_output(video))
+		return video_start_streaming(q, count);
+
+	if (!video->ops->start_streaming)
+		return 0;
+
+	return video->ops->start_streaming(video, count);
+}
+
+static void meta_stop_streaming(struct vb2_queue *q)
+{
+	struct camss_video *video = vb2_get_drv_priv(q);
+
+	if (!video_is_meta_output(video)) {
+		video_stop_streaming(q);
+		return;
+	}
+
+	if (video->ops->stop_streaming)
+		video->ops->stop_streaming(video);
+}
+
 static const struct vb2_ops msm_meta_vb2_q_ops = {
 	.queue_setup     = meta_queue_setup,
 	.buf_init        = meta_buf_init,
 	.buf_prepare     = meta_buf_prepare,
 	.buf_queue       = video_buf_queue,
 	.prepare_streaming = video_prepare_streaming,
-	.start_streaming = video_start_streaming,
-	.stop_streaming  = video_stop_streaming,
+	.start_streaming = meta_start_streaming,
+	.stop_streaming  = meta_stop_streaming,
 	.unprepare_streaming = video_unprepare_streaming,
 };
 
@@ -795,9 +844,13 @@ static int meta_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
 static const struct v4l2_ioctl_ops msm_meta_ioctl_ops = {
 	.vidioc_querycap		= video_querycap,
 	.vidioc_enum_fmt_meta_cap	= meta_enum_fmt,
+	.vidioc_enum_fmt_meta_out	= meta_enum_fmt,
 	.vidioc_g_fmt_meta_cap		= meta_try_fmt,
+	.vidioc_g_fmt_meta_out		= meta_try_fmt,
 	.vidioc_s_fmt_meta_cap		= meta_try_fmt,
+	.vidioc_s_fmt_meta_out		= meta_try_fmt,
 	.vidioc_try_fmt_meta_cap	= meta_try_fmt,
+	.vidioc_try_fmt_meta_out	= meta_try_fmt,
 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
 	.vidioc_querybuf		= vb2_ioctl_querybuf,
 	.vidioc_qbuf			= vb2_ioctl_qbuf,
@@ -916,20 +969,24 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
 	q->dev = video->camss->dev;
 	q->lock = &video->q_lock;
 
-	if (video->type == V4L2_BUF_TYPE_META_CAPTURE) {
+	if (video->type == V4L2_BUF_TYPE_META_CAPTURE || video_is_meta_output(video)) {
 		q->ops = &msm_meta_vb2_q_ops;
 		q->io_modes = VB2_DMABUF | VB2_MMAP;
 	} else if (video->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
 		dev_err(v4l2_dev->dev, "Invalid format type\n");
 		goto error_vb2_init;
 	}
+
 	ret = vb2_queue_init(q);
 	if (ret < 0) {
 		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
 		goto error_vb2_init;
 	}
 
-	pad->flags = MEDIA_PAD_FL_SINK;
+	pad->flags = video_is_meta_output(video) ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+	if (video_is_meta_output(video))
+		vdev->entity.ops = &video_meta_output_media_ops;
+
 	ret = media_entity_pads_init(&vdev->entity, 1, pad);
 	if (ret < 0) {
 		dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
@@ -957,6 +1014,12 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
 	vdev->release = msm_video_release;
 	vdev->v4l2_dev = v4l2_dev;
 	vdev->vfl_dir = VFL_DIR_RX;
+	if (video_is_meta_output(video)) {
+		vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING |
+				    V4L2_CAP_IO_MC;
+		vdev->ioctl_ops = &msm_meta_ioctl_ops;
+		vdev->vfl_dir = VFL_DIR_TX;
+	}
 	vdev->queue = &video->vb2_q;
 	vdev->lock = &video->lock;
 	strscpy(vdev->name, name, sizeof(vdev->name));
diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h
index cfee619973ef..a2be135bee24 100644
--- a/drivers/media/platform/qcom/camss/camss-video.h
+++ b/drivers/media/platform/qcom/camss/camss-video.h
@@ -22,6 +22,7 @@
 struct camss_buffer {
 	struct vb2_v4l2_buffer vb;
 	dma_addr_t addr[3];
+	void *vaddr;
 	struct list_head queue;
 };
 
-- 
2.34.1


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

* [PATCH 26/45] dt-bindings: media: qcom,sa8775p-camss: add pixel path variant
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (24 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 25/45] media: qcom: camss: sm8250: apply IFE parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-07  9:49   ` Bryan O'Donoghue
  2026-10-06 22:39 ` [PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (19 subsequent siblings)
  45 siblings, 1 reply; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Document qcom,sa8775p-camss-pix for CAMSS configurations that use IFE
pixel paths. Keep qcom,sa8775p-camss for the legacy VFE resources.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../devicetree/bindings/media/qcom,sa8775p-camss.yaml         | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
index 48f280e99809..9206c7cfe197 100644
--- a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
@@ -14,7 +14,9 @@ description:
 
 properties:
   compatible:
-    const: qcom,sa8775p-camss
+    enum:
+      - qcom,sa8775p-camss-pix
+      - qcom,sa8775p-camss
 
   reg:
     maxItems: 22
-- 
2.34.1


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

* [PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (25 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 26/45] dt-bindings: media: qcom,sa8775p-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-07  6:44   ` Gjorgji Rosikopulos (Consultant)
  2026-10-06 22:39 ` [PATCH 28/45] media: qcom: camss: sa8775p: add IFE scaler v2.3x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (18 subsequent siblings)
  45 siblings, 1 reply; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the IFE BUS v3.1x write engine used by the SA8775P full and lite
pixel paths.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-bus-v31x.c  | 267 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   1 +
 3 files changed, 269 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 500b056a906c..2251b57ba38b 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -16,6 +16,7 @@ qcom-camss-objs += \
 		camss-format.o \
 		camss-ife.o \
 		camss-ife-bus-v20x.o \
+		camss-ife-bus-v31x.o \
 		camss-ife-camif-v10x.o \
 		camss-ife-lite-top-v10x.o \
 		camss-ife-mod-v10x.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c b/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
new file mode 100644
index 000000000000..c3a99bd0f75e
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/bitfield.h>
+
+#include "camss.h"
+#include "camss-ife.h"
+
+/* Block base offsets */
+#define BUSWR_V31X_OFFS		0x400
+#define BUSWR_V31X_LITE_OFFS	0x480
+
+/* Per-client register offset */
+#define BUSWR_V31X_CLIENT_OFFS(n)	((n) * 0x100)
+
+/* Interface interrupt and violation status bits */
+#define BUSWR_V31X_IIF_IRQ_CMD_CLEAR			BIT(0)
+#define BUSWR_V31X_OVERFLOW_ERR_MASK			GENMASK(25, 0)
+#define IS_BUSWR_V31X_OVERFLOW_ERR(s)	\
+	((s) & BUSWR_V31X_OVERFLOW_ERR_MASK)
+#define BUSWR_V31X_VIOLATION_ERR_MASK			GENMASK(25, 0)
+#define IS_BUSWR_V31X_VIOLATION_ERR(s)	\
+	((s) & BUSWR_V31X_VIOLATION_ERR_MASK)
+#define BUSWR_V31X_IMG_SIZE_VIOLATION_ERR_MASK		GENMASK(25, 0)
+#define IS_BUSWR_V31X_IMG_SIZE_VIOLATION_ERR(s)	\
+	((s) & BUSWR_V31X_IMG_SIZE_VIOLATION_ERR_MASK)
+#define BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION	BIT(31)
+#define IS_BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(s)	\
+	((s) & BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION)
+#define BUSWR_V31X_IIF_IRQ_S0_VIOLATION			BIT(30)
+#define IS_BUSWR_V31X_IIF_IRQ_S0_VIOLATION(s)	\
+	((s) & BUSWR_V31X_IIF_IRQ_S0_VIOLATION)
+
+/* Client configuration field values */
+#define BUSWR_V31X_IIF_CGC_OVERRIDE			GENMASK(26, 0)
+#define BUSWR_V31X_CLIENT_CFG_MODE			GENMASK(17, 16)
+#define BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN		BIT(4)
+#define BUSWR_V31X_CLIENT_CFG_EN			BIT(0)
+#define BUSWR_V31X_CLIENT_IMG_CFG0_HEIGHT		GENMASK(31, 16)
+#define BUSWR_V31X_CLIENT_IMG_CFG0_WIDTH		GENMASK(15, 0)
+#define BUSWR_V31X_CLIENT_IMG_CFG2_STRIDE		GENMASK(23, 0)
+#define BUSWR_V31X_CLIENT_PACKER_CFG_ALIGN		BIT(5)
+#define BUSWR_V31X_CLIENT_PACKER_CFG_FORMAT		GENMASK(4, 0)
+#define BUSWR_V31X_PACKER_FMT_PLAIN16_10_BPP		0x5
+#define BUSWR_V31X_PACKER_FMT_PLAIN16_16_BPP		0x8
+#define BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD	GENMASK(4, 0)
+#define BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN	GENMASK(31, 0)
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_PERIOD		GENMASK(4, 0)
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_PATTERN	GENMASK(31, 0)
+
+/* FIFO limit and frame-based RDI defaults */
+#define BUSWR_V31X_IMG_ADDR_FIFO_MAX_DEPTH		2
+#define BUSWR_V31X_RDI_DEFAULT_WIDTH			0xffff
+#define BUSWR_V31X_RDI_DEFAULT_STRIDE			0xffff
+#define BUSWR_V31X_CLIENT_ADDR_STATUS_2_IMAGE_ADDR_FIFO_WORD_CNT	GENMASK(2, 0)
+
+/* Common registers */
+#define BUSWR_V31X_IIF_CGC_OVERRIDE_REG		0x00000008
+#define BUSWR_V31X_WM_TEST_BUS_CTRL_REG		0x000000fc
+#define BUSWR_V31X_IIF_IRQ_MASK_S0		0x00000018
+#define BUSWR_V31X_IIF_IRQ_CLEAR_S0		0x00000020
+#define BUSWR_V31X_IIF_IRQ_STATUS_S0		0x00000028
+#define BUSWR_V31X_IIF_IRQ_CMD_REG		0x00000030
+#define BUSWR_V31X_STATUS_CLEAR_REG		0x00000060
+#define BUSWR_V31X_VIOLATION_STATUS_REG		0x00000064
+#define BUSWR_V31X_OVERFLOW_STATUS_REG		0x00000068
+#define BUSWR_V31X_IMG_SIZE_VIOLATION_REG	0x00000070
+
+/* Per-client registers (relative to client base = module_base + CLIENT_OFFS(n)) */
+#define BUSWR_V31X_CLIENT_CFG_REG		0x00000200
+#define BUSWR_V31X_CLIENT_ADDR_IMAGE_REG	0x00000204
+#define BUSWR_V31X_CLIENT_IMG_CFG0_REG		0x0000020c
+#define BUSWR_V31X_CLIENT_IMG_CFG2_REG		0x00000214
+#define BUSWR_V31X_CLIENT_PACKER_CFG_REG	0x00000218
+#define BUSWR_V31X_CLIENT_IRQ_SS_CFG0_REG	0x00000230
+#define BUSWR_V31X_CLIENT_IRQ_SS_CFG1_REG	0x00000234
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_REG	0x00000238
+#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_REG	0x0000023c
+#define BUSWR_V31X_CLIENT_ADDR_STATUS_0_REG	0x00000270
+#define BUSWR_V31X_CLIENT_ADDR_STATUS_2_REG	0x00000278
+
+static void ife_buswr_31x_acquire(struct ife_device *ife, void __iomem *base)
+{
+	struct ife_buswr_ctx *ctx = &ife->buswr_ctx;
+
+	if (ife->res->type == CAMSS_IFE_SUBDEV_TYPE_IFE_LITE)
+		ctx->base = base + BUSWR_V31X_LITE_OFFS;
+	else
+		ctx->base = base + BUSWR_V31X_OFFS;
+
+	ctx->dev = ife->camss->dev;
+}
+
+static void ife_buswr_31x_release(struct ife_buswr_ctx *ctx)
+{
+	ctx->base = NULL;
+}
+
+static void ife_buswr_31x_wm_config(struct ife_buswr_ctx *ctx, const u8 wm,
+				    const struct camss_ife_wm_config *cfg)
+{
+	void __iomem *base = ctx->base;
+	bool align = cfg->packing_fmt >= BUSWR_V31X_PACKER_FMT_PLAIN16_10_BPP &&
+		     cfg->packing_fmt <= BUSWR_V31X_PACKER_FMT_PLAIN16_16_BPP;
+	u32 pattern = 1;
+	u32 period = 0;
+	u32 bytes = cfg->bytes;
+	u32 stride = cfg->stride;
+	u32 lines = cfg->lines;
+	u32 rval;
+
+	if (cfg->mode_config == BUSWR_CLIENT_CFG_MODE_FRAME_BASED) {
+		bytes = BUSWR_V31X_RDI_DEFAULT_WIDTH;
+		lines = 0;
+		stride = BUSWR_V31X_RDI_DEFAULT_STRIDE;
+	}
+
+	writel_relaxed(BUSWR_V31X_IIF_CGC_OVERRIDE,
+		       base + BUSWR_V31X_IIF_CGC_OVERRIDE_REG);
+	writel_relaxed(0, base + BUSWR_V31X_WM_TEST_BUS_CTRL_REG);
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_CFG_MODE, cfg->mode_config);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_PACKER_CFG_FORMAT, cfg->packing_fmt) |
+	       FIELD_PREP(BUSWR_V31X_CLIENT_PACKER_CFG_ALIGN, align);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_PACKER_CFG_REG +
+		       BUSWR_V31X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG0_WIDTH, bytes) |
+	       FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG0_HEIGHT, lines);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IMG_CFG0_REG +
+		       BUSWR_V31X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG2_STRIDE, stride);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IMG_CFG2_REG +
+		       BUSWR_V31X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD, period);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IRQ_SS_CFG0_REG +
+		       BUSWR_V31X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN, pattern);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IRQ_SS_CFG1_REG +
+		       BUSWR_V31X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_PERIOD, period);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_REG +
+		       BUSWR_V31X_CLIENT_OFFS(wm));
+
+	rval = FIELD_PREP(BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_PATTERN, pattern);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_REG +
+		       BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static void ife_buswr_31x_wm_enable(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	void __iomem *base = ctx->base;
+	u32 rval;
+
+	rval = readl_relaxed(base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+	rval |= BUSWR_V31X_CLIENT_CFG_EN | BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN;
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static void ife_buswr_31x_wm_disable(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	void __iomem *base = ctx->base;
+	u32 rval;
+
+	rval = readl_relaxed(base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+	rval &= ~(BUSWR_V31X_CLIENT_CFG_EN | BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN);
+	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static bool ife_buswr_31x_is_fifo_full(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	u32 rval = readl_relaxed(ctx->base + BUSWR_V31X_CLIENT_OFFS(wm) +
+				 BUSWR_V31X_CLIENT_ADDR_STATUS_2_REG);
+
+	rval = FIELD_GET(BUSWR_V31X_CLIENT_ADDR_STATUS_2_IMAGE_ADDR_FIFO_WORD_CNT, rval);
+
+	return rval >= BUSWR_V31X_IMG_ADDR_FIFO_MAX_DEPTH;
+}
+
+static void ife_buswr_31x_queue_buf(struct ife_buswr_ctx *ctx, const u8 num_wms, const u8 *wm,
+				    dma_addr_t *addr)
+{
+	int i;
+
+	for (i = 0; i < num_wms; i++) {
+		if (addr[i])
+			writel_relaxed(addr[i], ctx->base + BUSWR_V31X_CLIENT_ADDR_IMAGE_REG +
+				       BUSWR_V31X_CLIENT_OFFS(wm[i]));
+	}
+}
+
+static u32 ife_buswr_31x_consumed_addr(struct ife_buswr_ctx *ctx, u8 wm)
+{
+	return readl_relaxed(ctx->base + BUSWR_V31X_CLIENT_ADDR_STATUS_0_REG +
+			     BUSWR_V31X_CLIENT_OFFS(wm));
+}
+
+static u32 ife_buswr_31x_irq_status(struct ife_buswr_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+	u32 bus_stat;
+	u32 iif_stat;
+
+	bus_stat = readl_relaxed(base + BUSWR_V31X_OVERFLOW_STATUS_REG);
+	writel_relaxed(BIT(0), base + BUSWR_V31X_STATUS_CLEAR_REG);
+	if (IS_BUSWR_V31X_OVERFLOW_ERR(bus_stat)) {
+		writel_relaxed(bus_stat, base + BUSWR_V31X_STATUS_CLEAR_REG);
+		pr_warn("BUSWR common overflow: 0x%x\n", bus_stat);
+	}
+
+	bus_stat = readl_relaxed(base + BUSWR_V31X_VIOLATION_STATUS_REG);
+	if (IS_BUSWR_V31X_VIOLATION_ERR(bus_stat))
+		pr_warn("BUSWR common violation: 0x%x\n", bus_stat);
+
+	bus_stat = readl_relaxed(base + BUSWR_V31X_IMG_SIZE_VIOLATION_REG);
+	if (IS_BUSWR_V31X_IMG_SIZE_VIOLATION_ERR(bus_stat))
+		pr_warn("BUSWR common image size violation: 0x%x\n", bus_stat);
+
+	iif_stat = readl_relaxed(base + BUSWR_V31X_IIF_IRQ_STATUS_S0);
+	writel_relaxed(iif_stat, base + BUSWR_V31X_IIF_IRQ_CLEAR_S0);
+	if (iif_stat) {
+		if (IS_BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(iif_stat))
+			pr_warn("BUSWR image size violation from CCIF: 0x%x\n", iif_stat);
+		if (IS_BUSWR_V31X_IIF_IRQ_S0_VIOLATION(iif_stat))
+			pr_warn("BUSWR violation from CCIF: 0x%x\n", iif_stat);
+	}
+
+	writel_relaxed(BUSWR_V31X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V31X_IIF_IRQ_CMD_REG);
+
+	return iif_stat;
+}
+
+static void ife_buswr_31x_irq_enable(struct ife_buswr_ctx *ctx)
+{
+	writel_relaxed(~0, ctx->base + BUSWR_V31X_IIF_IRQ_MASK_S0);
+}
+
+static void ife_buswr_31x_irq_disable(struct ife_buswr_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+
+	writel_relaxed(0, base + BUSWR_V31X_IIF_IRQ_MASK_S0);
+	writel_relaxed(~0, base + BUSWR_V31X_IIF_IRQ_CLEAR_S0);
+	writel_relaxed(BUSWR_V31X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V31X_IIF_IRQ_CMD_REG);
+}
+
+const struct ife_hw_buswr_ops ife_buswr_31x_ops = {
+	.acquire	= ife_buswr_31x_acquire,
+	.release	= ife_buswr_31x_release,
+	.irq_enable	= ife_buswr_31x_irq_enable,
+	.irq_disable	= ife_buswr_31x_irq_disable,
+	.wm_config	= ife_buswr_31x_wm_config,
+	.wm_enable	= ife_buswr_31x_wm_enable,
+	.wm_disable	= ife_buswr_31x_wm_disable,
+	.queue_buf	= ife_buswr_31x_queue_buf,
+	.consumed_addr	= ife_buswr_31x_consumed_addr,
+	.irq_status	= ife_buswr_31x_irq_status,
+	.is_fifo_full	= ife_buswr_31x_is_fifo_full,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index c163c336fcc3..efbb1e75d550 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -397,6 +397,7 @@ camss_ife_pipe_for_stream_id(int nstreams,
 extern const struct ife_pads_descriptors ife_pads_v20x;
 extern const struct ife_hw_top_ops ife_top_20x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
+extern const struct ife_hw_buswr_ops ife_buswr_31x_ops;
 extern const struct camss_mod_ops ife_mod_v10x_ops;
 
 extern const struct ife_pads_descriptors ife_lite_pads_v20x;
-- 
2.34.1


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

* [PATCH 28/45] media: qcom: camss: sa8775p: add IFE scaler v2.3x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (26 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 29/45] media: qcom: camss: sa8775p: add IFE top v4.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (17 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the scaler helpers used by the IFE v4.1x pixel outputs.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../qcom/camss/camss-ife-scale-v23x.c         | 203 ++++++++++++++++++
 .../qcom/camss/camss-ife-scale-v23x.h         |  19 ++
 3 files changed, 223 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 2251b57ba38b..e28b7477dab1 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -22,6 +22,7 @@ qcom-camss-objs += \
 		camss-ife-mod-v10x.o \
 		camss-ife-params.o \
 		camss-ife-scale-v10x.o \
+		camss-ife-scale-v23x.o \
 		camss-ife-top-v20x.o \
 		camss-ife-pads.o \
 		camss-ispif.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c
new file mode 100644
index 000000000000..13ce9cb93bb0
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <linux/bitfield.h>
+#include <linux/math.h>
+
+#include "camss-ife.h"
+#include "camss-ife-scale-v23x.h"
+
+#define CAMIF_V23X_Q21_FORMAT			21
+
+#define CAMIF_V23X_DS_MN_MODULE_CFG		0x60
+#define CAMIF_V23X_DS_MN_CFG			0x64
+#define CAMIF_V23X_DS_MN_IMAGE_SIZE_CFG		0x68
+#define CAMIF_V23X_DS_MN_H_CFG			0x6c
+#define CAMIF_V23X_DS_MN_V_CFG			0x74
+#define CAMIF_V23X_DS_MN_CROP_LINE_CFG		0x7c
+#define CAMIF_V23X_DS_MN_CROP_PIXEL_CFG		0x80
+
+#define CAMIF_V23X_DS_MN_MODULE_CFG_CROP_EN	BIT(8)
+#define CAMIF_V23X_DS_MN_MODULE_CFG_EN		BIT(0)
+#define CAMIF_V23X_DS_MN_CFG_V_SCALE_EN		BIT(10)
+#define CAMIF_V23X_DS_MN_CFG_H_SCALE_EN		BIT(9)
+#define CAMIF_V23X_DS_MN_IMAGE_SIZE_WIDTH	GENMASK(29, 16)
+#define CAMIF_V23X_DS_MN_IMAGE_SIZE_HEIGHT	GENMASK(13, 0)
+#define CAMIF_V23X_DS_MN_CFG_INTERP_RES		GENMASK(31, 30)
+#define CAMIF_V23X_DS_MN_CROP_FIRST		GENMASK(29, 16)
+#define CAMIF_V23X_DS_MN_CROP_LAST		GENMASK(13, 0)
+
+#define CAMIF_V23X_INTERP_RES_0			0
+#define CAMIF_V23X_INTERP_RES_1			1
+#define CAMIF_V23X_INTERP_RES_2			2
+#define CAMIF_V23X_INTERP_RES_3			3
+
+#define CAMIF_V23X_POST_MN_MODULE_CFG		0x60
+#define CAMIF_V23X_POST_MN_CROP_LINE_CFG	0x68
+#define CAMIF_V23X_POST_MN_CROP_PIXEL_CFG	0x6c
+#define CAMIF_V23X_POST_MN_CH0_CLAMP_MIN	0x70
+#define CAMIF_V23X_POST_MN_CH0_ROUND_CFG	0x74
+#define CAMIF_V23X_POST_MN_CH1_CLAMP_MIN	0x78
+#define CAMIF_V23X_POST_MN_CH1_ROUND_CFG	0x7c
+#define CAMIF_V23X_POST_MN_CH2_CLAMP_MIN	0x80
+#define CAMIF_V23X_POST_MN_CH2_ROUND_CFG	0x84
+#define CAMIF_V23X_POST_MN_CH0_CLAMP_MAX	0x88
+#define CAMIF_V23X_POST_MN_CH1_CLAMP_MAX	0x8c
+#define CAMIF_V23X_POST_MN_CH2_CLAMP_MAX	0x90
+
+#define CAMIF_V23X_POST_MN_EN			BIT(0)
+
+static u32 camif_v23x_calc_ds_q2(u32 src_size, u32 dst_size)
+{
+	if (src_size > 64 * dst_size)
+		return CAMIF_V23X_INTERP_RES_0;
+	else if (src_size > 32 * dst_size)
+		return CAMIF_V23X_INTERP_RES_1;
+	else if (src_size > 16 * dst_size)
+		return CAMIF_V23X_INTERP_RES_2;
+
+	return CAMIF_V23X_INTERP_RES_3;
+}
+
+static u32 camif_v23x_calc_ds_q21(u32 src_size, u32 dst_size)
+{
+	u64 rq21 = (u64)src_size << CAMIF_V23X_Q21_FORMAT;
+
+	return DIV_ROUND_CLOSEST_ULL(rq21, dst_size);
+}
+
+static void camif_v23x_crop_config(void __iomem *mod_base,
+				   const struct camss_ife_pipeline_cfg *cfg)
+{
+	u32 rval;
+
+	rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_FIRST, cfg->ds_mn_crop->left) |
+	       FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, cfg->ds_mn_crop->width - 1);
+	writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_PIXEL_CFG);
+
+	rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_FIRST, cfg->ds_mn_crop->top) |
+	       FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, cfg->ds_mn_crop->height - 1);
+	writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_LINE_CFG);
+}
+
+void camif_v23x_scale_config(void __iomem *mod_base,
+			     const struct camss_ife_pipeline_cfg *cfg,
+			     const struct camss_ife_wm_config *wm_cfg)
+{
+	u32 height;
+	u32 width;
+	u32 h_q21;
+	u32 v_q21;
+	u32 h_q2;
+	u32 v_q2;
+	u32 rval;
+
+	if (cfg->ds_mn_crop) {
+		camif_v23x_crop_config(mod_base, cfg);
+		width = cfg->ds_mn_crop->width;
+		height = cfg->ds_mn_crop->height;
+
+		rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+		rval |= CAMIF_V23X_DS_MN_MODULE_CFG_CROP_EN;
+		writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+	} else {
+		width = cfg->in_width;
+		height = cfg->in_height;
+
+		/*
+		 * Vertical scaling misbehaves unless the crop registers are
+		 * programmed to the full frame, even with crop disabled.
+		 */
+		rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, width - 1);
+		writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_PIXEL_CFG);
+
+		rval = FIELD_PREP(CAMIF_V23X_DS_MN_CROP_LAST, height - 1);
+		writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CROP_LINE_CFG);
+
+		rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+		rval &= ~CAMIF_V23X_DS_MN_MODULE_CFG_CROP_EN;
+		writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+	}
+
+	rval = FIELD_PREP(CAMIF_V23X_DS_MN_IMAGE_SIZE_WIDTH, width - 1) |
+	       FIELD_PREP(CAMIF_V23X_DS_MN_IMAGE_SIZE_HEIGHT, height - 1);
+	writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_IMAGE_SIZE_CFG);
+
+	rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_CFG);
+	if (wm_cfg->width < width) {
+		h_q2 = camif_v23x_calc_ds_q2(width, wm_cfg->width);
+		h_q21 = camif_v23x_calc_ds_q21(width, wm_cfg->width);
+
+		writel_relaxed(FIELD_PREP(CAMIF_V23X_DS_MN_CFG_INTERP_RES, h_q2) | h_q21,
+			       mod_base + CAMIF_V23X_DS_MN_H_CFG);
+		rval |= CAMIF_V23X_DS_MN_CFG_H_SCALE_EN;
+	} else {
+		rval &= ~CAMIF_V23X_DS_MN_CFG_H_SCALE_EN;
+	}
+	writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CFG);
+
+	rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_CFG);
+	if (wm_cfg->height < height) {
+		v_q2 = camif_v23x_calc_ds_q2(height, wm_cfg->height);
+		v_q21 = camif_v23x_calc_ds_q21(height, wm_cfg->height);
+
+		writel_relaxed(FIELD_PREP(CAMIF_V23X_DS_MN_CFG_INTERP_RES, v_q2) | v_q21,
+			       mod_base + CAMIF_V23X_DS_MN_V_CFG);
+		rval |= CAMIF_V23X_DS_MN_CFG_V_SCALE_EN;
+	} else {
+		rval &= ~CAMIF_V23X_DS_MN_CFG_V_SCALE_EN;
+	}
+	writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_CFG);
+}
+
+void camif_v23x_scale_enable(void __iomem *mod_base)
+{
+	u32 rval;
+
+	rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+	rval |= CAMIF_V23X_DS_MN_MODULE_CFG_EN;
+	writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+}
+
+void camif_v23x_scale_disable(void __iomem *mod_base)
+{
+	u32 rval;
+
+	rval = readl_relaxed(mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+	rval &= ~CAMIF_V23X_DS_MN_MODULE_CFG_EN;
+	writel_relaxed(rval, mod_base + CAMIF_V23X_DS_MN_MODULE_CFG);
+}
+
+void camif_v23x_post_mn_config(void __iomem *base,
+			       const struct camss_ife_wm_config *cfg,
+			       bool chroma)
+{
+	writel_relaxed(cfg->height - 1, base + CAMIF_V23X_POST_MN_CROP_LINE_CFG);
+	writel_relaxed(cfg->width - 1, base + CAMIF_V23X_POST_MN_CROP_PIXEL_CFG);
+	writel_relaxed(0, base + CAMIF_V23X_POST_MN_CH0_CLAMP_MIN);
+	writel_relaxed(1023, base + CAMIF_V23X_POST_MN_CH0_CLAMP_MAX);
+	writel_relaxed(4 << 3, base + CAMIF_V23X_POST_MN_CH0_ROUND_CFG);
+
+	if (chroma) {
+		writel_relaxed(0, base + CAMIF_V23X_POST_MN_CH1_CLAMP_MIN);
+		writel_relaxed(1023, base + CAMIF_V23X_POST_MN_CH1_CLAMP_MAX);
+		writel_relaxed(4 << 3, base + CAMIF_V23X_POST_MN_CH1_ROUND_CFG);
+		writel_relaxed(0, base + CAMIF_V23X_POST_MN_CH2_CLAMP_MIN);
+		writel_relaxed(1023, base + CAMIF_V23X_POST_MN_CH2_CLAMP_MAX);
+		writel_relaxed(4 << 3, base + CAMIF_V23X_POST_MN_CH2_ROUND_CFG);
+	}
+}
+
+void camif_v23x_post_mn_enable(void __iomem *base)
+{
+	writel_relaxed(0, base + CAMIF_V23X_POST_MN_MODULE_CFG);
+}
+
+void camif_v23x_post_mn_disable(void __iomem *base)
+{
+	u32 rval;
+
+	rval = readl_relaxed(base + CAMIF_V23X_POST_MN_MODULE_CFG);
+	rval &= ~CAMIF_V23X_POST_MN_EN;
+	writel_relaxed(rval, base + CAMIF_V23X_POST_MN_MODULE_CFG);
+}
diff --git a/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h
new file mode 100644
index 000000000000..d3ca6420a5c7
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-scale-v23x.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#ifndef _CAMSS_IFE_SCALE_V23X_H_
+#define _CAMSS_IFE_SCALE_V23X_H_
+
+void camif_v23x_scale_config(void __iomem *mod_base,
+			     const struct camss_ife_pipeline_cfg *cfg,
+			     const struct camss_ife_wm_config *wm_cfg);
+void camif_v23x_scale_enable(void __iomem *mod_base);
+void camif_v23x_scale_disable(void __iomem *mod_base);
+
+void camif_v23x_post_mn_config(void __iomem *base,
+			       const struct camss_ife_wm_config *cfg,
+			       bool chroma);
+void camif_v23x_post_mn_enable(void __iomem *base);
+void camif_v23x_post_mn_disable(void __iomem *base);
+
+#endif /* _CAMSS_IFE_SCALE_V23X_H_ */
-- 
2.34.1


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

* [PATCH 29/45] media: qcom: camss: sa8775p: add IFE top v4.1x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (27 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 28/45] media: qcom: camss: sa8775p: add IFE scaler v2.3x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 30/45] media: qcom: camss: sa8775p: add IFE lite top v3.0x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (16 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the IFE top v4.1x engine used by the SA8775P full pixel path.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-top-v41x.c  | 384 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   1 +
 3 files changed, 386 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-top-v41x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index e28b7477dab1..479ab17f3bdb 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -24,6 +24,7 @@ qcom-camss-objs += \
 		camss-ife-scale-v10x.o \
 		camss-ife-scale-v23x.o \
 		camss-ife-top-v20x.o \
+		camss-ife-top-v41x.o \
 		camss-ife-pads.o \
 		camss-ispif.o \
 		camss-tpg.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-top-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-top-v41x.c
new file mode 100644
index 000000000000..b600dd009268
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-top-v41x.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Qualcomm Technologies, Inc.
+ */
+
+#include "camss-ife.h"
+#include "camss-ife-scale-v23x.h"
+#include "camss.h"
+
+#define IFE_V41X_TOP_OFFS		0x0000
+
+/* DS M/N scaler module base offsets from IFE TOP base */
+#define IFE_V41X_VID_DS_MN_Y_OFFSET	0x7f80
+#define IFE_V41X_DISP_DS_MN_Y_OFFSET	0x6780
+#define IFE_V41X_VID_DS_MN_C_OFFSET	0x8700
+#define IFE_V41X_DISP_DS_MN_C_OFFSET	0x6a00
+#define IFE_V41X_DISP_POST_MN_Y_OFFSET	0x6c80
+#define IFE_V41X_DISP_POST_MN_C_OFFSET	0x6e00
+
+#define IFE_V41X_MOD_VER_GEN		GENMASK(31, 28)
+#define IFE_V41X_MOD_VER_REV		GENMASK(27, 16)
+#define IFE_V41X_MOD_VER_STEP		GENMASK(15, 0)
+#define IFE_V41X_MOD_GEN(val)		FIELD_GET(IFE_V41X_MOD_VER_GEN, val)
+#define IFE_V41X_MOD_REV(val)		FIELD_GET(IFE_V41X_MOD_VER_REV, val)
+#define IFE_V41X_MOD_STEP(val)		FIELD_GET(IFE_V41X_MOD_VER_STEP, val)
+
+/* CSID-level reset and register-update (via csid_base) */
+#define IFE_V41X_CSID_RST_CMD		0x00000010
+#define IFE_V41X_CSID_RST_CMD_SW_RST	BIT(0)
+#define IFE_V41X_CSID_RUP_AUP_CMD	0x00000018
+#define IFE_V41X_CSID_RUP_IPP0		BIT(0)
+#define IFE_V41X_CSID_RUP_RDI0		BIT(4)
+#define IFE_V41X_CSID_RUP_RDI1		BIT(5)
+#define IFE_V41X_CSID_RUP_RDI2		BIT(6)
+#define IFE_V41X_CSID_AUP_IPP0		BIT(16)
+#define IFE_V41X_CSID_AUP_RDI0		BIT(20)
+#define IFE_V41X_CSID_AUP_RDI1		BIT(21)
+#define IFE_V41X_CSID_AUP_RDI2		BIT(22)
+
+/* TOP functional IRQ registers */
+#define IFE_V41X_FUNC_IRQ_CMD		0x000000a4
+#define IFE_V41X_FUNC_IRQ_CMD_CLEAR	BIT(0)
+#define IFE_V41X_FUNC_IRQ_MASK_0	0x000000a8
+#define IFE_V41X_FUNC_IRQ_MASK_1	0x000000ac
+#define IFE_V41X_FUNC_IRQ_CLEAR_0	0x000000b0
+#define IFE_V41X_FUNC_IRQ_CLEAR_1	0x000000b4
+#define IFE_V41X_FUNC_IRQ_STATUS_0	0x000000b8
+#define IFE_V41X_FUNC_IRQ_STATUS_1	0x000000bc
+
+/* IRQ_STATUS_0 bits */
+#define IFE_V41X_IRQ0_DIAG_VIOLATION		BIT(15)
+#define IFE_V41X_IRQ0_DSP_ERROR_VIOLATION	BIT(14)
+#define IFE_V41X_IRQ0_DSP_RX_FIFO_OVERFLOW	BIT(13)
+#define IFE_V41X_IRQ0_DSP_TX_FIFO_OVERFLOW	BIT(12)
+#define IFE_V41X_IRQ0_PIXEL_PIPE_VIOLATION	BIT(4)
+#define IFE_V41X_IRQ0_BUSWR			BIT(0)
+#define IFE_V41X_IRQ0_ERRORS_MASK \
+	(IFE_V41X_IRQ0_DIAG_VIOLATION		| \
+	 IFE_V41X_IRQ0_DSP_ERROR_VIOLATION	| \
+	 IFE_V41X_IRQ0_DSP_RX_FIFO_OVERFLOW	| \
+	 IFE_V41X_IRQ0_DSP_TX_FIFO_OVERFLOW	| \
+	 IFE_V41X_IRQ0_PIXEL_PIPE_VIOLATION)
+
+/* IRQ_STATUS_1 bits */
+#define IFE_V41X_IRQ1_PIXEL_PIPE_EPOCH1	BIT(17)
+#define IFE_V41X_IRQ1_PIXEL_PIPE_EPOCH0	BIT(16)
+#define IFE_V41X_IRQ1_RDI2_EOF			BIT(13)
+#define IFE_V41X_IRQ1_RDI2_SOF			BIT(12)
+#define IFE_V41X_IRQ1_RDI1_EOF			BIT(11)
+#define IFE_V41X_IRQ1_RDI1_SOF			BIT(10)
+#define IFE_V41X_IRQ1_RDI0_EOF			BIT(9)
+#define IFE_V41X_IRQ1_RDI0_SOF			BIT(8)
+#define IFE_V41X_IRQ1_PIXEL_PIPE_EOF		BIT(1)
+#define IFE_V41X_IRQ1_PIXEL_PIPE_SOF		BIT(0)
+#define IFE_V41X_IRQ1_RDI0 \
+	(IFE_V41X_IRQ1_RDI0_SOF | IFE_V41X_IRQ1_RDI0_EOF)
+#define IFE_V41X_IRQ1_RDI1 \
+	(IFE_V41X_IRQ1_RDI1_SOF | IFE_V41X_IRQ1_RDI1_EOF)
+#define IFE_V41X_IRQ1_RDI2 \
+	(IFE_V41X_IRQ1_RDI2_SOF | IFE_V41X_IRQ1_RDI2_EOF)
+#define IFE_V41X_IRQ1_PP \
+	(IFE_V41X_IRQ1_PIXEL_PIPE_SOF		| \
+	 IFE_V41X_IRQ1_PIXEL_PIPE_EOF		| \
+	 IFE_V41X_IRQ1_PIXEL_PIPE_EPOCH0	| \
+	 IFE_V41X_IRQ1_PIXEL_PIPE_EPOCH1)
+
+/* WM indices for PP output paths */
+#define IFE_V41X_PP_VID_Y_WM		0
+#define IFE_V41X_PP_VID_C_WM		1
+#define IFE_V41X_PP_DISP_Y_WM		3
+#define IFE_V41X_PP_DISP_C_WM		4
+
+#define IFE_V41X_HW_VERSION		0x00000000
+
+#define IFE_V41X_CORE_CFG_0				0x00000024
+#define IFE_V41X_CORE_CFG_0_VID_OUT_OUTPUT_PACKER_EN	BIT(10)
+#define IFE_V41X_CORE_CFG_0_DISP_DS4_R2PD_DISABLE	BIT(21)
+#define IFE_V41X_CORE_CFG_0_DISP_DS16_R2PD_DISABLE	BIT(22)
+
+static int ife_top_v41x_acquire(struct ife_device *ife, void __iomem *base,
+				void __iomem *csid_base)
+{
+	struct ife_top_ctx *ctx = &ife->top_ctx;
+
+	if (!csid_base)
+		return -EINVAL;
+
+	ctx->base	= base + IFE_V41X_TOP_OFFS;
+	ctx->csid_base	= csid_base;
+	ctx->dev	= ife->camss->dev;
+
+	return 0;
+}
+
+static void ife_top_v41x_release(struct ife_top_ctx *ctx)
+{
+	ctx->base      = NULL;
+	ctx->csid_base = NULL;
+}
+
+static u32 ife_top_v41x_version(struct ife_top_ctx *ctx)
+{
+	struct camss_hw_version hw_ver;
+	u32 reg = readl_relaxed(ctx->base + IFE_V41X_HW_VERSION);
+
+	hw_ver.ids.major = IFE_V41X_MOD_GEN(reg);
+	hw_ver.ids.minor = IFE_V41X_MOD_REV(reg);
+	hw_ver.ids.steps = IFE_V41X_MOD_STEP(reg);
+
+	return hw_ver.hw_version;
+}
+
+static void ife_top_v41x_reset(struct ife_top_ctx *ctx)
+{
+	void __iomem *top = ctx->base;
+	void __iomem *csid = ctx->csid_base;
+
+	writel_relaxed(~0, top + IFE_V41X_FUNC_IRQ_CLEAR_0);
+	writel_relaxed(~0, top + IFE_V41X_FUNC_IRQ_CLEAR_1);
+	writel_relaxed(IFE_V41X_FUNC_IRQ_CMD_CLEAR, top + IFE_V41X_FUNC_IRQ_CMD);
+	writel_relaxed(IFE_V41X_CSID_RST_CMD_SW_RST, csid + IFE_V41X_CSID_RST_CMD);
+}
+
+static void ife_top_v41x_reg_update(struct ife_top_ctx *ctx,
+				    const struct ife_pad_desc *dsc)
+{
+	void __iomem *csid = ctx->csid_base;
+	u32 rval;
+
+	switch (dsc->hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_PP:
+		rval = IFE_V41X_CSID_RUP_IPP0 | IFE_V41X_CSID_AUP_IPP0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		rval = IFE_V41X_CSID_RUP_RDI0 | IFE_V41X_CSID_AUP_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		rval = IFE_V41X_CSID_RUP_RDI1 | IFE_V41X_CSID_AUP_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		rval = IFE_V41X_CSID_RUP_RDI2 | IFE_V41X_CSID_AUP_RDI2;
+		break;
+	default:
+		dev_warn(ctx->dev, "Invalid RUP pipe: 0x%x\n", dsc->hw_pipe);
+		return;
+	}
+
+	writel_relaxed(rval, csid + IFE_V41X_CSID_RUP_AUP_CMD);
+}
+
+static int ife_top_v41x_pipe_config(struct ife_top_ctx *ctx,
+				    const struct ife_pad_desc *desc,
+				    const struct camss_ife_pipeline_cfg *pipeline_cfg,
+				    const struct camss_ife_wm_config *wm_cfg)
+{
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		if (wm_cfg[Y_PLANE].wm_id == IFE_V41X_PP_VID_Y_WM &&
+		    wm_cfg[C_PLANE].wm_id == IFE_V41X_PP_VID_C_WM) {
+			camif_v23x_scale_config(ctx->base + IFE_V41X_VID_DS_MN_Y_OFFSET,
+						pipeline_cfg, &wm_cfg[Y_PLANE]);
+			camif_v23x_scale_config(ctx->base + IFE_V41X_VID_DS_MN_C_OFFSET,
+						pipeline_cfg, &wm_cfg[C_PLANE]);
+		}
+
+		if (wm_cfg[Y_PLANE].wm_id == IFE_V41X_PP_DISP_Y_WM &&
+		    wm_cfg[C_PLANE].wm_id == IFE_V41X_PP_DISP_C_WM) {
+			camif_v23x_scale_config(ctx->base + IFE_V41X_DISP_DS_MN_Y_OFFSET,
+						pipeline_cfg, &wm_cfg[Y_PLANE]);
+			camif_v23x_scale_config(ctx->base + IFE_V41X_DISP_DS_MN_C_OFFSET,
+						pipeline_cfg, &wm_cfg[C_PLANE]);
+			camif_v23x_post_mn_config(ctx->base + IFE_V41X_DISP_POST_MN_Y_OFFSET,
+						  &wm_cfg[Y_PLANE], false);
+			camif_v23x_post_mn_config(ctx->base + IFE_V41X_DISP_POST_MN_C_OFFSET,
+						  &wm_cfg[C_PLANE], true);
+		}
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid HW pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ife_top_v41x_pipe_enable(struct ife_top_ctx *ctx,
+				    const struct ife_pad_desc *desc)
+{
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		camif_v23x_scale_enable(ctx->base + IFE_V41X_VID_DS_MN_Y_OFFSET);
+		camif_v23x_scale_enable(ctx->base + IFE_V41X_VID_DS_MN_C_OFFSET);
+		writel_relaxed(IFE_V41X_CORE_CFG_0_VID_OUT_OUTPUT_PACKER_EN |
+			       IFE_V41X_CORE_CFG_0_DISP_DS4_R2PD_DISABLE |
+			       IFE_V41X_CORE_CFG_0_DISP_DS16_R2PD_DISABLE,
+			       ctx->base + IFE_V41X_CORE_CFG_0);
+		camif_v23x_scale_enable(ctx->base + IFE_V41X_DISP_DS_MN_Y_OFFSET);
+		camif_v23x_scale_enable(ctx->base + IFE_V41X_DISP_DS_MN_C_OFFSET);
+		camif_v23x_post_mn_enable(ctx->base + IFE_V41X_DISP_POST_MN_Y_OFFSET);
+		camif_v23x_post_mn_enable(ctx->base + IFE_V41X_DISP_POST_MN_C_OFFSET);
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid HW pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ife_top_v41x_pipe_disable(struct ife_top_ctx *ctx,
+				     const struct ife_pad_desc *desc)
+{
+	enum camss_ife_hw_pipe hw_pipe = desc->hw_pipe;
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+	case CAMSS_IFE_HW_PIPE_RDI1:
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		break;
+	case CAMSS_IFE_HW_PIPE_PP:
+		camif_v23x_scale_disable(ctx->base + IFE_V41X_VID_DS_MN_Y_OFFSET);
+		camif_v23x_scale_disable(ctx->base + IFE_V41X_VID_DS_MN_C_OFFSET);
+		writel_relaxed(0, ctx->base + IFE_V41X_CORE_CFG_0);
+		camif_v23x_scale_disable(ctx->base + IFE_V41X_DISP_DS_MN_Y_OFFSET);
+		camif_v23x_scale_disable(ctx->base + IFE_V41X_DISP_DS_MN_C_OFFSET);
+		camif_v23x_post_mn_disable(ctx->base + IFE_V41X_DISP_POST_MN_Y_OFFSET);
+		camif_v23x_post_mn_disable(ctx->base + IFE_V41X_DISP_POST_MN_C_OFFSET);
+		break;
+	default:
+		dev_err(ctx->dev, "Invalid HW pipeline identifier: 0x%x\n", hw_pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ife_top_v41x_prepare(struct ife_top_ctx *ctx)
+{
+	void __iomem *top = ctx->base;
+	u32 rval;
+
+	writel_relaxed(~0, top + IFE_V41X_FUNC_IRQ_CLEAR_0);
+	writel_relaxed(~0, top + IFE_V41X_FUNC_IRQ_CLEAR_1);
+	writel_relaxed(IFE_V41X_FUNC_IRQ_CMD_CLEAR, top + IFE_V41X_FUNC_IRQ_CMD);
+
+	rval = IFE_V41X_IRQ0_BUSWR | IFE_V41X_IRQ0_ERRORS_MASK;
+	writel_relaxed(rval, top + IFE_V41X_FUNC_IRQ_MASK_0);
+}
+
+static void ife_top_v41x_enable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *top = ctx->base;
+	u32 rval = readl_relaxed(top + IFE_V41X_FUNC_IRQ_MASK_1);
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_PP:
+		rval |= IFE_V41X_IRQ1_PP;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		rval |= IFE_V41X_IRQ1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		rval |= IFE_V41X_IRQ1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		rval |= IFE_V41X_IRQ1_RDI2;
+		break;
+	default:
+		return;
+	}
+
+	writel_relaxed(rval, top + IFE_V41X_FUNC_IRQ_MASK_1);
+}
+
+static void ife_top_v41x_disable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *top = ctx->base;
+	u32 rval = readl_relaxed(top + IFE_V41X_FUNC_IRQ_MASK_1);
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_PP:
+		rval &= ~IFE_V41X_IRQ1_PP;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		rval &= ~IFE_V41X_IRQ1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		rval &= ~IFE_V41X_IRQ1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		rval &= ~IFE_V41X_IRQ1_RDI2;
+		break;
+	default:
+		return;
+	}
+
+	writel_relaxed(rval, top + IFE_V41X_FUNC_IRQ_MASK_1);
+}
+
+static void ife_top_v41x_irq_status(struct ife_top_ctx *ctx, u32 isr_stat[IFE_TOP_STATUS_MAX])
+{
+	void __iomem *top = ctx->base;
+	u32 stat0;
+	u32 stat1;
+
+	stat0 = readl_relaxed(top + IFE_V41X_FUNC_IRQ_STATUS_0);
+	stat1 = readl_relaxed(top + IFE_V41X_FUNC_IRQ_STATUS_1);
+
+	writel_relaxed(stat0, top + IFE_V41X_FUNC_IRQ_CLEAR_0);
+	writel_relaxed(stat1, top + IFE_V41X_FUNC_IRQ_CLEAR_1);
+	writel_relaxed(IFE_V41X_FUNC_IRQ_CMD_CLEAR, top + IFE_V41X_FUNC_IRQ_CMD);
+
+	isr_stat[IFE_TOP_STATUS_ID0] = 0;
+	isr_stat[IFE_TOP_STATUS_ID1] = 0;
+	isr_stat[IFE_TOP_STATUS_ID2] = stat0 & IFE_V41X_IRQ0_ERRORS_MASK;
+
+	if (stat0 & IFE_V41X_IRQ0_BUSWR)
+		isr_stat[IFE_TOP_STATUS_ID0] |= IFE_HW_TOP_IRQ0_BUSWR;
+
+	if (stat1 & IFE_V41X_IRQ1_RDI0_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI0_SOF;
+
+	if (stat1 & IFE_V41X_IRQ1_RDI1_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI1_SOF;
+
+	if (stat1 & IFE_V41X_IRQ1_RDI2_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI2_SOF;
+
+	if (stat1 & IFE_V41X_IRQ1_PIXEL_PIPE_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_PP_SOF;
+
+	if (stat1 & IFE_V41X_IRQ1_PIXEL_PIPE_EPOCH0)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_PP_EPOCH0;
+
+	if (stat1 & IFE_V41X_IRQ1_PIXEL_PIPE_EPOCH1)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_PP_EPOCH1;
+}
+
+const struct ife_hw_top_ops ife_top_v41x_ops = {
+	.acquire	= ife_top_v41x_acquire,
+	.release	= ife_top_v41x_release,
+	.version	= ife_top_v41x_version,
+	.prepare	= ife_top_v41x_prepare,
+	.enable		= ife_top_v41x_enable,
+	.disable	= ife_top_v41x_disable,
+	.pipe_config	= ife_top_v41x_pipe_config,
+	.pipe_enable	= ife_top_v41x_pipe_enable,
+	.pipe_disable	= ife_top_v41x_pipe_disable,
+	.reg_update	= ife_top_v41x_reg_update,
+	.reset		= ife_top_v41x_reset,
+	.irq_status	= ife_top_v41x_irq_status,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index efbb1e75d550..31d9c83767a5 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -396,6 +396,7 @@ camss_ife_pipe_for_stream_id(int nstreams,
 
 extern const struct ife_pads_descriptors ife_pads_v20x;
 extern const struct ife_hw_top_ops ife_top_20x_ops;
+extern const struct ife_hw_top_ops ife_top_v41x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_31x_ops;
 extern const struct camss_mod_ops ife_mod_v10x_ops;
-- 
2.34.1


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

* [PATCH 30/45] media: qcom: camss: sa8775p: add IFE lite top v3.0x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (28 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 29/45] media: qcom: camss: sa8775p: add IFE top v4.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 31/45] media: qcom: camss: sa8775p: add IFE pad descriptors Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (15 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the IFE lite top v3.0x engine used by the SA8775P RDI paths.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../qcom/camss/camss-ife-lite-top-v30x.c      | 265 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   1 +
 3 files changed, 267 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-lite-top-v30x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 479ab17f3bdb..695924e0c03b 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -17,6 +17,7 @@ qcom-camss-objs += \
 		camss-ife.o \
 		camss-ife-bus-v20x.o \
 		camss-ife-bus-v31x.o \
+		camss-ife-lite-top-v30x.o \
 		camss-ife-camif-v10x.o \
 		camss-ife-lite-top-v10x.o \
 		camss-ife-mod-v10x.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-lite-top-v30x.c b/drivers/media/platform/qcom/camss/camss-ife-lite-top-v30x.c
new file mode 100644
index 000000000000..c3c182e4c895
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-lite-top-v30x.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+
+#include "camss-ife.h"
+#include "camss.h"
+
+#define IFE_LT_V30X_TOP_OFFS		0x0000
+
+/* CSID register update command */
+#define IFE_LT_V30X_CSID_RUP_AUP_CMD	0x00000018
+#define IFE_LT_V30X_CSID_RUP_RDI0	BIT(4)
+#define IFE_LT_V30X_CSID_RUP_RDI1	BIT(5)
+#define IFE_LT_V30X_CSID_RUP_RDI2	BIT(6)
+#define IFE_LT_V30X_CSID_RUP_RDI3	BIT(7)
+#define IFE_LT_V30X_CSID_AUP_RDI0	BIT(20)
+#define IFE_LT_V30X_CSID_AUP_RDI1	BIT(21)
+#define IFE_LT_V30X_CSID_AUP_RDI2	BIT(22)
+#define IFE_LT_V30X_CSID_AUP_RDI3	BIT(23)
+
+#define IFE_LT_V30X_MOD_VER_GEN		GENMASK(31, 28)
+#define IFE_LT_V30X_MOD_VER_REV		GENMASK(27, 16)
+#define IFE_LT_V30X_MOD_VER_STEP	GENMASK(15, 0)
+#define IFE_LT_V30X_MOD_GEN(val)	FIELD_GET(IFE_LT_V30X_MOD_VER_GEN, val)
+#define IFE_LT_V30X_MOD_REV(val)	FIELD_GET(IFE_LT_V30X_MOD_VER_REV, val)
+#define IFE_LT_V30X_MOD_STEP(val)	FIELD_GET(IFE_LT_V30X_MOD_VER_STEP, val)
+
+/* TOP functional IRQ registers */
+#define IFE_LT_V30X_FUNC_IRQ_CMD	0x00000064
+#define IFE_LT_V30X_FUNC_IRQ_CMD_CLEAR	BIT(0)
+#define IFE_LT_V30X_FUNC_IRQ_MASK_0	0x00000068
+#define IFE_LT_V30X_FUNC_IRQ_MASK_1	0x0000006c
+#define IFE_LT_V30X_FUNC_IRQ_CLEAR_0	0x00000070
+#define IFE_LT_V30X_FUNC_IRQ_CLEAR_1	0x00000074
+#define IFE_LT_V30X_FUNC_IRQ_STATUS_0	0x00000078
+#define IFE_LT_V30X_FUNC_IRQ_STATUS_1	0x0000007c
+
+/* IRQ_STATUS_0 bits */
+#define IFE_LT_V30X_IRQ0_DIAG_VIOLATION	BIT(1)
+#define IFE_LT_V30X_IRQ0_BUSWR		BIT(0)
+#define IFE_LT_V30X_IRQ0_ERRORS_MASK \
+	(IFE_LT_V30X_IRQ0_DIAG_VIOLATION)
+
+/* IRQ_STATUS_1 bits - per-RDI SOF/EOF */
+#define IFE_LT_V30X_IRQ1_RDI3_EOF	BIT(7)
+#define IFE_LT_V30X_IRQ1_RDI3_SOF	BIT(6)
+#define IFE_LT_V30X_IRQ1_RDI2_EOF	BIT(5)
+#define IFE_LT_V30X_IRQ1_RDI2_SOF	BIT(4)
+#define IFE_LT_V30X_IRQ1_RDI1_EOF	BIT(3)
+#define IFE_LT_V30X_IRQ1_RDI1_SOF	BIT(2)
+#define IFE_LT_V30X_IRQ1_RDI0_EOF	BIT(1)
+#define IFE_LT_V30X_IRQ1_RDI0_SOF	BIT(0)
+
+#define IFE_LT_V30X_IRQ1_RDI0 \
+	(IFE_LT_V30X_IRQ1_RDI0_SOF | IFE_LT_V30X_IRQ1_RDI0_EOF)
+#define IFE_LT_V30X_IRQ1_RDI1 \
+	(IFE_LT_V30X_IRQ1_RDI1_SOF | IFE_LT_V30X_IRQ1_RDI1_EOF)
+#define IFE_LT_V30X_IRQ1_RDI2 \
+	(IFE_LT_V30X_IRQ1_RDI2_SOF | IFE_LT_V30X_IRQ1_RDI2_EOF)
+#define IFE_LT_V30X_IRQ1_RDI3 \
+	(IFE_LT_V30X_IRQ1_RDI3_SOF | IFE_LT_V30X_IRQ1_RDI3_EOF)
+
+#define IFE_LT_V30X_HW_VERSION		0x00000000
+
+static int ife_lite_top_30x_acquire(struct ife_device *ife, void __iomem *base,
+				    void __iomem *csid_base)
+{
+	struct ife_top_ctx *ctx = &ife->top_ctx;
+
+	if (!csid_base)
+		return -EINVAL;
+
+	ctx->base	= base + IFE_LT_V30X_TOP_OFFS;
+	ctx->dev	= ife->camss->dev;
+	ctx->csid_base	= csid_base;
+
+	return 0;
+}
+
+static void ife_lite_top_30x_release(struct ife_top_ctx *ctx)
+{
+	ctx->base      = NULL;
+	ctx->csid_base = NULL;
+}
+
+static u32 ife_lite_top_30x_version(struct ife_top_ctx *ctx)
+{
+	struct camss_hw_version hw_ver;
+	u32 reg = readl_relaxed(ctx->base + IFE_LT_V30X_HW_VERSION);
+
+	hw_ver.ids.major = IFE_LT_V30X_MOD_GEN(reg);
+	hw_ver.ids.minor = IFE_LT_V30X_MOD_REV(reg);
+	hw_ver.ids.steps = IFE_LT_V30X_MOD_STEP(reg);
+
+	return hw_ver.hw_version;
+}
+
+static void ife_lite_top_30x_reset(struct ife_top_ctx *ctx)
+{
+	void __iomem *top = ctx->base;
+
+	writel_relaxed(~0, top + IFE_LT_V30X_FUNC_IRQ_CLEAR_0);
+	writel_relaxed(~0, top + IFE_LT_V30X_FUNC_IRQ_CLEAR_1);
+	writel_relaxed(IFE_LT_V30X_FUNC_IRQ_CMD_CLEAR, top + IFE_LT_V30X_FUNC_IRQ_CMD);
+}
+
+static void ife_lite_top_30x_reg_update(struct ife_top_ctx *ctx,
+					const struct ife_pad_desc *dsc)
+{
+	void __iomem *csid = ctx->csid_base;
+	u32 rval;
+
+	switch (dsc->hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		rval = IFE_LT_V30X_CSID_RUP_RDI0 | IFE_LT_V30X_CSID_AUP_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		rval = IFE_LT_V30X_CSID_RUP_RDI1 | IFE_LT_V30X_CSID_AUP_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		rval = IFE_LT_V30X_CSID_RUP_RDI2 | IFE_LT_V30X_CSID_AUP_RDI2;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		rval = IFE_LT_V30X_CSID_RUP_RDI3 | IFE_LT_V30X_CSID_AUP_RDI3;
+		break;
+	default:
+		dev_warn(ctx->dev, "Invalid RUP pipe: 0x%x\n", dsc->hw_pipe);
+		return;
+	}
+
+	writel_relaxed(rval, csid + IFE_LT_V30X_CSID_RUP_AUP_CMD);
+}
+
+static int ife_lite_top_30x_pipe_config(struct ife_top_ctx *ctx,
+					const struct ife_pad_desc *desc,
+					const struct camss_ife_pipeline_cfg *pipeline_cfg,
+					const struct camss_ife_wm_config *wm_cfg)
+{
+	return 0;
+}
+
+static int ife_lite_top_30x_pipe_enable(struct ife_top_ctx *ctx,
+					const struct ife_pad_desc *desc)
+{
+	return 0;
+}
+
+static int ife_lite_top_30x_pipe_disable(struct ife_top_ctx *ctx,
+					 const struct ife_pad_desc *desc)
+{
+	return 0;
+}
+
+static void ife_lite_top_30x_prepare(struct ife_top_ctx *ctx)
+{
+	void __iomem *base = ctx->base;
+	u32 rval;
+
+	writel_relaxed(~0, base + IFE_LT_V30X_FUNC_IRQ_CLEAR_0);
+	writel_relaxed(~0, base + IFE_LT_V30X_FUNC_IRQ_CLEAR_1);
+	writel_relaxed(IFE_LT_V30X_FUNC_IRQ_CMD_CLEAR, base + IFE_LT_V30X_FUNC_IRQ_CMD);
+
+	rval = IFE_LT_V30X_IRQ0_BUSWR | IFE_LT_V30X_IRQ0_ERRORS_MASK;
+	writel_relaxed(rval, base + IFE_LT_V30X_FUNC_IRQ_MASK_0);
+}
+
+static void ife_lite_top_30x_enable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *base = ctx->base;
+	u32 rval = readl_relaxed(base + IFE_LT_V30X_FUNC_IRQ_MASK_1);
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		rval |= IFE_LT_V30X_IRQ1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		rval |= IFE_LT_V30X_IRQ1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		rval |= IFE_LT_V30X_IRQ1_RDI2;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		rval |= IFE_LT_V30X_IRQ1_RDI3;
+		break;
+	default:
+		return;
+	}
+
+	writel_relaxed(rval, base + IFE_LT_V30X_FUNC_IRQ_MASK_1);
+}
+
+static void ife_lite_top_30x_disable(struct ife_top_ctx *ctx, enum camss_ife_hw_pipe hw_pipe)
+{
+	void __iomem *base = ctx->base;
+	u32 rval = readl_relaxed(base + IFE_LT_V30X_FUNC_IRQ_MASK_1);
+
+	switch (hw_pipe) {
+	case CAMSS_IFE_HW_PIPE_RDI0:
+		rval &= ~IFE_LT_V30X_IRQ1_RDI0;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI1:
+		rval &= ~IFE_LT_V30X_IRQ1_RDI1;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI2:
+		rval &= ~IFE_LT_V30X_IRQ1_RDI2;
+		break;
+	case CAMSS_IFE_HW_PIPE_RDI3:
+		rval &= ~IFE_LT_V30X_IRQ1_RDI3;
+		break;
+	default:
+		dev_warn(ctx->dev, "Invalid HW pipeline identifier: 0x%x\n", hw_pipe);
+		return;
+	}
+
+	writel_relaxed(rval, base + IFE_LT_V30X_FUNC_IRQ_MASK_1);
+}
+
+static void ife_lite_top_30x_irq_status(struct ife_top_ctx *ctx, u32 isr_stat[IFE_TOP_STATUS_MAX])
+{
+	void __iomem *base = ctx->base;
+	u32 stat0;
+	u32 stat1;
+
+	stat0 = readl_relaxed(base + IFE_LT_V30X_FUNC_IRQ_STATUS_0);
+	stat1 = readl_relaxed(base + IFE_LT_V30X_FUNC_IRQ_STATUS_1);
+
+	writel_relaxed(stat0, base + IFE_LT_V30X_FUNC_IRQ_CLEAR_0);
+	writel_relaxed(stat1, base + IFE_LT_V30X_FUNC_IRQ_CLEAR_1);
+	writel_relaxed(IFE_LT_V30X_FUNC_IRQ_CMD_CLEAR, base + IFE_LT_V30X_FUNC_IRQ_CMD);
+
+	isr_stat[IFE_TOP_STATUS_ID0] = 0;
+	isr_stat[IFE_TOP_STATUS_ID1] = 0;
+	isr_stat[IFE_TOP_STATUS_ID2] = stat0 & IFE_LT_V30X_IRQ0_ERRORS_MASK;
+
+	if (stat0 & IFE_LT_V30X_IRQ0_BUSWR)
+		isr_stat[IFE_TOP_STATUS_ID0] |= IFE_HW_TOP_IRQ0_BUSWR;
+
+	if (stat1 & IFE_LT_V30X_IRQ1_RDI0_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI0_SOF;
+
+	if (stat1 & IFE_LT_V30X_IRQ1_RDI1_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI1_SOF;
+
+	if (stat1 & IFE_LT_V30X_IRQ1_RDI2_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI2_SOF;
+
+	if (stat1 & IFE_LT_V30X_IRQ1_RDI3_SOF)
+		isr_stat[IFE_TOP_STATUS_ID1] |= IFE_HW_TOP_IRQ1_RDI3_SOF;
+}
+
+const struct ife_hw_top_ops ife_lite_top_30x_ops = {
+	.acquire	= ife_lite_top_30x_acquire,
+	.release	= ife_lite_top_30x_release,
+	.version	= ife_lite_top_30x_version,
+	.prepare	= ife_lite_top_30x_prepare,
+	.enable		= ife_lite_top_30x_enable,
+	.disable	= ife_lite_top_30x_disable,
+	.pipe_config	= ife_lite_top_30x_pipe_config,
+	.pipe_enable	= ife_lite_top_30x_pipe_enable,
+	.pipe_disable	= ife_lite_top_30x_pipe_disable,
+	.reg_update	= ife_lite_top_30x_reg_update,
+	.reset		= ife_lite_top_30x_reset,
+	.irq_status	= ife_lite_top_30x_irq_status,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 31d9c83767a5..eff4ab4bd1c9 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -403,5 +403,6 @@ extern const struct camss_mod_ops ife_mod_v10x_ops;
 
 extern const struct ife_pads_descriptors ife_lite_pads_v20x;
 extern const struct ife_hw_top_ops ife_lite_top_10x_ops;
+extern const struct ife_hw_top_ops ife_lite_top_30x_ops;
 
 #endif /* CAMSS_IFE_SUB_DEV_H */
-- 
2.34.1


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

* [PATCH 31/45] media: qcom: camss: sa8775p: add IFE pad descriptors
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (29 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 30/45] media: qcom: camss: sa8775p: add IFE lite top v3.0x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 32/45] media: qcom: camss: sa8775p: add IFE completion groups Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (14 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Describe the SA8775P full and lite IFE pads and write masters.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-pads.c      | 216 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   2 +
 2 files changed, 218 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-pads.c b/drivers/media/platform/qcom/camss/camss-ife-pads.c
index 7a4e41c9660b..b1e6204ceb21 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-pads.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-pads.c
@@ -358,3 +358,219 @@ const struct ife_pads_descriptors ife_pads_v20x = {
 	.num_desc = ARRAY_SIZE(ife_pad_desc_v20x),
 	.desc = ife_pad_desc_v20x,
 };
+
+static const struct ife_pad_limits ife_vid_limits_v30x = {
+	.min_width	= 320,
+	.min_height	= 240,
+	.max_width	= 16384,
+	.max_height	= 16384,
+	.max_h_ratio	= 16,
+	.max_v_ratio	= 16,
+};
+
+static const struct ife_pad_limits ife_vid_limits_v41x = {
+	.min_width	= 16,
+	.min_height	= 16,
+	.max_width	= 16384,
+	.max_height	= 16384,
+	.max_h_ratio	= 64,
+	.max_v_ratio	= 64,
+};
+
+static const struct ife_pad_desc ife_lite_pad_desc_v30x[] = {
+	{
+		.pad_flags = MEDIA_PAD_FL_SINK,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v30x,
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v30x,
+		.video_name = "rdi0",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI0,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI0,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.num_wms = 1,
+		.wm = { 0 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v30x,
+		.video_name = "rdi1",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI1,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI1,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.num_wms = 1,
+		.wm = { 1 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v30x,
+		.video_name = "rdi2",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI2,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI2,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.num_wms = 1,
+		.wm = { 2 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v30x,
+		.video_name = "rdi3",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI3,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI3,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.num_wms = 1,
+		.wm = { 3 },
+	},
+};
+
+const struct ife_pads_descriptors ife_lite_pads_v30x = {
+	.num_desc = ARRAY_SIZE(ife_lite_pad_desc_v30x),
+	.desc = ife_lite_pad_desc_v30x,
+};
+
+static const struct ife_pad_desc ife_pad_desc_v41x[] = {
+	{
+		.pad_flags = MEDIA_PAD_FL_SINK,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v41x,
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_vid_pix_fmts_v20x),
+			.formats = ife_vid_pix_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v41x,
+		.video_name = "vid_full",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_VID_FULL,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+		.packing_fmt = 0x1,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_LINE_BASED,
+		.num_wms = 2,
+		.wm = { 0, 1 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_vid_pix_fmts_v20x),
+			.formats = ife_vid_pix_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v41x,
+		.video_name = "disp_full",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_DISP_FULL,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+		.packing_fmt = 0x1,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_LINE_BASED,
+		.num_wms = 2,
+		.wm = { 3, 4 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v41x,
+		.video_name = "rdi0",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI0,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI0,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.num_wms = 1,
+		.wm = { 16 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v41x,
+		.video_name = "rdi1",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI1,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI1,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.num_wms = 1,
+		.wm = { 17 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_sink_fmts_v20x),
+			.formats = ife_sink_fmts_v20x,
+		},
+		.limits = &ife_vid_limits_v41x,
+		.video_name = "rdi2",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.output = CAMSS_IFE_OUTPUT_RDI2,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI2,
+		.packing_fmt = 0x0,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.num_wms = 1,
+		.wm = { 18 },
+	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SINK,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_params_fmts_v20x),
+			.formats = ife_params_fmts_v20x,
+		},
+		.limits = &ife_meta_limits_v20x,
+		.video_name = "params",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.meta_dataformat = V4L2_META_FMT_QC_IFE_PARAMS,
+		.meta_buffersize = PAGE_ALIGN(v4l2_isp_buffer_size(CAMSS_IFE_PARAMS_MAX_PAYLOAD)),
+	},
+};
+
+const struct ife_pads_descriptors ife_pads_v41x = {
+	.num_desc = ARRAY_SIZE(ife_pad_desc_v41x),
+	.desc = ife_pad_desc_v41x,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index eff4ab4bd1c9..0ca276203bbb 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -395,6 +395,7 @@ camss_ife_pipe_for_stream_id(int nstreams,
 			     u32 id);
 
 extern const struct ife_pads_descriptors ife_pads_v20x;
+extern const struct ife_pads_descriptors ife_pads_v41x;
 extern const struct ife_hw_top_ops ife_top_20x_ops;
 extern const struct ife_hw_top_ops ife_top_v41x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
@@ -402,6 +403,7 @@ extern const struct ife_hw_buswr_ops ife_buswr_31x_ops;
 extern const struct camss_mod_ops ife_mod_v10x_ops;
 
 extern const struct ife_pads_descriptors ife_lite_pads_v20x;
+extern const struct ife_pads_descriptors ife_lite_pads_v30x;
 extern const struct ife_hw_top_ops ife_lite_top_10x_ops;
 extern const struct ife_hw_top_ops ife_lite_top_30x_ops;
 
-- 
2.34.1


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

* [PATCH 32/45] media: qcom: camss: sa8775p: add IFE completion groups
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (30 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 31/45] media: qcom: camss: sa8775p: add IFE pad descriptors Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 33/45] media: qcom: camss: sa8775p: add CSID pixel path Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (13 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Describe the SA8775P completion groups and statistics output.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-pads.c      | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-pads.c b/drivers/media/platform/qcom/camss/camss-ife-pads.c
index b1e6204ceb21..38e87a1b3826 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-pads.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-pads.c
@@ -484,6 +484,8 @@ static const struct ife_pad_desc ife_pad_desc_v41x[] = {
 		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
 		.packing_fmt = 0x1,
 		.mode_config = BUSWR_CLIENT_CFG_MODE_LINE_BASED,
+		.rup_group_mask = BIT(0),
+		.comp_group_mask = BIT(0),
 		.num_wms = 2,
 		.wm = { 0, 1 },
 	},
@@ -501,6 +503,8 @@ static const struct ife_pad_desc ife_pad_desc_v41x[] = {
 		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
 		.packing_fmt = 0x1,
 		.mode_config = BUSWR_CLIENT_CFG_MODE_LINE_BASED,
+		.rup_group_mask = BIT(0),
+		.comp_group_mask = BIT(3),
 		.num_wms = 2,
 		.wm = { 3, 4 },
 	},
@@ -518,6 +522,8 @@ static const struct ife_pad_desc ife_pad_desc_v41x[] = {
 		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI0,
 		.packing_fmt = 0x0,
 		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(1),
+		.comp_group_mask = BIT(11),
 		.num_wms = 1,
 		.wm = { 16 },
 	},
@@ -535,6 +541,8 @@ static const struct ife_pad_desc ife_pad_desc_v41x[] = {
 		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI1,
 		.packing_fmt = 0x0,
 		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(2),
+		.comp_group_mask = BIT(12),
 		.num_wms = 1,
 		.wm = { 17 },
 	},
@@ -552,9 +560,32 @@ static const struct ife_pad_desc ife_pad_desc_v41x[] = {
 		.hw_pipe = CAMSS_IFE_HW_PIPE_RDI2,
 		.packing_fmt = 0x0,
 		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(3),
+		.comp_group_mask = BIT(13),
 		.num_wms = 1,
 		.wm = { 18 },
 	},
+	{
+		.pad_flags = MEDIA_PAD_FL_SOURCE,
+		.sup_fmts = {
+			.nformats = ARRAY_SIZE(ife_stats_fmts_v20x),
+			.formats = ife_stats_fmts_v20x,
+		},
+		.limits = &ife_meta_limits_v20x,
+		.video_name = "stats",
+		.link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.meta_dataformat = V4L2_META_FMT_QC_IFE_STAT_3A,
+		.meta_buffersize = PAGE_ALIGN(sizeof(struct camss_ife_stats)),
+		.output = CAMSS_IFE_OUTPUT_STATS,
+		.hw_pipe = CAMSS_IFE_HW_PIPE_PP,
+		.packing_fmt = 0xa,
+		.mode_config = BUSWR_CLIENT_CFG_MODE_FRAME_BASED,
+		.rup_group_mask = BIT(0),
+		.comp_group_mask = BIT(5),
+		.num_wms = 1,
+		.wm = { 12 },
+	},
 	{
 		.pad_flags = MEDIA_PAD_FL_SINK,
 		.sup_fmts = {
-- 
2.34.1


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

* [PATCH 33/45] media: qcom: camss: sa8775p: add CSID pixel path
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (31 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 32/45] media: qcom: camss: sa8775p: add IFE completion groups Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 34/45] media: qcom: camss: sa8775p: add IFE modules v4.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (12 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Extend the common generation 3 CSID backend with SA8775P streams.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-csid-gen3.c     | 265 +++++++++++++++++-
 .../media/platform/qcom/camss/camss-csid.c    |   1 +
 .../media/platform/qcom/camss/camss-csid.h    |   4 +
 drivers/media/platform/qcom/camss/camss-ife.c |  23 ++
 drivers/media/platform/qcom/camss/camss-ife.h |   2 +
 drivers/media/platform/qcom/camss/camss.c     |   1 +
 drivers/media/platform/qcom/camss/camss.h     |   1 +
 7 files changed, 291 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c
index 0fdbf75fb27d..e44b42215461 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c
@@ -4,6 +4,7 @@
  *
  * Copyright (c) 2024 Qualcomm Technologies, Inc.
  */
+#include <linux/bitfield.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -19,6 +20,9 @@
 #define		OUTPUT_IFE_EN			0x100
 #define		INTERNAL_CSID			1
 
+#define CSID_GLOBAL_CMD			0x8
+#define		GLOBAL_CMD_START		BIT(0)
+
 #define CSID_RST_CFG			0xC
 #define		RST_MODE			BIT(0)
 #define		RST_LOCATION			BIT(4)
@@ -31,10 +35,12 @@
 #define		IRQ_CMD_CLEAR			BIT(0)
 
 #define CSID_RUP_AUP_CMD		0x18
+#define		CSID_RUP_AUP_IPP0		(BIT(0) | BIT(16))
 #define		CSID_RUP_AUP_RDI(rdi)		((BIT(4) | BIT(20)) << (rdi))
 
 #define CSID_TOP_IRQ_STATUS		0x7C
 #define		 TOP_IRQ_STATUS_RESET_DONE	BIT(0)
+#define		 TOP_IRQ_STATUS_BUF_DONE	BIT(14)
 
 #define CSID_TOP_IRQ_MASK		0x80
 #define CSID_TOP_IRQ_CLEAR		0x84
@@ -44,6 +50,7 @@
 #define CSID_CSI2_RX_IRQ_MASK		0xA0
 #define CSID_CSI2_RX_IRQ_CLEAR		0xA4
 #define CSID_CSI2_RX_IRQ_SET		0xA8
+#define		CSI2_RX_IRQ_ERROR_MASK		0x01bfb800
 
 #define IS_CSID_690(csid)	((csid->camss->res->version == CAMSS_8775P) \
 				 || (csid->camss->res->version == CAMSS_8300))
@@ -54,6 +61,12 @@
 #define CSID_BUF_DONE_IRQ_MASK		0x90
 #define CSID_BUF_DONE_IRQ_CLEAR		0x94
 #define CSID_BUF_DONE_IRQ_SET		0x98
+#define		BUF_DONE_IRQ_STATUS_VID_FULL	BIT(0)
+#define		BUF_DONE_IRQ_STATUS_DISP_FULL	BIT(1)
+#define		BUF_DONE_IRQ_STATUS_STATS	BIT(5)
+#define		BUF_DONE_IRQ_STATUS_PP		(BUF_DONE_IRQ_STATUS_VID_FULL | \
+						 BUF_DONE_IRQ_STATUS_DISP_FULL | \
+						 BUF_DONE_IRQ_STATUS_STATS)
 
 #define CSID_CSI2_RDIN_IRQ_STATUS(rdi)	(0xEC + 0x10 * (rdi))
 #define		RUP_DONE_IRQ_STATUS		BIT(23)
@@ -73,6 +86,25 @@
 #define		CSI2_RX_CFG1_ECC_CORRECTION_EN	BIT(0)
 #define		CSI2_RX_CFG1_VC_MODE		BIT(2)
 
+#define CSID_IPP0_CFG0			0x300
+#define		IPP0_CFG0_EN			BIT(31)
+#define		IPP0_CFG0_VC			GENMASK(26, 22)
+#define		IPP0_CFG0_DT			GENMASK(21, 16)
+#define		IPP0_CFG0_DECODE_FMT		GENMASK(15, 12)
+
+#define CSID_IPP0_CTRL			0x304
+#define		IPP0_CTRL_START_CMD		1
+
+#define CSID_IPP0_CFG1			0x310
+#define		IPP0_CFG1_PIX_STORE_EN		BIT(14)
+#define		IPP0_CFG1_TIMESTAMP_EN		BIT(9)
+#define		IPP0_CFG1_MIN_HBI		GENMASK(7, 4)
+
+#define CSID_IPP0_EPOCH_IRQ_CFG			0x334
+#define CSID_IPP0_EPOCH0_SUBSAMPLE_PATTERN	0x338
+#define CSID_IPP0_EPOCH1_SUBSAMPLE_PATTERN	0x33c
+#define CSID_IPP0_IRQ_SUBSAMPLE_PERIOD		0x380
+
 #define CSID_RDI_CFG0(rdi)	(csid_is_lite(csid) && IS_CSID_690(csid) ?\
 					(0x300 + 0x100 * (rdi)) :\
 					(0x500 + 0x100 * (rdi)))
@@ -97,8 +129,13 @@
 #define		RDI_CFG1_CROP_H_EN		BIT(7)
 #define		RDI_CFG1_CROP_V_EN		BIT(8)
 #define		RDI_CFG1_PIX_STORE		BIT(10)
+#define		RDI_CFG1_TIMESTAMP_EN		BIT(4)
+#define		RDI_CFG1_TIMESTAMP_STB_SEL	GENMASK(1, 0)
 #define		RDI_CFG1_PACKING_FORMAT_MIPI	BIT(15)
 
+#define		ERROR_RECOVERY_CFG0_OVERFLOW_CTRL_EN	BIT(0)
+#define		ERROR_RECOVERY_CFG0_OVERFLOW_CTRL_MODE	BIT(3)
+
 #define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi)	(csid_is_lite(csid) && IS_CSID_690(csid) ?\
 							(0x348 + 0x100 * (rdi)) :\
 							(0x548 + 0x100 * (rdi)))
@@ -106,6 +143,39 @@
 							(0x34C + 0x100 * (rdi)) :\
 							(0x54C + 0x100 * (rdi)))
 #define CSI2_RX_CFG0_PHY_SEL_BASE_IDX	1
+#define CSID_PIX_STREAM_ID		3
+#define EPOCH_IRQ_CFG_EPOCH0_LINE	GENMASK(29, 16)
+#define EPOCH_IRQ_CFG_EPOCH1_LINE	GENMASK(13, 0)
+
+static bool csid_is_pix_stream(struct csid_device *csid, u32 stream_id)
+{
+	return stream_id == CSID_PIX_STREAM_ID && !csid_is_lite(csid);
+}
+
+static unsigned int csid_num_rdis(struct csid_device *csid)
+{
+	return csid_is_lite(csid) ? 4 : 3;
+}
+
+static u32 csid_rdi_error_recovery_cfg0(struct csid_device *csid, u32 rdi)
+{
+	return (csid_is_lite(csid) ? 0x314 : 0x514) + 0x100 * rdi;
+}
+
+static u32 csid_rdi_epoch_irq_cfg(struct csid_device *csid, u32 rdi)
+{
+	return (csid_is_lite(csid) ? 0x32c : 0x52c) + 0x100 * rdi;
+}
+
+static u32 csid_rdi_epoch0_pattern(struct csid_device *csid, u32 rdi)
+{
+	return (csid_is_lite(csid) ? 0x330 : 0x530) + 0x100 * rdi;
+}
+
+static u32 csid_rdi_epoch1_pattern(struct csid_device *csid, u32 rdi)
+{
+	return (csid_is_lite(csid) ? 0x334 : 0x534) + 0x100 * rdi;
+}
 
 static void __csid_configure_rx(struct csid_device *csid,
 				struct csid_phy_config *phy, int vc)
@@ -157,6 +227,133 @@ static void __csid_configure_wrapper(struct csid_device *csid)
 	writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id));
 }
 
+static void __csid_configure_ipp(struct csid_device *csid, u8 vc, u8 dt)
+{
+	struct v4l2_mbus_framefmt *input_format =
+		&csid->fmt[MSM_CSID_PAD_FIRST_SRC + CSID_PIX_STREAM_ID];
+	const struct csid_format_info *format =
+		csid_get_fmt_entry(csid->res->formats->formats,
+				   csid->res->formats->nformats, input_format->code);
+	u32 val;
+
+	val = IPP0_CFG0_EN;
+	val |= FIELD_PREP(IPP0_CFG0_VC, vc);
+	val |= FIELD_PREP(IPP0_CFG0_DT, dt);
+	val |= FIELD_PREP(IPP0_CFG0_DECODE_FMT, format->decode_format);
+	writel(val, csid->base + CSID_IPP0_CFG0);
+
+	val = IPP0_CFG1_PIX_STORE_EN | IPP0_CFG1_TIMESTAMP_EN;
+	val |= FIELD_PREP(IPP0_CFG1_MIN_HBI, 4);
+	writel(val, csid->base + CSID_IPP0_CFG1);
+
+	val = FIELD_PREP(EPOCH_IRQ_CFG_EPOCH0_LINE, input_format->height / 3);
+	val |= FIELD_PREP(EPOCH_IRQ_CFG_EPOCH1_LINE, input_format->height / 2);
+	writel(val, csid->base + CSID_IPP0_EPOCH_IRQ_CFG);
+	writel(1, csid->base + CSID_IPP0_EPOCH0_SUBSAMPLE_PATTERN);
+	writel(1, csid->base + CSID_IPP0_EPOCH1_SUBSAMPLE_PATTERN);
+	writel(0, csid->base + CSID_IPP0_IRQ_SUBSAMPLE_PERIOD);
+	writel(CSID_RUP_AUP_IPP0, csid->base + CSID_RUP_AUP_CMD);
+}
+
+static void __csid_ctrl_ipp(struct csid_device *csid, bool enable)
+{
+	if (!enable)
+		writel(0, csid->base + CSID_IPP0_CFG0);
+
+	writel(enable ? IPP0_CTRL_START_CMD : 0, csid->base + CSID_IPP0_CTRL);
+}
+
+static void __csid_configure_rdi_streams(struct csid_device *csid, bool enable,
+					 u32 port, u8 vc, u8 dt)
+{
+	struct v4l2_mbus_framefmt *input_format =
+		&csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
+	u32 val;
+
+	if (!enable) {
+		val = readl(csid->base + CSID_RDI_CFG0(port));
+		writel(val & ~RDI_CFG0_EN, csid->base + CSID_RDI_CFG0(port));
+		return;
+	}
+
+	val = DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
+	val |= dt << RDI_CFG0_DT;
+	val |= vc << RDI_CFG0_VC;
+	val |= (port & 0x3) << RDI_CFG0_DT_ID;
+	writel(val, csid->base + CSID_RDI_CFG0(port));
+
+	val = RDI_CFG1_PACKING_FORMAT_MIPI | RDI_CFG1_TIMESTAMP_EN |
+	      FIELD_PREP(RDI_CFG1_TIMESTAMP_STB_SEL, 2);
+	writel(val, csid->base + CSID_RDI_CFG1(port));
+
+	val = ERROR_RECOVERY_CFG0_OVERFLOW_CTRL_EN |
+	      ERROR_RECOVERY_CFG0_OVERFLOW_CTRL_MODE;
+	writel(val, csid->base + csid_rdi_error_recovery_cfg0(csid, port));
+	writel(0, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));
+	writel(1, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));
+
+	val = FIELD_PREP(EPOCH_IRQ_CFG_EPOCH0_LINE, input_format->height / 3);
+	val |= FIELD_PREP(EPOCH_IRQ_CFG_EPOCH1_LINE, input_format->height / 2);
+	writel(val, csid->base + csid_rdi_epoch_irq_cfg(csid, port));
+	writel(1, csid->base + csid_rdi_epoch0_pattern(csid, port));
+	writel(1, csid->base + csid_rdi_epoch1_pattern(csid, port));
+	writel(CSID_RUP_AUP_RDI(port), csid->base + CSID_RUP_AUP_CMD);
+
+	writel(0, csid->base + CSID_RDI_CTRL(port));
+	val = readl(csid->base + CSID_RDI_CFG0(port));
+	writel(val | RDI_CFG0_EN, csid->base + CSID_RDI_CFG0(port));
+}
+
+static void csid_buf_done_irq(struct csid_device *csid, u32 mask, bool enable)
+{
+	unsigned long flags;
+	u32 top_mask;
+	u32 val;
+
+	spin_lock_irqsave(&csid->irq_lock, flags);
+
+	top_mask = readl(csid->base + CSID_TOP_IRQ_MASK);
+	val = readl(csid->base + CSID_BUF_DONE_IRQ_MASK);
+
+	writel(mask, csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+	if (enable) {
+		val |= mask;
+		top_mask |= TOP_IRQ_STATUS_BUF_DONE;
+	} else {
+		val &= ~mask;
+		if (!val)
+			top_mask &= ~TOP_IRQ_STATUS_BUF_DONE;
+	}
+
+	writel(val, csid->base + CSID_BUF_DONE_IRQ_MASK);
+	writel(top_mask, csid->base + CSID_TOP_IRQ_MASK);
+	writel(IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+
+	spin_unlock_irqrestore(&csid->irq_lock, flags);
+}
+
+static void csid_buf_done(struct csid_device *csid, u32 status)
+{
+	const struct parent_dev_ops *ops = csid->res->parent_dev_ops;
+	unsigned int i;
+
+	if (!ops->buf_done)
+		return;
+
+	if (!csid_is_lite(csid)) {
+		if (status & BUF_DONE_IRQ_STATUS_VID_FULL)
+			ops->buf_done(csid->camss, csid->id, CAMSS_IFE_OUTPUT_VID_FULL);
+		if (status & BUF_DONE_IRQ_STATUS_DISP_FULL)
+			ops->buf_done(csid->camss, csid->id, CAMSS_IFE_OUTPUT_DISP_FULL);
+		if (status & BUF_DONE_IRQ_STATUS_STATS)
+			ops->buf_done(csid->camss, csid->id, CAMSS_IFE_OUTPUT_STATS);
+	}
+
+	for (i = 0; i < csid_num_rdis(csid); i++)
+		if (status & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i))
+			ops->buf_done(csid->camss, csid->id, CAMSS_IFE_OUTPUT_RDI0 + i);
+}
+
 static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
 {
 	u32 val;
@@ -234,6 +431,47 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
 		}
 }
 
+static void csid_configure_rx(struct csid_device *csid)
+{
+	__csid_configure_wrapper(csid);
+	__csid_configure_rx(csid, &csid->phy, 0);
+	writel(CSI2_RX_IRQ_ERROR_MASK, csid->base + CSID_CSI2_RX_IRQ_MASK);
+	writel(GLOBAL_CMD_START, csid->base + CSID_GLOBAL_CMD);
+}
+
+static void csid_enable_stream(struct csid_device *csid, u32 stream_id, u8 vc, u8 dt)
+{
+	u32 mask;
+
+	if (csid_is_pix_stream(csid, stream_id)) {
+		__csid_configure_ipp(csid, vc, dt);
+		mask = BUF_DONE_IRQ_STATUS_PP;
+		csid_buf_done_irq(csid, mask, true);
+		__csid_ctrl_ipp(csid, true);
+	} else if (stream_id < csid_num_rdis(csid)) {
+		mask = BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + stream_id);
+		csid_buf_done_irq(csid, mask, true);
+		__csid_configure_rdi_streams(csid, true, stream_id, vc, dt);
+		__csid_ctrl_rdi(csid, true, stream_id);
+	}
+}
+
+static void csid_disable_stream(struct csid_device *csid, u32 stream_id)
+{
+	u32 mask;
+
+	if (csid_is_pix_stream(csid, stream_id)) {
+		mask = BUF_DONE_IRQ_STATUS_PP;
+		csid_buf_done_irq(csid, mask, false);
+		__csid_ctrl_ipp(csid, false);
+	} else if (stream_id < csid_num_rdis(csid)) {
+		mask = BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + stream_id);
+		csid_buf_done_irq(csid, mask, false);
+		__csid_ctrl_rdi(csid, false, stream_id);
+		__csid_configure_rdi_streams(csid, false, stream_id, 0, 0);
+	}
+}
+
 static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
 {
 	return 0;
@@ -259,10 +497,14 @@ static void csid_subdev_reg_update(struct csid_device *csid, int port_id, bool c
 static irqreturn_t csid_isr(int irq, void *dev)
 {
 	struct csid_device *csid = dev;
-	u32 val, buf_done_val;
+	unsigned long flags;
+	u32 buf_done_val;
+	u32 val;
 	u8 reset_done;
 	int i;
 
+	spin_lock_irqsave(&csid->irq_lock, flags);
+
 	val = readl(csid->base + CSID_TOP_IRQ_STATUS);
 	writel(val, csid->base + CSID_TOP_IRQ_CLEAR);
 	reset_done = val & TOP_IRQ_STATUS_RESET_DONE;
@@ -282,8 +524,19 @@ static irqreturn_t csid_isr(int irq, void *dev)
 			if (val & RUP_DONE_IRQ_STATUS)
 				/* clear the reg update bit */
 				csid_subdev_reg_update(csid, i, true);
+		}
+
+	val = IRQ_CMD_CLEAR;
+	writel(val, csid->base + CSID_IRQ_CMD);
+
+	spin_unlock_irqrestore(&csid->irq_lock, flags);
 
-			if (buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i)) {
+	if (csid->res->streams_enable) {
+		csid_buf_done(csid, buf_done_val);
+	} else {
+		for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
+			if (csid->phy.en_vc & BIT(i) &&
+			    buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i)) {
 				/*
 				 * For Titan Gen3, bus done and RUP IRQ have been moved to
 				 * CSID from VFE. Once CSID received bus done, need notify
@@ -291,10 +544,7 @@ static irqreturn_t csid_isr(int irq, void *dev)
 				 */
 				camss_buf_done(csid->camss, csid->id, i);
 			}
-		}
-
-	val = IRQ_CMD_CLEAR;
-	writel(val, csid->base + CSID_IRQ_CMD);
+	}
 
 	if (reset_done)
 		complete(&csid->reset_complete);
@@ -354,6 +604,9 @@ static void csid_subdev_init(struct csid_device *csid)
 const struct csid_hw_ops csid_ops_gen3 = {
 	.configure_stream = csid_configure_stream,
 	.configure_testgen_pattern = csid_configure_testgen_pattern,
+	.configure_rx = csid_configure_rx,
+	.enable_stream = csid_enable_stream,
+	.disable_stream = csid_disable_stream,
 	.hw_version = csid_hw_version,
 	.isr = csid_isr,
 	.reset = csid_reset,
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index b7e6bfc58bcf..da13963ef1a0 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -1199,6 +1199,7 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
 		return ret;
 
 	init_completion(&csid->reset_complete);
+	spin_lock_init(&csid->irq_lock);
 
 	return 0;
 }
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index f079956bae8e..2d99b61eb98d 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -12,6 +12,8 @@
 
 #include <linux/clk.h>
 #include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
 #include <media/media-entity.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
@@ -191,6 +193,8 @@ struct csid_device {
 	struct regulator_bulk_data *supplies;
 	int num_supplies;
 	struct completion reset_complete;
+	/* Protects interrupt status, mask, and clear registers. */
+	spinlock_t irq_lock;
 	struct csid_testgen_config testgen;
 	bool tpg_linked;
 	struct csid_phy_config phy;
diff --git a/drivers/media/platform/qcom/camss/camss-ife.c b/drivers/media/platform/qcom/camss/camss-ife.c
index cc5cebfd0862..e2049e00ea96 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.c
+++ b/drivers/media/platform/qcom/camss/camss-ife.c
@@ -1290,6 +1290,29 @@ static const struct v4l2_subdev_ops ife_v4l2_ops = {
 	.pad	= &ife_pad_ops,
 };
 
+void camss_ife_buf_done(struct camss *camss, int id, enum camss_ife_output output)
+{
+	struct ife_device *ife;
+	unsigned long flags;
+	int i;
+
+	if (id >= camss->ife_total_num)
+		return;
+
+	ife = &camss->ife[id];
+	spin_lock_irqsave(&ife->bufs_lock, flags);
+
+	for (i = 0; i < ife->npads; i++) {
+		const struct ife_pad_desc *desc = ife->pads[i].desc;
+		struct ife_video_pad *vpad = ife->pads[i].vid_pad;
+
+		if (vpad && vpad->streaming && desc->output == output)
+			ife_hw_isr_wm_done(ife, vpad);
+	}
+
+	spin_unlock_irqrestore(&ife->bufs_lock, flags);
+}
+
 static int ife_video_start_streaming(struct camss_video *vid,
 				     unsigned int count)
 {
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 0ca276203bbb..3d35c17fca7f 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -394,6 +394,8 @@ camss_ife_pipe_for_stream_id(int nstreams,
 			     const struct camss_ife_stream *streams,
 			     u32 id);
 
+void camss_ife_buf_done(struct camss *camss, int id, enum camss_ife_output output);
+
 extern const struct ife_pads_descriptors ife_pads_v20x;
 extern const struct ife_pads_descriptors ife_pads_v41x;
 extern const struct ife_hw_top_ops ife_top_20x_ops;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 2dc7864ebe07..7a3e5e6f81e1 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -5185,6 +5185,7 @@ static const struct parent_dev_ops ife_parent_dev_ops = {
 	.get = ife_parent_dev_ops_get,
 	.put = ife_parent_dev_ops_put,
 	.get_base_address = ife_parent_dev_ops_get_base_address,
+	.buf_done = camss_ife_buf_done,
 };
 
 int camss_attach_host_data(struct camss *camss, struct v4l2_subdev *sd,
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 65582ff7db23..c19e95df0a54 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -201,6 +201,7 @@ struct parent_dev_ops {
 	int (*get)(struct camss *camss, int id);
 	int (*put)(struct camss *camss, int id);
 	void __iomem *(*get_base_address)(struct camss *camss, int id);
+	void (*buf_done)(struct camss *camss, int id, enum camss_ife_output output);
 };
 
 void camss_add_clock_margin(u64 *rate);
-- 
2.34.1


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

* [PATCH 34/45] media: qcom: camss: sa8775p: add IFE modules v4.1x
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (32 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 33/45] media: qcom: camss: sa8775p: add CSID pixel path Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 35/45] media: qcom: camss: sa8775p: add pixel path resources Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (11 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Add the module backend used by the SA8775P IFE pixel path. It
configures the default white balance, demosaic, color transform,
and AWB BG blocks.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/Makefile    |   1 +
 .../platform/qcom/camss/camss-ife-mod-v41x.c  | 328 ++++++++++++++++++
 drivers/media/platform/qcom/camss/camss-ife.h |   1 +
 3 files changed, 330 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c

diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 695924e0c03b..2e4c07fda524 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -21,6 +21,7 @@ qcom-camss-objs += \
 		camss-ife-camif-v10x.o \
 		camss-ife-lite-top-v10x.o \
 		camss-ife-mod-v10x.o \
+		camss-ife-mod-v41x.o \
 		camss-ife-params.o \
 		camss-ife-scale-v10x.o \
 		camss-ife-scale-v23x.o \
diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
new file mode 100644
index 000000000000..9f4c8ae9667e
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ *
+ * IFE pixel-path post-processing modules for the v41x register layout.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+
+#include "camss-ife.h"
+
+/* Module base offsets */
+#define IFE_MOD_V41X_WB_GAIN_OFFSET		0x4000
+#define IFE_MOD_V41X_DEMOSAIC_OFFSET		0x5580
+#define IFE_MOD_V41X_XFORM_OFFSET		0x6200
+#define IFE_MOD_V41X_AWB_BG_OFFSET		0x6800
+
+/* WB gain registers */
+#define IFE_MOD_V41X_WB_GAIN_MODULE_CFG		0x60
+#define IFE_MOD_V41X_WB_GAIN_CFG_0		0x68
+#define IFE_MOD_V41X_WB_GAIN_CFG_1		0x6c
+#define IFE_MOD_V41X_WB_GAIN_CFG_2		0x70
+
+/* Demosaic registers */
+#define IFE_MOD_V41X_DEMOSAIC_MODULE_CFG		0x60
+#define IFE_MOD_V41X_DEMOSAIC_INTERP_COEFF_CFG_0	0x68
+#define IFE_MOD_V41X_DEMOSAIC_INTERP_COEFF_CFG_1	0x6c
+#define IFE_MOD_V41X_DEMOSAIC_INTERP_CLASSIFIER_CFG_0	0x70
+#define IFE_MOD_V41X_DEMOSAIC_INTERP_CLASSIFIER_CFG_1	0x74
+
+/* Color transform (XFORM) registers, 3 channels */
+#define IFE_MOD_V41X_XFORM_MODULE_CFG		0x60
+#define IFE_MOD_V41X_XFORM_CH0_COEFF_CFG_0	0x68
+#define IFE_MOD_V41X_XFORM_CH0_COEFF_CFG_1	0x6c
+#define IFE_MOD_V41X_XFORM_CH0_ADDOFFSET_CFG	0x70
+#define IFE_MOD_V41X_XFORM_CH0_SUBOFFSET_CFG	0x74
+#define IFE_MOD_V41X_XFORM_CH0_CLAMP0_CFG	0x78
+#define IFE_MOD_V41X_XFORM_CH0_CLAMP1_CFG	0x7c
+#define IFE_MOD_V41X_XFORM_CH1_COEFF_CFG_0	0x80
+#define IFE_MOD_V41X_XFORM_CH1_COEFF_CFG_1	0x84
+#define IFE_MOD_V41X_XFORM_CH1_ADDOFFSET_CFG	0x88
+#define IFE_MOD_V41X_XFORM_CH1_SUBOFFSET_CFG	0x8c
+#define IFE_MOD_V41X_XFORM_CH1_CLAMP0_CFG	0x90
+#define IFE_MOD_V41X_XFORM_CH1_CLAMP1_CFG	0x94
+#define IFE_MOD_V41X_XFORM_CH2_COEFF_CFG_0	0x98
+#define IFE_MOD_V41X_XFORM_CH2_COEFF_CFG_1	0x9c
+#define IFE_MOD_V41X_XFORM_CH2_ADDOFFSET_CFG	0xa0
+#define IFE_MOD_V41X_XFORM_CH2_SUBOFFSET_CFG	0xa4
+#define IFE_MOD_V41X_XFORM_CH2_CLAMP0_CFG	0xa8
+#define IFE_MOD_V41X_XFORM_CH2_CLAMP1_CFG	0xac
+
+/* AWB-BG registers */
+#define IFE_MOD_V41X_AWB_BG_MODULE_CFG		0x60
+#define IFE_MOD_V41X_AWB_BG_HORZ_RGN_CFG_0	0x6c
+#define IFE_MOD_V41X_AWB_BG_HORZ_RGN_CFG_1	0x70
+#define IFE_MOD_V41X_AWB_BG_VERT_RGN_CFG_0	0x74
+#define IFE_MOD_V41X_AWB_BG_VERT_RGN_CFG_1	0x78
+#define IFE_MOD_V41X_AWB_BG_RGN_WIDTH_CFG	0x7c
+#define IFE_MOD_V41X_AWB_BG_HI_THRESHOLD_CFG_0	0x80
+#define IFE_MOD_V41X_AWB_BG_LO_THRESHOLD_CFG_0	0x94
+
+/* WB gain bitfields */
+#define IFE_MOD_V41X_WB_GAIN_CFG_EN		BIT(0)
+#define IFE_MOD_V41X_WB_GAIN_VALUE		GENMASK(31, 17)
+
+/* Demosaic bitfields */
+#define IFE_MOD_V41X_DEMOSAIC_CFG_EN			BIT(0)
+#define IFE_MOD_V41X_DEMOSAIC_CFG_DYN_G_CLAMP_EN	BIT(14)
+#define IFE_MOD_V41X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN	BIT(15)
+
+/* Color transform bitfields */
+#define IFE_MOD_V41X_XFORM_CFG_EN		BIT(0)
+#define IFE_MOD_V41X_XFORM_MATRIX_M0		GENMASK(12, 0)
+#define IFE_MOD_V41X_XFORM_MATRIX_M1		GENMASK(28, 16)
+#define IFE_MOD_V41X_XFORM_OFFSET_VALUE		GENMASK(31, 17)
+#define IFE_MOD_V41X_XFORM_CLAMP_VALUE		GENMASK(31, 18)
+
+/* AWB-BG bitfields */
+#define IFE_MOD_V41X_AWB_BG_MODULE_CFG_EN	BIT(0)
+#define IFE_MOD_V41X_AWB_BG_RGN_SAMPLE_PATTERN	GENMASK(31, 16)
+#define IFE_MOD_V41X_AWB_BG_HORZ_CFG0_H_NUM	GENMASK(21, 16)
+#define IFE_MOD_V41X_AWB_BG_HORZ_CFG1_WIDTH	GENMASK(8, 0)
+#define IFE_MOD_V41X_AWB_BG_VERT_CFG0_V_NUM	GENMASK(21, 16)
+#define IFE_MOD_V41X_AWB_BG_VERT_CFG1_HEIGHT	GENMASK(8, 0)
+#define IFE_MOD_V41X_AWB_BG_RGN_WIDTH_LAST	GENMASK(24, 16)
+#define IFE_MOD_V41X_AWB_BG_RGN_WIDTH_FIRST	GENMASK(8, 0)
+#define IFE_MOD_V41X_AWB_BG_HI_THRESHOLD	GENMASK(31, 8)
+
+/* WB gain default */
+#define IFE_MOD_V41X_WB_GAIN_UNITY		0x400
+
+/* Demosaic defaults */
+#define IFE_MOD_V41X_DEMOSAIC_LAMBDA_G		0x80
+#define IFE_MOD_V41X_DEMOSAIC_LAMBDA_RB		0
+#define IFE_MOD_V41X_DEMOSAIC_A_K_DEFAULT	0x08000000
+#define IFE_MOD_V41X_DEMOSAIC_W_K_DEFAULT	0x66
+
+/* Color transform defaults */
+#define IFE_MOD_V41X_XFORM_MSB_SHIFT		4
+#define IFE_MOD_V41X_XFORM_CHROMA_OFFSET	0x200
+#define IFE_MOD_V41X_XFORM_CLAMP_MAX		0x3ff
+
+/* AWB-BG defaults */
+#define IFE_MOD_V41X_AWB_BG_MAX_H_NUM		64
+#define IFE_MOD_V41X_AWB_BG_MAX_V_NUM		48
+#define IFE_MOD_V41X_AWB_BG_SAMPLE_PATTERN	GENMASK(15, 0)
+#define IFE_MOD_V41X_AWB_BG_THRESHOLD		0x3ffff
+#define IFE_MOD_V41X_AWB_BG_NUM_THRESHOLDS	5
+
+static void mod_v41x_wb_gain_config(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
+	u32 value = FIELD_PREP(IFE_MOD_V41X_WB_GAIN_VALUE,
+			       IFE_MOD_V41X_WB_GAIN_UNITY);
+
+	writel_relaxed(value, base + IFE_MOD_V41X_WB_GAIN_CFG_0);
+	writel_relaxed(value, base + IFE_MOD_V41X_WB_GAIN_CFG_1);
+	writel_relaxed(value, base + IFE_MOD_V41X_WB_GAIN_CFG_2);
+}
+
+static void mod_v41x_demosaic_config(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_DEMOSAIC_OFFSET;
+
+	writel_relaxed(IFE_MOD_V41X_DEMOSAIC_LAMBDA_RB,
+		       base + IFE_MOD_V41X_DEMOSAIC_INTERP_COEFF_CFG_0);
+	writel_relaxed(IFE_MOD_V41X_DEMOSAIC_LAMBDA_G,
+		       base + IFE_MOD_V41X_DEMOSAIC_INTERP_COEFF_CFG_1);
+	writel_relaxed(IFE_MOD_V41X_DEMOSAIC_A_K_DEFAULT,
+		       base + IFE_MOD_V41X_DEMOSAIC_INTERP_CLASSIFIER_CFG_0);
+	writel_relaxed(IFE_MOD_V41X_DEMOSAIC_W_K_DEFAULT,
+		       base + IFE_MOD_V41X_DEMOSAIC_INTERP_CLASSIFIER_CFG_1);
+}
+
+static void mod_v41x_awb_bg_config(struct ife_pp_ctx *ctx,
+				   const struct camss_pp_cfg *cfg)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_AWB_BG_OFFSET;
+	unsigned int i;
+	u32 height = (cfg->height / IFE_MOD_V41X_AWB_BG_MAX_V_NUM) & ~3u;
+	u32 width = (cfg->width / IFE_MOD_V41X_AWB_BG_MAX_H_NUM) & ~3u;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_AWB_BG_MODULE_CFG);
+	value &= ~IFE_MOD_V41X_AWB_BG_RGN_SAMPLE_PATTERN;
+	value |= FIELD_PREP(IFE_MOD_V41X_AWB_BG_RGN_SAMPLE_PATTERN,
+			    IFE_MOD_V41X_AWB_BG_SAMPLE_PATTERN);
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_MODULE_CFG);
+
+	value = FIELD_PREP(IFE_MOD_V41X_AWB_BG_HORZ_CFG0_H_NUM,
+			   IFE_MOD_V41X_AWB_BG_MAX_H_NUM - 1);
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_HORZ_RGN_CFG_0);
+
+	value = FIELD_PREP(IFE_MOD_V41X_AWB_BG_HORZ_CFG1_WIDTH, width - 1);
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_HORZ_RGN_CFG_1);
+
+	value = FIELD_PREP(IFE_MOD_V41X_AWB_BG_VERT_CFG0_V_NUM,
+			   IFE_MOD_V41X_AWB_BG_MAX_V_NUM - 1);
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_VERT_RGN_CFG_0);
+
+	value = FIELD_PREP(IFE_MOD_V41X_AWB_BG_VERT_CFG1_HEIGHT, height - 1);
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_VERT_RGN_CFG_1);
+
+	value = FIELD_PREP(IFE_MOD_V41X_AWB_BG_RGN_WIDTH_FIRST, width - 1) |
+		FIELD_PREP(IFE_MOD_V41X_AWB_BG_RGN_WIDTH_LAST, width - 1);
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_RGN_WIDTH_CFG);
+
+	value = FIELD_PREP(IFE_MOD_V41X_AWB_BG_HI_THRESHOLD,
+			   IFE_MOD_V41X_AWB_BG_THRESHOLD);
+	for (i = 0; i < IFE_MOD_V41X_AWB_BG_NUM_THRESHOLDS; i++) {
+		writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_HI_THRESHOLD_CFG_0 + i * 4);
+		writel_relaxed(0, base + IFE_MOD_V41X_AWB_BG_LO_THRESHOLD_CFG_0 + i * 4);
+	}
+}
+
+static void mod_v41x_awb_bg_enable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_AWB_BG_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_AWB_BG_MODULE_CFG);
+	value |= IFE_MOD_V41X_AWB_BG_MODULE_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_MODULE_CFG);
+}
+
+static void mod_v41x_awb_bg_disable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_AWB_BG_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_AWB_BG_MODULE_CFG);
+	value &= ~IFE_MOD_V41X_AWB_BG_MODULE_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_AWB_BG_MODULE_CFG);
+}
+
+static void mod_v41x_xform_write_coeff(void __iomem *base, u32 offset,
+				       u32 m0, u32 m1)
+{
+	u32 value;
+
+	value = FIELD_PREP(IFE_MOD_V41X_XFORM_MATRIX_M0, m0) |
+		FIELD_PREP(IFE_MOD_V41X_XFORM_MATRIX_M1, m1);
+	writel_relaxed(value, base + offset);
+}
+
+static void mod_v41x_xform_write_channel(void __iomem *base, u32 coeff0,
+					 u32 coeff1, u32 add_offset,
+					 u32 sub_offset, u32 clamp0,
+					 u32 clamp1, u32 m0, u32 m1,
+					 u32 m2, u32 offset)
+{
+	mod_v41x_xform_write_coeff(base, coeff0, m0, m1);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_XFORM_MATRIX_M0, m2), base + coeff1);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_XFORM_OFFSET_VALUE,
+				  offset << IFE_MOD_V41X_XFORM_MSB_SHIFT),
+		       base + add_offset);
+	writel_relaxed(0, base + sub_offset);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_XFORM_CLAMP_VALUE, 0), base + clamp0);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_XFORM_CLAMP_VALUE,
+				  IFE_MOD_V41X_XFORM_CLAMP_MAX <<
+				    IFE_MOD_V41X_XFORM_MSB_SHIFT),
+		       base + clamp1);
+}
+
+static void mod_v41x_modules_acquire(struct ife_device *ife, struct ife_pp_ctx *ctx,
+				     void __iomem *base)
+{
+	ctx->base = base;
+}
+
+static void mod_v41x_modules_release(struct ife_pp_ctx *ctx)
+{
+	ctx->base = NULL;
+}
+
+static void mod_v41x_modules_config(struct ife_pp_ctx *ctx,
+				    const struct camss_pp_cfg *cfg)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_XFORM_OFFSET;
+
+	ctx->in_width = cfg->width;
+	ctx->in_height = cfg->height;
+
+	mod_v41x_wb_gain_config(ctx);
+	mod_v41x_demosaic_config(ctx);
+	mod_v41x_awb_bg_config(ctx, cfg);
+
+	mod_v41x_xform_write_channel(base, IFE_MOD_V41X_XFORM_CH0_COEFF_CFG_0,
+				     IFE_MOD_V41X_XFORM_CH0_COEFF_CFG_1,
+				     IFE_MOD_V41X_XFORM_CH0_ADDOFFSET_CFG,
+				     IFE_MOD_V41X_XFORM_CH0_SUBOFFSET_CFG,
+				     IFE_MOD_V41X_XFORM_CH0_CLAMP0_CFG,
+				     IFE_MOD_V41X_XFORM_CH0_CLAMP1_CFG,
+				     0x259, 0x75, 0x132, 0);
+	mod_v41x_xform_write_channel(base, IFE_MOD_V41X_XFORM_CH1_COEFF_CFG_0,
+				     IFE_MOD_V41X_XFORM_CH1_COEFF_CFG_1,
+				     IFE_MOD_V41X_XFORM_CH1_ADDOFFSET_CFG,
+				     IFE_MOD_V41X_XFORM_CH1_SUBOFFSET_CFG,
+				     IFE_MOD_V41X_XFORM_CH1_CLAMP0_CFG,
+				     IFE_MOD_V41X_XFORM_CH1_CLAMP1_CFG,
+				     0x1eae, 0x1fe, 0x1f54,
+				     IFE_MOD_V41X_XFORM_CHROMA_OFFSET);
+	mod_v41x_xform_write_channel(base, IFE_MOD_V41X_XFORM_CH2_COEFF_CFG_0,
+				     IFE_MOD_V41X_XFORM_CH2_COEFF_CFG_1,
+				     IFE_MOD_V41X_XFORM_CH2_ADDOFFSET_CFG,
+				     IFE_MOD_V41X_XFORM_CH2_SUBOFFSET_CFG,
+				     IFE_MOD_V41X_XFORM_CH2_CLAMP0_CFG,
+				     IFE_MOD_V41X_XFORM_CH2_CLAMP1_CFG,
+				     0x1e55, 0x1fad, 0x1fe,
+				     IFE_MOD_V41X_XFORM_CHROMA_OFFSET);
+}
+
+static void mod_v41x_modules_enable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_WB_GAIN_MODULE_CFG);
+	value |= IFE_MOD_V41X_WB_GAIN_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_WB_GAIN_MODULE_CFG);
+
+	base = ctx->base + IFE_MOD_V41X_DEMOSAIC_OFFSET;
+	value = readl_relaxed(base + IFE_MOD_V41X_DEMOSAIC_MODULE_CFG);
+	value |= IFE_MOD_V41X_DEMOSAIC_CFG_DYN_G_CLAMP_EN |
+		 IFE_MOD_V41X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN |
+		 IFE_MOD_V41X_DEMOSAIC_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_DEMOSAIC_MODULE_CFG);
+
+	base = ctx->base + IFE_MOD_V41X_XFORM_OFFSET;
+	value = readl_relaxed(base + IFE_MOD_V41X_XFORM_MODULE_CFG);
+	value |= IFE_MOD_V41X_XFORM_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_XFORM_MODULE_CFG);
+
+	mod_v41x_awb_bg_enable(ctx);
+}
+
+static void mod_v41x_modules_disable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
+	u32 value;
+
+	mod_v41x_awb_bg_disable(ctx);
+
+	value = readl_relaxed(base + IFE_MOD_V41X_WB_GAIN_MODULE_CFG);
+	value &= ~IFE_MOD_V41X_WB_GAIN_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_WB_GAIN_MODULE_CFG);
+
+	base = ctx->base + IFE_MOD_V41X_DEMOSAIC_OFFSET;
+	value = readl_relaxed(base + IFE_MOD_V41X_DEMOSAIC_MODULE_CFG);
+	value &= ~(IFE_MOD_V41X_DEMOSAIC_CFG_DYN_G_CLAMP_EN |
+		   IFE_MOD_V41X_DEMOSAIC_CFG_DYN_RB_CLAMP_EN |
+		   IFE_MOD_V41X_DEMOSAIC_CFG_EN);
+	writel_relaxed(value, base + IFE_MOD_V41X_DEMOSAIC_MODULE_CFG);
+
+	base = ctx->base + IFE_MOD_V41X_XFORM_OFFSET;
+	value = readl_relaxed(base + IFE_MOD_V41X_XFORM_MODULE_CFG);
+	value &= ~IFE_MOD_V41X_XFORM_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_XFORM_MODULE_CFG);
+}
+
+const struct camss_mod_ops ife_mod_v41x_ops = {
+	.acquire	= mod_v41x_modules_acquire,
+	.release	= mod_v41x_modules_release,
+	.config		= mod_v41x_modules_config,
+	.enable		= mod_v41x_modules_enable,
+	.disable	= mod_v41x_modules_disable,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index 3d35c17fca7f..c38b96076a1f 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -403,6 +403,7 @@ extern const struct ife_hw_top_ops ife_top_v41x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
 extern const struct ife_hw_buswr_ops ife_buswr_31x_ops;
 extern const struct camss_mod_ops ife_mod_v10x_ops;
+extern const struct camss_mod_ops ife_mod_v41x_ops;
 
 extern const struct ife_pads_descriptors ife_lite_pads_v20x;
 extern const struct ife_pads_descriptors ife_lite_pads_v30x;
-- 
2.34.1


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

* [PATCH 35/45] media: qcom: camss: sa8775p: add pixel path resources
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (33 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 34/45] media: qcom: camss: sa8775p: add IFE modules v4.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 36/45] media: qcom: camss: sa8775p: add CCM parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (10 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Describe the CSIPHY, CSID, IFE, and IFE Lite resources used by the
SA8775P pixel path. Add a separate compatible so the new path can be
enabled without changing the legacy resources.

Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss.c | 424 ++++++++++++++++++++++
 1 file changed, 424 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 7a3e5e6f81e1..a0b51c12c0bf 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -4382,6 +4382,412 @@ static const struct camss_subdev_resources vfe_res_8775p[] = {
 	},
 };
 
+static const struct camss_subdev_resources csiphy_res_sa8775p_pix[] = {
+	/* CSIPHY0 */
+	{
+		.regulators = {
+			{ .supply = "vdda-phy", .init_load_uA = 15900 },
+			{ .supply = "vdda-pll", .init_load_uA = 8900 }
+		},
+		.clock = { "csiphy_rx", "csiphy0", "csiphy0_timer" },
+		.clock_rate = {
+			{ 400000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "csiphy0" },
+		.interrupt = { "csiphy0" },
+		.csiphy = {
+			.id = 0,
+			.streams_enable = true,
+			.hw_ops = &csiphy_ops_3ph_1_0,
+			.formats = &csiphy_formats_sdm845
+		}
+	},
+	/* CSIPHY1 */
+	{
+		.regulators = {
+			{ .supply = "vdda-phy", .init_load_uA = 15900 },
+			{ .supply = "vdda-pll", .init_load_uA = 8900 }
+		},
+		.clock = { "csiphy_rx", "csiphy1", "csiphy1_timer" },
+		.clock_rate = {
+			{ 400000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "csiphy1" },
+		.interrupt = { "csiphy1" },
+		.csiphy = {
+			.id = 1,
+			.streams_enable = true,
+			.hw_ops = &csiphy_ops_3ph_1_0,
+			.formats = &csiphy_formats_sdm845
+		}
+	},
+	/* CSIPHY2 */
+	{
+		.regulators = {
+			{ .supply = "vdda-phy", .init_load_uA = 15900 },
+			{ .supply = "vdda-pll", .init_load_uA = 8900 }
+		},
+		.clock = { "csiphy_rx", "csiphy2", "csiphy2_timer" },
+		.clock_rate = {
+			{ 400000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "csiphy2" },
+		.interrupt = { "csiphy2" },
+		.csiphy = {
+			.id = 2,
+			.streams_enable = true,
+			.hw_ops = &csiphy_ops_3ph_1_0,
+			.formats = &csiphy_formats_sdm845
+		}
+	},
+	/* CSIPHY3 */
+	{
+		.regulators = {
+			{ .supply = "vdda-phy", .init_load_uA = 15900 },
+			{ .supply = "vdda-pll", .init_load_uA = 8900 }
+		},
+		.clock = { "csiphy_rx", "csiphy3", "csiphy3_timer" },
+		.clock_rate = {
+			{ 400000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "csiphy3" },
+		.interrupt = { "csiphy3" },
+		.csiphy = {
+			.id = 3,
+			.streams_enable = true,
+			.hw_ops = &csiphy_ops_3ph_1_0,
+			.formats = &csiphy_formats_sdm845
+		}
+	},
+};
+
+static const struct camss_ife_stream csid_ife_streams_sa8775p_pix[] = {
+	{ .id = 0, .pipe = CAMSS_IFE_HW_PIPE_RDI0 },
+	{ .id = 1, .pipe = CAMSS_IFE_HW_PIPE_RDI1 },
+	{ .id = 2, .pipe = CAMSS_IFE_HW_PIPE_RDI2 },
+	{ .id = 3, .pipe = CAMSS_IFE_HW_PIPE_PP },
+};
+
+static const struct camss_ife_stream csid_ife_lite_streams_sa8775p_pix[] = {
+	{ .id = 0, .pipe = CAMSS_IFE_HW_PIPE_RDI0 },
+	{ .id = 1, .pipe = CAMSS_IFE_HW_PIPE_RDI1 },
+	{ .id = 2, .pipe = CAMSS_IFE_HW_PIPE_RDI2 },
+	{ .id = 3, .pipe = CAMSS_IFE_HW_PIPE_RDI3 },
+};
+
+static const struct camss_subdev_resources csid_res_sa8775p_pix[] = {
+	/* CSID0 */
+	{
+		.regulators = {},
+		.clock = { "csid", "csiphy_rx" },
+		.clock_rate = { { 400000000 }, { 400000000 } },
+		.reg = { "csid0" },
+		.interrupt = { "csid0" },
+		.csid = {
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen3,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		},
+	},
+	/* CSID1 */
+	{
+		.regulators = {},
+		.clock = { "csid", "csiphy_rx" },
+		.clock_rate = { { 400000000 }, { 400000000 } },
+		.reg = { "csid1" },
+		.interrupt = { "csid1" },
+		.csid = {
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen3,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		},
+	},
+	/* CSID_LITE0 */
+	{
+		.regulators = {},
+		.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+		.clock_rate = { { 400000000 }, { 400000000 } },
+		.reg = { "csid_lite0" },
+		.interrupt = { "csid_lite0" },
+		.csid = {
+			.is_lite = true,
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen3,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		},
+	},
+	/* CSID_LITE1 */
+	{
+		.regulators = {},
+		.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+		.clock_rate = { { 400000000 }, { 400000000 } },
+		.reg = { "csid_lite1" },
+		.interrupt = { "csid_lite1" },
+		.csid = {
+			.is_lite = true,
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen3,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		},
+	},
+	/* CSID_LITE2 */
+	{
+		.regulators = {},
+		.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+		.clock_rate = { { 400000000 }, { 400000000 } },
+		.reg = { "csid_lite2" },
+		.interrupt = { "csid_lite2" },
+		.csid = {
+			.is_lite = true,
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen3,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		},
+	},
+	/* CSID_LITE3 */
+	{
+		.regulators = {},
+		.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+		.clock_rate = { { 400000000 }, { 400000000 } },
+		.reg = { "csid_lite3" },
+		.interrupt = { "csid_lite3" },
+		.csid = {
+			.is_lite = true,
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen3,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		},
+	},
+	/* CSID_LITE4 */
+	{
+		.regulators = {},
+		.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+		.clock_rate = { { 400000000 }, { 400000000 } },
+		.reg = { "csid_lite4" },
+		.interrupt = { "csid_lite4" },
+		.csid = {
+			.is_lite = true,
+			.streams_enable = true,
+			.source_streams_mode = CSID_STREAMS_SINGLE_PAD,
+			.hw_ops = &csid_ops_gen3,
+			.parent_dev_ops = &ife_parent_dev_ops,
+			.formats = &csid_formats_gen2,
+		},
+	},
+};
+
+static const struct camss_subdev_resources ife_res_sa8775p_pix[] = {
+	/* IFE0 */
+	{
+		.regulators = {},
+		.clock = { "cpas_vfe0", "vfe0", "vfe0_fast_ahb",
+			   "cpas_ahb", "gcc_axi_hf", "cpas_fast_ahb_clk",
+			   "camnoc_axi" },
+		.clock_rate = {
+			{ 0 },
+			{ 480000000 },
+			{ 300000000, 400000000 },
+			{ 300000000, 400000000 },
+			{ 0 },
+			{ 300000000, 400000000 },
+			{ 400000000 },
+		},
+		.reg = { "vfe0" },
+		.interrupt = { "vfe0" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE,
+			.pads = &ife_pads_v41x,
+			.top_ops = &ife_top_v41x_ops,
+			.buswr_ops = &ife_buswr_31x_ops,
+			.mod_ops = &ife_mod_v41x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_streams_sa8775p_pix),
+			.sink_streams = csid_ife_streams_sa8775p_pix,
+		},
+	},
+	/* IFE1 */
+	{
+		.regulators = {},
+		.clock = { "cpas_vfe1", "vfe1", "vfe1_fast_ahb",
+			   "cpas_ahb", "gcc_axi_hf", "cpas_fast_ahb_clk",
+			   "camnoc_axi" },
+		.clock_rate = {
+			{ 0 },
+			{ 480000000 },
+			{ 300000000, 400000000 },
+			{ 300000000, 400000000 },
+			{ 0 },
+			{ 300000000, 400000000 },
+			{ 400000000 },
+		},
+		.reg = { "vfe1" },
+		.interrupt = { "vfe1" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE,
+			.pads = &ife_pads_v41x,
+			.top_ops = &ife_top_v41x_ops,
+			.buswr_ops = &ife_buswr_31x_ops,
+			.mod_ops = &ife_mod_v41x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_streams_sa8775p_pix),
+			.sink_streams = csid_ife_streams_sa8775p_pix,
+		},
+	},
+	/* IFE_LITE0 */
+	{
+		.regulators = {},
+		.clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
+			   "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite",
+			   "gcc_axi_hf", "camnoc_axi" },
+		.clock_rate = {
+			{ 0 },
+			{ 0 },
+			{ 300000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 480000000, 600000000, 600000000, 600000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "vfe_lite0" },
+		.interrupt = { "vfe_lite0" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+			.pads = &ife_lite_pads_v30x,
+			.top_ops = &ife_lite_top_30x_ops,
+			.buswr_ops = &ife_buswr_31x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_lite_streams_sa8775p_pix),
+			.sink_streams = csid_ife_lite_streams_sa8775p_pix,
+		},
+	},
+	/* IFE_LITE1 */
+	{
+		.regulators = {},
+		.clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
+			   "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite",
+			   "gcc_axi_hf", "camnoc_axi" },
+		.clock_rate = {
+			{ 0 },
+			{ 0 },
+			{ 300000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 480000000, 600000000, 600000000, 600000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "vfe_lite1" },
+		.interrupt = { "vfe_lite1" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+			.pads = &ife_lite_pads_v30x,
+			.top_ops = &ife_lite_top_30x_ops,
+			.buswr_ops = &ife_buswr_31x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_lite_streams_sa8775p_pix),
+			.sink_streams = csid_ife_lite_streams_sa8775p_pix,
+		},
+	},
+	/* IFE_LITE2 */
+	{
+		.regulators = {},
+		.clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
+			   "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite",
+			   "gcc_axi_hf", "camnoc_axi" },
+		.clock_rate = {
+			{ 0 },
+			{ 0 },
+			{ 300000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 480000000, 600000000, 600000000, 600000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "vfe_lite2" },
+		.interrupt = { "vfe_lite2" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+			.pads = &ife_lite_pads_v30x,
+			.top_ops = &ife_lite_top_30x_ops,
+			.buswr_ops = &ife_buswr_31x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_lite_streams_sa8775p_pix),
+			.sink_streams = csid_ife_lite_streams_sa8775p_pix,
+		},
+	},
+	/* IFE_LITE3 */
+	{
+		.regulators = {},
+		.clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
+			   "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite",
+			   "gcc_axi_hf", "camnoc_axi" },
+		.clock_rate = {
+			{ 0 },
+			{ 0 },
+			{ 300000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 480000000, 600000000, 600000000, 600000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "vfe_lite3" },
+		.interrupt = { "vfe_lite3" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+			.pads = &ife_lite_pads_v30x,
+			.top_ops = &ife_lite_top_30x_ops,
+			.buswr_ops = &ife_buswr_31x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_lite_streams_sa8775p_pix),
+			.sink_streams = csid_ife_lite_streams_sa8775p_pix,
+		},
+	},
+	/* IFE_LITE4 */
+	{
+		.regulators = {},
+		.clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
+			   "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite",
+			   "gcc_axi_hf", "camnoc_axi" },
+		.clock_rate = {
+			{ 0 },
+			{ 0 },
+			{ 300000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 400000000, 400000000, 400000000, 400000000 },
+			{ 480000000, 600000000, 600000000, 600000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "vfe_lite4" },
+		.interrupt = { "vfe_lite4" },
+		.ife = {
+			.type = CAMSS_IFE_SUBDEV_TYPE_IFE_LITE,
+			.pads = &ife_lite_pads_v30x,
+			.top_ops = &ife_lite_top_30x_ops,
+			.buswr_ops = &ife_buswr_31x_ops,
+			.num_sink_streams = ARRAY_SIZE(csid_ife_lite_streams_sa8775p_pix),
+			.sink_streams = csid_ife_lite_streams_sa8775p_pix,
+		},
+	},
+};
+
 static const struct resources_icc icc_res_qcs8300[] = {
 	{
 		.name = "ahb",
@@ -6275,6 +6681,23 @@ static const struct camss_resources sa8775p_resources = {
 	.icc_path_num = ARRAY_SIZE(icc_res_sa8775p),
 };
 
+static const struct camss_resources sa8775p_pix_resources = {
+	.version = CAMSS_8775P,
+	.csiphy_res = csiphy_res_sa8775p_pix,
+	.tpg_res = tpg_res_8775p,
+	.csid_res = csid_res_sa8775p_pix,
+	.csid_wrapper_res = &csid_wrapper_res_sm8550,
+	.ife_res = ife_res_sa8775p_pix,
+	.icc_res = icc_res_sa8775p,
+	.icc_path_num = ARRAY_SIZE(icc_res_sa8775p),
+	.csiphy_num = ARRAY_SIZE(csiphy_res_sa8775p_pix),
+	.tpg_num = ARRAY_SIZE(tpg_res_8775p),
+	.csid_num = ARRAY_SIZE(csid_res_sa8775p_pix),
+	.ife_num = 2,
+	.ife_lite_num = 5,
+	.link_entities = camss_ife_link_entities
+};
+
 static const struct camss_resources sdm660_resources = {
 	.version = CAMSS_660,
 	.csiphy_res = csiphy_res_660,
@@ -6449,6 +6872,7 @@ static const struct of_device_id camss_dt_match[] = {
 	{ .compatible = "qcom,sm6150-camss", .data = &sm6150_resources },
 	{ .compatible = "qcom,sm6350-camss", .data = &sm6350_resources },
 	{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
+	{ .compatible = "qcom,sa8775p-camss-pix", .data = &sa8775p_pix_resources },
 	{ .compatible = "qcom,sm8250pix-camss", .data = &sm8250pix_resources },
 	{ .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
 	{ .compatible = "qcom,sm8650-camss", .data = &sm8650_resources },
-- 
2.34.1


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

* [PATCH 36/45] media: qcom: camss: sa8775p: add CCM parameters
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (34 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 35/45] media: qcom: camss: sa8775p: add pixel path resources Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 37/45] media: qcom: camss: sa8775p: add LSC parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (9 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

Program the color correction matrix, offsets, and Q8 shift from IFE
parameter buffers. Enable or disable the block as requested.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-mod-v41x.c  | 139 ++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
index 9f4c8ae9667e..0db88534b421 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -7,8 +7,14 @@
 
 #include <linux/bitfield.h>
 #include <linux/io.h>
+#include <linux/slab.h>
 
+#include <linux/qcom-camss-config.h>
+#include <media/v4l2-isp.h>
+
+#include "camss.h"
 #include "camss-ife.h"
+#include "camss-ife-params.h"
 
 /* Module base offsets */
 #define IFE_MOD_V41X_WB_GAIN_OFFSET		0x4000
@@ -108,6 +114,26 @@
 #define IFE_MOD_V41X_AWB_BG_THRESHOLD		0x3ffff
 #define IFE_MOD_V41X_AWB_BG_NUM_THRESHOLDS	5
 
+/* CCM offset, registers and bitfields */
+#define IFE_MOD_V41X_CCM_OFFSET			0x5800
+#define IFE_MOD_V41X_CCM_MODULE_CFG		0x60
+#define IFE_MOD_V41X_CCM_COEFF_A_CFG_0		0x68
+#define IFE_MOD_V41X_CCM_COEFF_A_CFG_1		0x6c
+#define IFE_MOD_V41X_CCM_COEFF_B_CFG_0		0x70
+#define IFE_MOD_V41X_CCM_COEFF_B_CFG_1		0x74
+#define IFE_MOD_V41X_CCM_COEFF_C_CFG_0		0x78
+#define IFE_MOD_V41X_CCM_COEFF_C_CFG_1		0x7c
+#define IFE_MOD_V41X_CCM_OFFSET_K_CFG_0		0x80
+#define IFE_MOD_V41X_CCM_OFFSET_K_CFG_1		0x84
+#define IFE_MOD_V41X_CCM_OFFSET_K_CFG_2		0x88
+#define IFE_MOD_V41X_CCM_SHIFT_M_CFG		0x8c
+#define IFE_MOD_V41X_CCM_CFG_EN			BIT(0)
+#define IFE_MOD_V41X_CCM_MATRIX_0		GENMASK(11, 0)
+#define IFE_MOD_V41X_CCM_MATRIX_1		GENMASK(27, 16)
+#define IFE_MOD_V41X_CCM_OFFSET_VALUE		GENMASK(31, 17)
+#define IFE_MOD_V41X_CCM_SHIFT_M_PARAM		GENMASK(1, 0)
+#define IFE_MOD_V41X_CCM_SHIFT_M_Q8		0x1
+
 static void mod_v41x_wb_gain_config(struct ife_pp_ctx *ctx)
 {
 	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
@@ -133,6 +159,81 @@ static void mod_v41x_demosaic_config(struct ife_pp_ctx *ctx)
 		       base + IFE_MOD_V41X_DEMOSAIC_INTERP_CLASSIFIER_CFG_1);
 }
 
+static void mod_v41x_ccm_enable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_CCM_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_CCM_MODULE_CFG);
+	value |= IFE_MOD_V41X_CCM_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_CCM_MODULE_CFG);
+}
+
+static void mod_v41x_ccm_disable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_CCM_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_CCM_MODULE_CFG);
+	value &= ~IFE_MOD_V41X_CCM_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_CCM_MODULE_CFG);
+}
+
+static void mod_v41x_ccm_config(struct ife_pp_ctx *ctx,
+				const struct camss_ife_params_color_correct *cfg)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_CCM_OFFSET;
+	u32 value;
+
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_SHIFT_M_PARAM,
+				  IFE_MOD_V41X_CCM_SHIFT_M_Q8),
+		       base + IFE_MOD_V41X_CCM_SHIFT_M_CFG);
+
+	/*
+	 * HW banks A/B/C each multiply one fixed input channel (A=G_in,
+	 * B=B_in, C=R_in), and HW output indices 0/1/2 are also G/B/R, not
+	 * R/G/B, same as IFE_MOD_V10X_CCM. The UAPI's coeff[out][in]/
+	 * offset[out] are R,G,B-indexed on both axes (0=R,1=G,2=B); remap
+	 * accordingly: bank X's slot N takes UAPI row (N+1)%3 (G,B,R output
+	 * order) and the column matching that bank's input channel.
+	 */
+	value = FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[1][1]) |
+		FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_1, cfg->coeff[2][1]);
+	writel_relaxed(value, base + IFE_MOD_V41X_CCM_COEFF_A_CFG_0);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[0][1]),
+		       base + IFE_MOD_V41X_CCM_COEFF_A_CFG_1);
+
+	value = FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[1][2]) |
+		FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_1, cfg->coeff[2][2]);
+	writel_relaxed(value, base + IFE_MOD_V41X_CCM_COEFF_B_CFG_0);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[0][2]),
+		       base + IFE_MOD_V41X_CCM_COEFF_B_CFG_1);
+
+	value = FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[1][0]) |
+		FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_1, cfg->coeff[2][0]);
+	writel_relaxed(value, base + IFE_MOD_V41X_CCM_COEFF_C_CFG_0);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_MATRIX_0, cfg->coeff[0][0]),
+		       base + IFE_MOD_V41X_CCM_COEFF_C_CFG_1);
+
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_OFFSET_VALUE, cfg->offset[1]),
+		       base + IFE_MOD_V41X_CCM_OFFSET_K_CFG_0);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_OFFSET_VALUE, cfg->offset[2]),
+		       base + IFE_MOD_V41X_CCM_OFFSET_K_CFG_1);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_CCM_OFFSET_VALUE, cfg->offset[0]),
+		       base + IFE_MOD_V41X_CCM_OFFSET_K_CFG_2);
+}
+
+static void mod_v41x_ccm_apply(struct ife_pp_ctx *ctx,
+			       const struct camss_ife_params_color_correct *cfg, bool enable)
+{
+	if (enable) {
+		mod_v41x_ccm_config(ctx, cfg);
+		mod_v41x_ccm_enable(ctx);
+	} else {
+		mod_v41x_ccm_disable(ctx);
+	}
+}
+
 static void mod_v41x_awb_bg_config(struct ife_pp_ctx *ctx,
 				   const struct camss_pp_cfg *cfg)
 {
@@ -226,12 +327,17 @@ static void mod_v41x_xform_write_channel(void __iomem *base, u32 coeff0,
 static void mod_v41x_modules_acquire(struct ife_device *ife, struct ife_pp_ctx *ctx,
 				     void __iomem *base)
 {
+	ctx->dev = ife->camss->dev;
 	ctx->base = base;
+	ctx->params_scratch = devm_kzalloc(ctx->dev,
+					   v4l2_isp_buffer_size(CAMSS_IFE_PARAMS_MAX_PAYLOAD),
+					   GFP_KERNEL);
 }
 
 static void mod_v41x_modules_release(struct ife_pp_ctx *ctx)
 {
 	ctx->base = NULL;
+	ctx->params_scratch = NULL;
 }
 
 static void mod_v41x_modules_config(struct ife_pp_ctx *ctx,
@@ -319,10 +425,43 @@ static void mod_v41x_modules_disable(struct ife_pp_ctx *ctx)
 	writel_relaxed(value, base + IFE_MOD_V41X_XFORM_MODULE_CFG);
 }
 
+static void mod_v41x_params_apply_ccm(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+
+	mod_v41x_ccm_apply(ctx, &block->color_correct, enable);
+}
+
+static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[] = {
+	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = {
+		.size = sizeof(struct camss_ife_params_color_correct),
+	},
+};
+
+static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
+	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = mod_v41x_params_apply_ccm,
+};
+
+static_assert(ARRAY_SIZE(mod_v41x_params_handlers) ==
+	      ARRAY_SIZE(mod_v41x_params_type_info));
+
+static int mod_v41x_modules_apply_params(struct ife_pp_ctx *ctx,
+					 struct vb2_buffer *vb)
+{
+	return camss_ife_params_apply(ctx->dev, vb, ctx->params_scratch,
+				      CAMSS_IFE_PARAMS_MAX_PAYLOAD,
+				      mod_v41x_params_type_info,
+				      mod_v41x_params_handlers,
+				      ARRAY_SIZE(mod_v41x_params_handlers),
+				      ctx);
+}
+
 const struct camss_mod_ops ife_mod_v41x_ops = {
 	.acquire	= mod_v41x_modules_acquire,
 	.release	= mod_v41x_modules_release,
 	.config		= mod_v41x_modules_config,
 	.enable		= mod_v41x_modules_enable,
 	.disable	= mod_v41x_modules_disable,
+	.apply_params	= mod_v41x_modules_apply_params,
 };
-- 
2.34.1


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

* [PATCH 37/45] media: qcom: camss: sa8775p: add LSC parameters
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (35 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 36/45] media: qcom: camss: sa8775p: add CCM parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 38/45] media: qcom: camss: sa8775p: add gamma parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (8 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

Program the lens shading geometry and red and blue gain tables from
IFE parameter buffers. Validate the block counts before updating
the hardware.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-mod-v41x.c  | 142 ++++++++++++++++++
 1 file changed, 142 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
index 0db88534b421..df21ea4040df 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -134,6 +134,31 @@
 #define IFE_MOD_V41X_CCM_SHIFT_M_PARAM		GENMASK(1, 0)
 #define IFE_MOD_V41X_CCM_SHIFT_M_Q8		0x1
 
+/* Shared DMI port registers */
+#define IFE_MOD_V41X_DMI_LUT_CFG		0xc
+#define IFE_MOD_V41X_DMI_DATA			0x10
+
+/* LSC offset, registers and bitfields */
+#define IFE_MOD_V41X_LSC_OFFSET			0x3c80
+#define IFE_MOD_V41X_LSC_MODULE_CFG		0x60
+#define IFE_MOD_V41X_LSC_LSC_0_CFG		0x68
+#define IFE_MOD_V41X_LSC_LSC_1_CFG		0x6c
+#define IFE_MOD_V41X_LSC_LSC_2_CFG		0x70
+#define IFE_MOD_V41X_LSC_LSC_3_CFG		0x74
+#define IFE_MOD_V41X_LSC_CFG_EN			BIT(0)
+#define IFE_MOD_V41X_LSC_NUM_SUBBLOCKS		GENMASK(13, 12)
+#define IFE_MOD_V41X_LSC_INIT_SUBBLOCK_Y	GENMASK(30, 28)
+#define IFE_MOD_V41X_LSC_NUM_BLOCKS_H		GENMASK(3, 0)
+#define IFE_MOD_V41X_LSC_NUM_BLOCKS_V		GENMASK(7, 4)
+#define IFE_MOD_V41X_LSC_BLOCK_WIDTH		GENMASK(10, 0)
+#define IFE_MOD_V41X_LSC_BLOCK_HEIGHT		GENMASK(25, 16)
+#define IFE_MOD_V41X_LSC_INV_SUBBLOCK_Q20	20
+#define IFE_MOD_V41X_LSC_INV_SUBBLOCK_WIDTH	GENMASK(28, 12)
+#define IFE_MOD_V41X_LSC_INV_SUBBLOCK_HEIGHT	GENMASK(28, 12)
+#define IFE_MOD_V41X_LSC_LUT_SEL_NONE		0x0
+#define IFE_MOD_V41X_LSC_LUT_SEL_RED		0x1
+#define IFE_MOD_V41X_LSC_LUT_SEL_BLUE		0x2
+
 static void mod_v41x_wb_gain_config(struct ife_pp_ctx *ctx)
 {
 	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
@@ -234,6 +259,102 @@ static void mod_v41x_ccm_apply(struct ife_pp_ctx *ctx,
 	}
 }
 
+static void mod_v41x_dmi_write(void __iomem *dmi_base, u32 sel,
+			       const u32 *table, size_t count)
+{
+	size_t i;
+
+	writel_relaxed(sel, dmi_base + IFE_MOD_V41X_DMI_LUT_CFG);
+	for (i = 0; i < count; i++)
+		writel_relaxed(table[i], dmi_base + IFE_MOD_V41X_DMI_DATA);
+}
+
+static void mod_v41x_lsc_enable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_LSC_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_LSC_MODULE_CFG);
+	value |= IFE_MOD_V41X_LSC_CFG_EN |
+		 FIELD_PREP(IFE_MOD_V41X_LSC_INIT_SUBBLOCK_Y, 0) |
+		 FIELD_PREP(IFE_MOD_V41X_LSC_NUM_SUBBLOCKS, 1);
+	writel_relaxed(value, base + IFE_MOD_V41X_LSC_MODULE_CFG);
+}
+
+static void mod_v41x_lsc_disable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_LSC_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_LSC_MODULE_CFG);
+	value &= ~IFE_MOD_V41X_LSC_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_LSC_MODULE_CFG);
+}
+
+static void mod_v41x_lsc_config(struct ife_pp_ctx *ctx,
+				const struct camss_ife_params_lsc *cfg)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_LSC_OFFSET;
+	u32 subblock_w, subblock_h;
+	u32 num_h = cfg->num_hor_blocks;
+	u32 num_v = cfg->num_ver_blocks;
+	u32 value;
+
+	value = FIELD_PREP(IFE_MOD_V41X_LSC_NUM_BLOCKS_H, num_h - 2) |
+		FIELD_PREP(IFE_MOD_V41X_LSC_NUM_BLOCKS_V, num_v - 2);
+	writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_0_CFG);
+
+	value = FIELD_PREP(IFE_MOD_V41X_LSC_BLOCK_WIDTH, (ctx->in_width / num_h) - 1) |
+		FIELD_PREP(IFE_MOD_V41X_LSC_BLOCK_HEIGHT, (ctx->in_height / num_v) - 1);
+	writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_1_CFG);
+
+	subblock_w = (ctx->in_width / 4) / (num_h / 2);
+	value = subblock_w |
+		FIELD_PREP(IFE_MOD_V41X_LSC_INV_SUBBLOCK_WIDTH,
+			   (1 << IFE_MOD_V41X_LSC_INV_SUBBLOCK_Q20) / (subblock_w + 1));
+	writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_2_CFG);
+
+	subblock_h = (ctx->in_height / 4) / (num_v / 2);
+	value = subblock_h |
+		FIELD_PREP(IFE_MOD_V41X_LSC_INV_SUBBLOCK_HEIGHT,
+			   (1 << IFE_MOD_V41X_LSC_INV_SUBBLOCK_Q20) / (subblock_h + 1));
+	writel_relaxed(value, base + IFE_MOD_V41X_LSC_LSC_3_CFG);
+
+	mod_v41x_dmi_write(base, IFE_MOD_V41X_LSC_LUT_SEL_RED,
+			   cfg->table_r, CAMSS_IFE_MAX_LSC_BLOCKS);
+	mod_v41x_dmi_write(base, IFE_MOD_V41X_LSC_LUT_SEL_BLUE,
+			   cfg->table_b, CAMSS_IFE_MAX_LSC_BLOCKS);
+
+	/*
+	 * The last table's auto-incremented DMI_DATA writes only land in LUT
+	 * RAM once DMI_LUT_CFG.LUT_SEL changes value again - without this,
+	 * the Blue LUT silently never commits.
+	 */
+	writel_relaxed(IFE_MOD_V41X_LSC_LUT_SEL_NONE, base + IFE_MOD_V41X_DMI_LUT_CFG);
+}
+
+static int mod_v41x_lsc_validate(const struct camss_ife_params_lsc *cfg)
+{
+	u32 num_h = cfg->num_hor_blocks;
+	u32 num_v = cfg->num_ver_blocks;
+
+	if (num_h < 2 || num_h > 17 || num_v < 2 || num_v > 13)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void mod_v41x_lsc_apply(struct ife_pp_ctx *ctx,
+			       const struct camss_ife_params_lsc *cfg, bool enable)
+{
+	if (enable) {
+		mod_v41x_lsc_config(ctx, cfg);
+		mod_v41x_lsc_enable(ctx);
+	} else {
+		mod_v41x_lsc_disable(ctx);
+	}
+}
+
 static void mod_v41x_awb_bg_config(struct ife_pp_ctx *ctx,
 				   const struct camss_pp_cfg *cfg)
 {
@@ -433,14 +554,35 @@ static void mod_v41x_params_apply_ccm(void *priv, const union camss_ife_params_b
 	mod_v41x_ccm_apply(ctx, &block->color_correct, enable);
 }
 
+static void mod_v41x_params_apply_lsc(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+	int ret;
+
+	if (enable) {
+		ret = mod_v41x_lsc_validate(&block->lsc);
+		if (ret) {
+			dev_dbg(ctx->dev, "LSC validate failed: %d\n", ret);
+			return;
+		}
+	}
+
+	mod_v41x_lsc_apply(ctx, &block->lsc, enable);
+}
+
 static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[] = {
 	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = {
 		.size = sizeof(struct camss_ife_params_color_correct),
 	},
+	[CAMSS_IFE_PARAMS_LSC] = {
+		.size = sizeof(struct camss_ife_params_lsc),
+	},
 };
 
 static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
 	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = mod_v41x_params_apply_ccm,
+	[CAMSS_IFE_PARAMS_LSC] = mod_v41x_params_apply_lsc,
 };
 
 static_assert(ARRAY_SIZE(mod_v41x_params_handlers) ==
-- 
2.34.1


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

* [PATCH 38/45] media: qcom: camss: sa8775p: add gamma parameters
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (36 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 37/45] media: qcom: camss: sa8775p: add LSC parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 39/45] media: qcom: camss: sa8775p: add white balance parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (7 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

Program the red, green, and blue gamma tables from IFE parameter
buffers. Upsample each userspace table to the hardware table size.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-mod-v41x.c  | 73 +++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
index df21ea4040df..77d33c05cc6e 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -159,6 +159,15 @@
 #define IFE_MOD_V41X_LSC_LUT_SEL_RED		0x1
 #define IFE_MOD_V41X_LSC_LUT_SEL_BLUE		0x2
 
+/* Gamma LUT (GLUT) offset, registers and bitfields */
+#define IFE_MOD_V41X_GLUT_OFFSET		0x5f00
+#define IFE_MOD_V41X_GLUT_MODULE_CFG		0x60
+#define IFE_MOD_V41X_GLUT_CFG_EN		BIT(0)
+#define IFE_MOD_V41X_GLUT_LUT_SEL_NONE		0x0
+#define IFE_MOD_V41X_GLUT_LUT_SEL_G		0x1
+#define IFE_MOD_V41X_GLUT_LUT_SEL_B		0x2
+#define IFE_MOD_V41X_GLUT_LUT_SEL_R		0x3
+
 static void mod_v41x_wb_gain_config(struct ife_pp_ctx *ctx)
 {
 	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
@@ -355,6 +364,58 @@ static void mod_v41x_lsc_apply(struct ife_pp_ctx *ctx,
 	}
 }
 
+static void mod_v41x_glut_enable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_GLUT_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_GLUT_MODULE_CFG);
+	value |= IFE_MOD_V41X_GLUT_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_GLUT_MODULE_CFG);
+}
+
+static void mod_v41x_glut_disable(struct ife_pp_ctx *ctx)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_GLUT_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_GLUT_MODULE_CFG);
+	value &= ~IFE_MOD_V41X_GLUT_CFG_EN;
+	writel_relaxed(value, base + IFE_MOD_V41X_GLUT_MODULE_CFG);
+}
+
+static void mod_v41x_glut_config(struct ife_pp_ctx *ctx,
+				 const struct camss_ife_params_gamma_256 *cfg)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_GLUT_OFFSET;
+
+	mod_v41x_dmi_write(base, IFE_MOD_V41X_GLUT_LUT_SEL_G, cfg->table_g,
+			   CAMSS_IFE_GAMMA_256_SIZE);
+	mod_v41x_dmi_write(base, IFE_MOD_V41X_GLUT_LUT_SEL_B, cfg->table_b,
+			   CAMSS_IFE_GAMMA_256_SIZE);
+	mod_v41x_dmi_write(base, IFE_MOD_V41X_GLUT_LUT_SEL_R, cfg->table_r,
+			   CAMSS_IFE_GAMMA_256_SIZE);
+
+	/*
+	 * The last table's auto-incremented DMI_DATA writes only land in LUT
+	 * RAM once DMI_LUT_CFG.LUT_SEL changes value again - without this,
+	 * the final (R) table silently never commits and GLUT reads back
+	 * black on that channel. Deselect the LUT once more to flush it.
+	 */
+	writel_relaxed(IFE_MOD_V41X_GLUT_LUT_SEL_NONE, base + IFE_MOD_V41X_DMI_LUT_CFG);
+}
+
+static void mod_v41x_glut_apply(struct ife_pp_ctx *ctx,
+				const struct camss_ife_params_gamma_256 *cfg, bool enable)
+{
+	if (enable) {
+		mod_v41x_glut_config(ctx, cfg);
+		mod_v41x_glut_enable(ctx);
+	} else {
+		mod_v41x_glut_disable(ctx);
+	}
+}
+
 static void mod_v41x_awb_bg_config(struct ife_pp_ctx *ctx,
 				   const struct camss_pp_cfg *cfg)
 {
@@ -571,6 +632,14 @@ static void mod_v41x_params_apply_lsc(void *priv, const union camss_ife_params_b
 	mod_v41x_lsc_apply(ctx, &block->lsc, enable);
 }
 
+static void mod_v41x_params_apply_gamma(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+
+	mod_v41x_glut_apply(ctx, &block->gamma_256, enable);
+}
+
 static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[] = {
 	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = {
 		.size = sizeof(struct camss_ife_params_color_correct),
@@ -578,11 +647,15 @@ static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[]
 	[CAMSS_IFE_PARAMS_LSC] = {
 		.size = sizeof(struct camss_ife_params_lsc),
 	},
+	[CAMSS_IFE_PARAMS_GAMMA_256] = {
+		.size = sizeof(struct camss_ife_params_gamma_256),
+	},
 };
 
 static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
 	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = mod_v41x_params_apply_ccm,
 	[CAMSS_IFE_PARAMS_LSC] = mod_v41x_params_apply_lsc,
+	[CAMSS_IFE_PARAMS_GAMMA_256] = mod_v41x_params_apply_gamma,
 };
 
 static_assert(ARRAY_SIZE(mod_v41x_params_handlers) ==
-- 
2.34.1


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

* [PATCH 39/45] media: qcom: camss: sa8775p: add white balance parameters
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (37 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 38/45] media: qcom: camss: sa8775p: add gamma parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 40/45] media: qcom: camss: sa8775p: add AWB BG parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (6 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

Program the red, green, and blue white balance gains from IFE parameter
buffers. Enable or disable the gain block as requested.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-mod-v41x.c  | 42 +++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
index 77d33c05cc6e..3c53f585133c 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -179,6 +179,36 @@ static void mod_v41x_wb_gain_config(struct ife_pp_ctx *ctx)
 	writel_relaxed(value, base + IFE_MOD_V41X_WB_GAIN_CFG_2);
 }
 
+static void mod_v41x_wb_gain_set(struct ife_pp_ctx *ctx,
+				 const struct camss_ife_params_wb_gain *cfg)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
+
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_WB_GAIN_VALUE, cfg->g),
+		       base + IFE_MOD_V41X_WB_GAIN_CFG_0);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_WB_GAIN_VALUE, cfg->b),
+		       base + IFE_MOD_V41X_WB_GAIN_CFG_1);
+	writel_relaxed(FIELD_PREP(IFE_MOD_V41X_WB_GAIN_VALUE, cfg->r),
+		       base + IFE_MOD_V41X_WB_GAIN_CFG_2);
+}
+
+static void mod_v41x_wb_gain_apply(struct ife_pp_ctx *ctx,
+				   const struct camss_ife_params_wb_gain *cfg,
+				   bool enable)
+{
+	void __iomem *base = ctx->base + IFE_MOD_V41X_WB_GAIN_OFFSET;
+	u32 value;
+
+	value = readl_relaxed(base + IFE_MOD_V41X_WB_GAIN_MODULE_CFG);
+	if (enable) {
+		mod_v41x_wb_gain_set(ctx, cfg);
+		value |= IFE_MOD_V41X_WB_GAIN_CFG_EN;
+	} else {
+		value &= ~IFE_MOD_V41X_WB_GAIN_CFG_EN;
+	}
+	writel_relaxed(value, base + IFE_MOD_V41X_WB_GAIN_MODULE_CFG);
+}
+
 static void mod_v41x_demosaic_config(struct ife_pp_ctx *ctx)
 {
 	void __iomem *base = ctx->base + IFE_MOD_V41X_DEMOSAIC_OFFSET;
@@ -632,6 +662,14 @@ static void mod_v41x_params_apply_lsc(void *priv, const union camss_ife_params_b
 	mod_v41x_lsc_apply(ctx, &block->lsc, enable);
 }
 
+static void mod_v41x_params_apply_wb(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+
+	mod_v41x_wb_gain_apply(ctx, &block->wb_gain, enable);
+}
+
 static void mod_v41x_params_apply_gamma(void *priv, const union camss_ife_params_block *block)
 {
 	struct ife_pp_ctx *ctx = priv;
@@ -650,12 +688,16 @@ static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[]
 	[CAMSS_IFE_PARAMS_GAMMA_256] = {
 		.size = sizeof(struct camss_ife_params_gamma_256),
 	},
+	[CAMSS_IFE_PARAMS_WB_GAIN] = {
+		.size = sizeof(struct camss_ife_params_wb_gain),
+	},
 };
 
 static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
 	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = mod_v41x_params_apply_ccm,
 	[CAMSS_IFE_PARAMS_LSC] = mod_v41x_params_apply_lsc,
 	[CAMSS_IFE_PARAMS_GAMMA_256] = mod_v41x_params_apply_gamma,
+	[CAMSS_IFE_PARAMS_WB_GAIN] = mod_v41x_params_apply_wb,
 };
 
 static_assert(ARRAY_SIZE(mod_v41x_params_handlers) ==
-- 
2.34.1


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

* [PATCH 40/45] media: qcom: camss: sa8775p: add AWB BG parameters
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (38 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 39/45] media: qcom: camss: sa8775p: add white balance parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 41/45] media: qcom: camss: create TPG links for IFE Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (5 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

Configure and control the AWB BG statistics block from IFE parameter
buffers. Derive its grid from the active pixel-path size.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 .../platform/qcom/camss/camss-ife-mod-v41x.c      | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
index 3c53f585133c..b35a3f375026 100644
--- a/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
+++ b/drivers/media/platform/qcom/camss/camss-ife-mod-v41x.c
@@ -678,6 +678,17 @@ static void mod_v41x_params_apply_gamma(void *priv, const union camss_ife_params
 	mod_v41x_glut_apply(ctx, &block->gamma_256, enable);
 }
 
+static void mod_v41x_params_apply_bg_stats(void *priv, const union camss_ife_params_block *block)
+{
+	struct ife_pp_ctx *ctx = priv;
+	bool enable = block->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+
+	if (enable)
+		mod_v41x_awb_bg_enable(ctx);
+	else
+		mod_v41x_awb_bg_disable(ctx);
+}
+
 static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[] = {
 	[CAMSS_IFE_PARAMS_COLOR_CORRECT] = {
 		.size = sizeof(struct camss_ife_params_color_correct),
@@ -691,6 +702,9 @@ static const struct v4l2_isp_params_block_type_info mod_v41x_params_type_info[]
 	[CAMSS_IFE_PARAMS_WB_GAIN] = {
 		.size = sizeof(struct camss_ife_params_wb_gain),
 	},
+	[CAMSS_IFE_PARAMS_BG_STATS] = {
+		.size = sizeof(struct camss_ife_params_bg_stats),
+	},
 };
 
 static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
@@ -698,6 +712,7 @@ static const camss_ife_params_handler_fn mod_v41x_params_handlers[] = {
 	[CAMSS_IFE_PARAMS_LSC] = mod_v41x_params_apply_lsc,
 	[CAMSS_IFE_PARAMS_GAMMA_256] = mod_v41x_params_apply_gamma,
 	[CAMSS_IFE_PARAMS_WB_GAIN] = mod_v41x_params_apply_wb,
+	[CAMSS_IFE_PARAMS_BG_STATS] = mod_v41x_params_apply_bg_stats,
 };
 
 static_assert(ARRAY_SIZE(mod_v41x_params_handlers) ==
-- 
2.34.1


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

* [PATCH 41/45] media: qcom: camss: create TPG links for IFE
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (39 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 40/45] media: qcom: camss: sa8775p: add AWB BG parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 42/45] media: qcom: camss: use one lane for TPG internal path Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (4 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio,
	Prashant Shrotriya

From: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>

The IFE topology uses its own entity-link callback. Create TPG-to-CSID
links there so a TPG can feed the IFE pipeline.

Signed-off-by: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss-ife.c | 22 ++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/qcom/camss/camss-ife.c b/drivers/media/platform/qcom/camss/camss-ife.c
index e2049e00ea96..ab6102fd1126 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.c
+++ b/drivers/media/platform/qcom/camss/camss-ife.c
@@ -12,6 +12,7 @@
 #include "camss.h"
 #include "camss-csid.h"
 #include "camss-ife.h"
+#include "camss-tpg.h"
 
 #define CAMSS_IFE_PREFIX "ife"
 #define CAMSS_IFE_IRQ_NAME "msm_vfe"
@@ -1574,9 +1575,28 @@ int camss_ife_subdev_init(struct camss *camss, struct ife_device *ife,
 
 int camss_ife_link_entities(struct camss *camss)
 {
-	int ife_id, csid_id;
+	int csid_id;
+	int tpg_id;
+	int ife_id;
 	int rc;
 
+	for (tpg_id = 0; tpg_id < camss->res->tpg_num; tpg_id++) {
+		for (csid_id = 0; csid_id < camss->res->csid_num; csid_id++) {
+			rc = media_create_pad_link(&camss->tpg[tpg_id].subdev.entity,
+						   MSM_TPG_PAD_SRC,
+						   &camss->csid[csid_id].subdev.entity,
+						   MSM_CSID_PAD_SINK, 0);
+			if (rc) {
+				dev_err(camss->dev,
+					"Failed to link %s->%s: %d\n",
+					camss->tpg[tpg_id].subdev.entity.name,
+					camss->csid[csid_id].subdev.entity.name,
+					rc);
+				return rc;
+			}
+		}
+	}
+
 	for (ife_id = 0; ife_id < camss->res->csiphy_num; ife_id++) {
 		for (csid_id = 0; csid_id < camss->res->csid_num; csid_id++) {
 			rc = media_create_pad_link(&camss->csiphy[ife_id].subdev.entity,
-- 
2.34.1


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

* [PATCH 42/45] media: qcom: camss: use one lane for TPG internal path
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (40 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 41/45] media: qcom: camss: create TPG links for IFE Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 43/45] media: qcom: camss: ife: queue params buffers in FIFO order Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (3 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio,
	Prashant Shrotriya

From: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>

The internal TPG path sends the CSI-2 packet header on lane
zero. Configure both the TPG and CSID for one lane to avoid packet
header ECC errors.

Signed-off-by: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss-csid.c     | 7 +++++--
 drivers/media/platform/qcom/camss/camss-tpg-gen1.c | 2 +-
 drivers/media/platform/qcom/camss/camss-tpg.h      | 1 +
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index da13963ef1a0..6bb0af77fd49 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -1276,11 +1276,12 @@ static int csid_link_setup(struct media_entity *entity,
 		if (sd->grp_id == TPG_GRP_ID) {
 			tpg = v4l2_get_subdevdata(sd);
 
-			csid->phy.lane_cnt = tpg->res->lane_cnt;
+			csid->phy.lane_cnt = 1;
 			csid->phy.csiphy_id = tpg->id;
 			csid->phy.lane_assign = csid_get_lane_assign(NULL, csid->phy.lane_cnt);
 			csid->tpg_linked = true;
-		} else {
+			tpg->lane_cnt_override = 1;
+		} else if (sd->grp_id == CSIPHY_GRP_ID) {
 			csiphy = v4l2_get_subdevdata(sd);
 
 			/* If a sensor is not linked to CSIPHY */
@@ -1294,6 +1295,8 @@ static int csid_link_setup(struct media_entity *entity,
 			csid->phy.lane_cnt = lane_cfg->num_data;
 			csid->phy.lane_assign = csid_get_lane_assign(lane_cfg, lane_cfg->num_data);
 			csid->tpg_linked = false;
+		} else {
+			return -EPERM;
 		}
 	}
 	/* Decide which virtual channels to enable based on which source pads are enabled */
diff --git a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c
index d29de5f93c18..423f45378a48 100644
--- a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c
+++ b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c
@@ -105,7 +105,7 @@ static int tpg_stream_on(struct tpg_device *tpg)
 	const struct tpg_format_info *format;
 	u8 payload_mode = (tg->mode > TPG_PAYLOAD_MODE_DISABLED) ?
 			   tg->mode - 1 : 0;
-	u8 lane_cnt = tpg->res->lane_cnt;
+	u8 lane_cnt = tpg->lane_cnt_override ? tpg->lane_cnt_override : tpg->res->lane_cnt;
 	u8 vc, dt, last_vc = 0;
 	u32 val;
 
diff --git a/drivers/media/platform/qcom/camss/camss-tpg.h b/drivers/media/platform/qcom/camss/camss-tpg.h
index 7fb35a97dd06..fb22747f5eed 100644
--- a/drivers/media/platform/qcom/camss/camss-tpg.h
+++ b/drivers/media/platform/qcom/camss/camss-tpg.h
@@ -94,6 +94,7 @@ struct tpg_device {
 	struct v4l2_ctrl *testgen_mode;
 	const struct tpg_subdev_resources *res;
 	u32 hw_version;
+	u8 lane_cnt_override;
 };
 
 struct camss_subdev_resources;
-- 
2.34.1


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

* [PATCH 43/45] media: qcom: camss: ife: queue params buffers in FIFO order
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (41 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 42/45] media: qcom: camss: use one lane for TPG internal path Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 44/45] dt-bindings: media: qcom,qcs8300-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (2 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

From: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>

ife->pending_params was a single-slot pointer: queuing a second PARAMS
buffer before the next PP epoch0 IRQ consumed it would silently
displace the first one, completing it with VB2_BUF_STATE_ERROR. This
means a userspace client that queues several params buffers ahead of
time (mirroring how image buffers are queued) loses all but the most
recently queued one instead of having them applied one per frame in
order.

Turn pending_params into a struct list_head FIFO, following the same
pending_bufs/bufs_lock pattern already used for image buffers. Buffers
are now enqueued at the tail in camss_ife_queue_buffer() and dequeued
from the head once per PP epoch0 IRQ, applying at most one params
buffer per frame without dropping queued-ahead buffers. Flushing on
stream-off now completes every buffer left in the list rather than
just one.

Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss-ife.c | 25 +++++++++----------
 drivers/media/platform/qcom/camss/camss-ife.h |  2 +-
 2 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/drivers/media/platform/qcom/camss/camss-ife.c b/drivers/media/platform/qcom/camss/camss-ife.c
index ab6102fd1126..57fab10a87f5 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.c
+++ b/drivers/media/platform/qcom/camss/camss-ife.c
@@ -293,8 +293,10 @@ static void ife_hw_isr_pp_epoch0(struct ife_device *ife)
 
 	spin_lock_irqsave(&ife->bufs_lock, flags);
 
-	params_buf = ife->pending_params;
-	ife->pending_params = NULL;
+	params_buf = list_first_entry_or_null(&ife->pending_params,
+					      struct camss_buffer, queue);
+	if (params_buf)
+		list_del(&params_buf->queue);
 
 	for (i = 0; i < ife->npads; i++) {
 		struct ife_video_pad *vid_pad = ife->pads[i].vid_pad;
@@ -1360,17 +1362,13 @@ static int ife_queue_buffer(struct camss_video *vid, struct camss_buffer *buf)
 	struct ife_device *ife = vid_pad->ife;
 
 	if (is_ife_video_params(vid)) {
-		struct camss_buffer *old_buf;
 		unsigned long flags;
 
 		spin_lock_irqsave(&ife->bufs_lock, flags);
-		old_buf = ife->pending_params;
-		ife->pending_params = buf;
+		INIT_LIST_HEAD(&buf->queue);
+		list_add_tail(&buf->queue, &ife->pending_params);
 		spin_unlock_irqrestore(&ife->bufs_lock, flags);
 
-		if (old_buf)
-			vb2_buffer_done(&old_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
-
 		return 0;
 	}
 
@@ -1387,15 +1385,15 @@ static int ife_flush_buffers(struct camss_video *vid, enum vb2_buffer_state stat
 	if (is_ife_video_params(vid)) {
 		struct ife_device *ife = vid_pad->ife;
 		struct camss_buffer *buf;
+		struct camss_buffer *tmp;
 		unsigned long flags;
 
 		spin_lock_irqsave(&ife->bufs_lock, flags);
-		buf = ife->pending_params;
-		ife->pending_params = NULL;
-		spin_unlock_irqrestore(&ife->bufs_lock, flags);
-
-		if (buf)
+		list_for_each_entry_safe(buf, tmp, &ife->pending_params, queue) {
+			list_del(&buf->queue);
 			vb2_buffer_done(&buf->vb.vb2_buf, state);
+		}
+		spin_unlock_irqrestore(&ife->bufs_lock, flags);
 
 		return 0;
 	}
@@ -1499,6 +1497,7 @@ int camss_ife_subdev_init(struct camss *camss, struct ife_device *ife,
 	init_completion(&ife->reset_complete);
 	mutex_init(&ife->state_mutex);
 	spin_lock_init(&ife->bufs_lock);
+	INIT_LIST_HEAD(&ife->pending_params);
 
 	/* Memory */
 	base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
index c38b96076a1f..9656d29717d1 100644
--- a/drivers/media/platform/qcom/camss/camss-ife.h
+++ b/drivers/media/platform/qcom/camss/camss-ife.h
@@ -367,7 +367,7 @@ struct ife_device {
 
 	struct camss_ife_pipeline_cfg pipeline_cfg[IFE_CAMIF_HW_PIPES_MAX];
 
-	struct camss_buffer *pending_params;
+	struct list_head pending_params;
 
 	struct v4l2_subdev sd;
 	struct device *genpd;
-- 
2.34.1


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

* [PATCH 44/45] dt-bindings: media: qcom,qcs8300-camss: add pixel path variant
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (42 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 43/45] media: qcom: camss: ife: queue params buffers in FIFO order Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-06 22:39 ` [PATCH 45/45] media: qcom: camss: sa8775p: add pixel path resources for QCS8300 Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-07  9:59 ` [PATCH 00/45] media: qcom: camss: add IFE pixel path support Bryan O'Donoghue
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio,
	Prashant Shrotriya

From: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>

Document qcom,qcs8300-camss-pix for CAMSS configurations that use IFE
pixel paths. Keep qcom,qcs8300-camss for the legacy VFE resources.

Signed-off-by: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>
---
 .../devicetree/bindings/media/qcom,qcs8300-camss.yaml         | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml b/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml
index e5f170aa4d9e..f85104682e52 100644
--- a/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml
@@ -14,7 +14,9 @@ description:
 
 properties:
   compatible:
-    const: qcom,qcs8300-camss
+    enum:
+      - qcom,qcs8300-camss
+      - qcom,qcs8300-camss-pix
 
   reg:
     maxItems: 21
-- 
2.34.1


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

* [PATCH 45/45] media: qcom: camss: sa8775p: add pixel path resources for QCS8300
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (43 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 44/45] dt-bindings: media: qcom,qcs8300-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-06 22:39 ` Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-07  9:59 ` [PATCH 00/45] media: qcom: camss: add IFE pixel path support Bryan O'Donoghue
  45 siblings, 0 replies; 57+ messages in thread
From: Gjorgji.Rosikopulos.gjorgji.rosikopulos @ 2026-10-06 22:39 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio,
	Prashant Shrotriya

From: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>

QCS8300 shares the same IFE/CSID pixel path hardware as SA8775P, with
one difference: QCS8300 has 3 CSIPHYs (0-2) while SA8775P has 4
(0-3).

Add csiphy_res_qcs8300_pix with streams_enable set on each entry, as
required by the CSID pixel path driver, and a qcs8300_pix_resources
table that reuses the SA8775P CSID/IFE/TPG resources together with
the QCS8300 ICC bandwidth paths.

Signed-off-by: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>
---
 drivers/media/platform/qcom/camss/camss.c | 84 +++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index a0b51c12c0bf..4233357321c0 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -4469,6 +4469,72 @@ static const struct camss_subdev_resources csiphy_res_sa8775p_pix[] = {
 	},
 };
 
+static const struct camss_subdev_resources csiphy_res_qcs8300_pix[] = {
+	/* CSIPHY0 */
+	{
+		.regulators = {
+			{ .supply = "vdda-phy", .init_load_uA = 15900 },
+			{ .supply = "vdda-pll", .init_load_uA = 8900 }
+		},
+		.clock = { "csiphy_rx", "csiphy0", "csiphy0_timer" },
+		.clock_rate = {
+			{ 400000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "csiphy0" },
+		.interrupt = { "csiphy0" },
+		.csiphy = {
+			.id = 0,
+			.streams_enable = true,
+			.hw_ops = &csiphy_ops_3ph_1_0,
+			.formats = &csiphy_formats_sdm845
+		}
+	},
+	/* CSIPHY1 */
+	{
+		.regulators = {
+			{ .supply = "vdda-phy", .init_load_uA = 15900 },
+			{ .supply = "vdda-pll", .init_load_uA = 8900 }
+		},
+		.clock = { "csiphy_rx", "csiphy1", "csiphy1_timer" },
+		.clock_rate = {
+			{ 400000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "csiphy1" },
+		.interrupt = { "csiphy1" },
+		.csiphy = {
+			.id = 1,
+			.streams_enable = true,
+			.hw_ops = &csiphy_ops_3ph_1_0,
+			.formats = &csiphy_formats_sdm845
+		}
+	},
+	/* CSIPHY2 */
+	{
+		.regulators = {
+			{ .supply = "vdda-phy", .init_load_uA = 15900 },
+			{ .supply = "vdda-pll", .init_load_uA = 8900 }
+		},
+		.clock = { "csiphy_rx", "csiphy2", "csiphy2_timer" },
+		.clock_rate = {
+			{ 400000000 },
+			{ 0 },
+			{ 400000000 },
+		},
+		.reg = { "csiphy2" },
+		.interrupt = { "csiphy2" },
+		.csiphy = {
+			.id = 2,
+			.streams_enable = true,
+			.hw_ops = &csiphy_ops_3ph_1_0,
+			.formats = &csiphy_formats_sdm845
+		}
+	},
+};
+
 static const struct camss_ife_stream csid_ife_streams_sa8775p_pix[] = {
 	{ .id = 0, .pipe = CAMSS_IFE_HW_PIPE_RDI0 },
 	{ .id = 1, .pipe = CAMSS_IFE_HW_PIPE_RDI1 },
@@ -6698,6 +6764,23 @@ static const struct camss_resources sa8775p_pix_resources = {
 	.link_entities = camss_ife_link_entities
 };
 
+static const struct camss_resources qcs8300_pix_resources = {
+	.version = CAMSS_8300,
+	.csiphy_res = csiphy_res_qcs8300_pix,
+	.tpg_res = tpg_res_8775p,
+	.csid_res = csid_res_sa8775p_pix,
+	.csid_wrapper_res = &csid_wrapper_res_sm8550,
+	.ife_res = ife_res_sa8775p_pix,
+	.icc_res = icc_res_qcs8300,
+	.icc_path_num = ARRAY_SIZE(icc_res_qcs8300),
+	.csiphy_num = ARRAY_SIZE(csiphy_res_qcs8300_pix),
+	.tpg_num = ARRAY_SIZE(tpg_res_8775p),
+	.csid_num = ARRAY_SIZE(csid_res_sa8775p_pix),
+	.ife_num = 2,
+	.ife_lite_num = 5,
+	.link_entities = camss_ife_link_entities
+};
+
 static const struct camss_resources sdm660_resources = {
 	.version = CAMSS_660,
 	.csiphy_res = csiphy_res_660,
@@ -6873,6 +6956,7 @@ static const struct of_device_id camss_dt_match[] = {
 	{ .compatible = "qcom,sm6350-camss", .data = &sm6350_resources },
 	{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
 	{ .compatible = "qcom,sa8775p-camss-pix", .data = &sa8775p_pix_resources },
+	{ .compatible = "qcom,qcs8300-camss-pix", .data = &qcs8300_pix_resources },
 	{ .compatible = "qcom,sm8250pix-camss", .data = &sm8250pix_resources },
 	{ .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
 	{ .compatible = "qcom,sm8650-camss", .data = &sm8650_resources },
-- 
2.34.1


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

* Re: [PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x
  2026-10-06 22:39 ` [PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-07  6:44   ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 0 replies; 57+ messages in thread
From: Gjorgji Rosikopulos (Consultant) @ 2026-10-07  6:44 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	hariram.purushothaman, pshrotri, Vikram Sharma, johannes.goede,
	konradybcio

Hello,

Appoagies somehow after my last rebase the co-develped by was removed. For all the changes
of SA8775P credits to Vikram and Prashant. This will be fixed in the next series.

Co-developed-by: Vikram Sharma <vikram.sharma@oss.qualcomm.com>
Signed-off-by: Vikram Sharma <vikram.sharma@oss.qualcomm.com>
Co-developed-by: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>
Signed-off-by: Prashant Shrotriya <prashant.shrotriya@oss.qualcomm.com>

~Gjorgji

On 10/7/2026 1:39 AM, "Gjorgji Rosikopulos gjorgji.rosikopulos"@oss.qualcomm.com wrote:
> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> 
> Add the IFE BUS v3.1x write engine used by the SA8775P full and lite
> pixel paths.
> 
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
>  drivers/media/platform/qcom/camss/Makefile    |   1 +
>  .../platform/qcom/camss/camss-ife-bus-v31x.c  | 267 ++++++++++++++++++
>  drivers/media/platform/qcom/camss/camss-ife.h |   1 +
>  3 files changed, 269 insertions(+)
>  create mode 100644 drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
> 
> diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
> index 500b056a906c..2251b57ba38b 100644
> --- a/drivers/media/platform/qcom/camss/Makefile
> +++ b/drivers/media/platform/qcom/camss/Makefile
> @@ -16,6 +16,7 @@ qcom-camss-objs += \
>  		camss-format.o \
>  		camss-ife.o \
>  		camss-ife-bus-v20x.o \
> +		camss-ife-bus-v31x.o \
>  		camss-ife-camif-v10x.o \
>  		camss-ife-lite-top-v10x.o \
>  		camss-ife-mod-v10x.o \
> diff --git a/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c b/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
> new file mode 100644
> index 000000000000..c3a99bd0f75e
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss/camss-ife-bus-v31x.c
> @@ -0,0 +1,267 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2023, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Technologies, Inc.
> + */
> +
> +#include <linux/bitfield.h>
> +
> +#include "camss.h"
> +#include "camss-ife.h"
> +
> +/* Block base offsets */
> +#define BUSWR_V31X_OFFS		0x400
> +#define BUSWR_V31X_LITE_OFFS	0x480
> +
> +/* Per-client register offset */
> +#define BUSWR_V31X_CLIENT_OFFS(n)	((n) * 0x100)
> +
> +/* Interface interrupt and violation status bits */
> +#define BUSWR_V31X_IIF_IRQ_CMD_CLEAR			BIT(0)
> +#define BUSWR_V31X_OVERFLOW_ERR_MASK			GENMASK(25, 0)
> +#define IS_BUSWR_V31X_OVERFLOW_ERR(s)	\
> +	((s) & BUSWR_V31X_OVERFLOW_ERR_MASK)
> +#define BUSWR_V31X_VIOLATION_ERR_MASK			GENMASK(25, 0)
> +#define IS_BUSWR_V31X_VIOLATION_ERR(s)	\
> +	((s) & BUSWR_V31X_VIOLATION_ERR_MASK)
> +#define BUSWR_V31X_IMG_SIZE_VIOLATION_ERR_MASK		GENMASK(25, 0)
> +#define IS_BUSWR_V31X_IMG_SIZE_VIOLATION_ERR(s)	\
> +	((s) & BUSWR_V31X_IMG_SIZE_VIOLATION_ERR_MASK)
> +#define BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION	BIT(31)
> +#define IS_BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(s)	\
> +	((s) & BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION)
> +#define BUSWR_V31X_IIF_IRQ_S0_VIOLATION			BIT(30)
> +#define IS_BUSWR_V31X_IIF_IRQ_S0_VIOLATION(s)	\
> +	((s) & BUSWR_V31X_IIF_IRQ_S0_VIOLATION)
> +
> +/* Client configuration field values */
> +#define BUSWR_V31X_IIF_CGC_OVERRIDE			GENMASK(26, 0)
> +#define BUSWR_V31X_CLIENT_CFG_MODE			GENMASK(17, 16)
> +#define BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN		BIT(4)
> +#define BUSWR_V31X_CLIENT_CFG_EN			BIT(0)
> +#define BUSWR_V31X_CLIENT_IMG_CFG0_HEIGHT		GENMASK(31, 16)
> +#define BUSWR_V31X_CLIENT_IMG_CFG0_WIDTH		GENMASK(15, 0)
> +#define BUSWR_V31X_CLIENT_IMG_CFG2_STRIDE		GENMASK(23, 0)
> +#define BUSWR_V31X_CLIENT_PACKER_CFG_ALIGN		BIT(5)
> +#define BUSWR_V31X_CLIENT_PACKER_CFG_FORMAT		GENMASK(4, 0)
> +#define BUSWR_V31X_PACKER_FMT_PLAIN16_10_BPP		0x5
> +#define BUSWR_V31X_PACKER_FMT_PLAIN16_16_BPP		0x8
> +#define BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD	GENMASK(4, 0)
> +#define BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN	GENMASK(31, 0)
> +#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_PERIOD		GENMASK(4, 0)
> +#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_PATTERN	GENMASK(31, 0)
> +
> +/* FIFO limit and frame-based RDI defaults */
> +#define BUSWR_V31X_IMG_ADDR_FIFO_MAX_DEPTH		2
> +#define BUSWR_V31X_RDI_DEFAULT_WIDTH			0xffff
> +#define BUSWR_V31X_RDI_DEFAULT_STRIDE			0xffff
> +#define BUSWR_V31X_CLIENT_ADDR_STATUS_2_IMAGE_ADDR_FIFO_WORD_CNT	GENMASK(2, 0)
> +
> +/* Common registers */
> +#define BUSWR_V31X_IIF_CGC_OVERRIDE_REG		0x00000008
> +#define BUSWR_V31X_WM_TEST_BUS_CTRL_REG		0x000000fc
> +#define BUSWR_V31X_IIF_IRQ_MASK_S0		0x00000018
> +#define BUSWR_V31X_IIF_IRQ_CLEAR_S0		0x00000020
> +#define BUSWR_V31X_IIF_IRQ_STATUS_S0		0x00000028
> +#define BUSWR_V31X_IIF_IRQ_CMD_REG		0x00000030
> +#define BUSWR_V31X_STATUS_CLEAR_REG		0x00000060
> +#define BUSWR_V31X_VIOLATION_STATUS_REG		0x00000064
> +#define BUSWR_V31X_OVERFLOW_STATUS_REG		0x00000068
> +#define BUSWR_V31X_IMG_SIZE_VIOLATION_REG	0x00000070
> +
> +/* Per-client registers (relative to client base = module_base + CLIENT_OFFS(n)) */
> +#define BUSWR_V31X_CLIENT_CFG_REG		0x00000200
> +#define BUSWR_V31X_CLIENT_ADDR_IMAGE_REG	0x00000204
> +#define BUSWR_V31X_CLIENT_IMG_CFG0_REG		0x0000020c
> +#define BUSWR_V31X_CLIENT_IMG_CFG2_REG		0x00000214
> +#define BUSWR_V31X_CLIENT_PACKER_CFG_REG	0x00000218
> +#define BUSWR_V31X_CLIENT_IRQ_SS_CFG0_REG	0x00000230
> +#define BUSWR_V31X_CLIENT_IRQ_SS_CFG1_REG	0x00000234
> +#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_REG	0x00000238
> +#define BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_REG	0x0000023c
> +#define BUSWR_V31X_CLIENT_ADDR_STATUS_0_REG	0x00000270
> +#define BUSWR_V31X_CLIENT_ADDR_STATUS_2_REG	0x00000278
> +
> +static void ife_buswr_31x_acquire(struct ife_device *ife, void __iomem *base)
> +{
> +	struct ife_buswr_ctx *ctx = &ife->buswr_ctx;
> +
> +	if (ife->res->type == CAMSS_IFE_SUBDEV_TYPE_IFE_LITE)
> +		ctx->base = base + BUSWR_V31X_LITE_OFFS;
> +	else
> +		ctx->base = base + BUSWR_V31X_OFFS;
> +
> +	ctx->dev = ife->camss->dev;
> +}
> +
> +static void ife_buswr_31x_release(struct ife_buswr_ctx *ctx)
> +{
> +	ctx->base = NULL;
> +}
> +
> +static void ife_buswr_31x_wm_config(struct ife_buswr_ctx *ctx, const u8 wm,
> +				    const struct camss_ife_wm_config *cfg)
> +{
> +	void __iomem *base = ctx->base;
> +	bool align = cfg->packing_fmt >= BUSWR_V31X_PACKER_FMT_PLAIN16_10_BPP &&
> +		     cfg->packing_fmt <= BUSWR_V31X_PACKER_FMT_PLAIN16_16_BPP;
> +	u32 pattern = 1;
> +	u32 period = 0;
> +	u32 bytes = cfg->bytes;
> +	u32 stride = cfg->stride;
> +	u32 lines = cfg->lines;
> +	u32 rval;
> +
> +	if (cfg->mode_config == BUSWR_CLIENT_CFG_MODE_FRAME_BASED) {
> +		bytes = BUSWR_V31X_RDI_DEFAULT_WIDTH;
> +		lines = 0;
> +		stride = BUSWR_V31X_RDI_DEFAULT_STRIDE;
> +	}
> +
> +	writel_relaxed(BUSWR_V31X_IIF_CGC_OVERRIDE,
> +		       base + BUSWR_V31X_IIF_CGC_OVERRIDE_REG);
> +	writel_relaxed(0, base + BUSWR_V31X_WM_TEST_BUS_CTRL_REG);
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_CFG_MODE, cfg->mode_config);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_PACKER_CFG_FORMAT, cfg->packing_fmt) |
> +	       FIELD_PREP(BUSWR_V31X_CLIENT_PACKER_CFG_ALIGN, align);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_PACKER_CFG_REG +
> +		       BUSWR_V31X_CLIENT_OFFS(wm));
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG0_WIDTH, bytes) |
> +	       FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG0_HEIGHT, lines);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IMG_CFG0_REG +
> +		       BUSWR_V31X_CLIENT_OFFS(wm));
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IMG_CFG2_STRIDE, stride);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IMG_CFG2_REG +
> +		       BUSWR_V31X_CLIENT_OFFS(wm));
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG0_PERIOD, period);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IRQ_SS_CFG0_REG +
> +		       BUSWR_V31X_CLIENT_OFFS(wm));
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_IRQ_SUBSAMPLE_CFG1_PATTERN, pattern);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_IRQ_SS_CFG1_REG +
> +		       BUSWR_V31X_CLIENT_OFFS(wm));
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_PERIOD, period);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_FRAMEDROP_CFG0_REG +
> +		       BUSWR_V31X_CLIENT_OFFS(wm));
> +
> +	rval = FIELD_PREP(BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_PATTERN, pattern);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_FRAMEDROP_CFG1_REG +
> +		       BUSWR_V31X_CLIENT_OFFS(wm));
> +}
> +
> +static void ife_buswr_31x_wm_enable(struct ife_buswr_ctx *ctx, u8 wm)
> +{
> +	void __iomem *base = ctx->base;
> +	u32 rval;
> +
> +	rval = readl_relaxed(base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
> +	rval |= BUSWR_V31X_CLIENT_CFG_EN | BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN;
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
> +}
> +
> +static void ife_buswr_31x_wm_disable(struct ife_buswr_ctx *ctx, u8 wm)
> +{
> +	void __iomem *base = ctx->base;
> +	u32 rval;
> +
> +	rval = readl_relaxed(base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
> +	rval &= ~(BUSWR_V31X_CLIENT_CFG_EN | BUSWR_V31X_CLIENT_CFG_AUTO_RECOVERY_EN);
> +	writel_relaxed(rval, base + BUSWR_V31X_CLIENT_CFG_REG + BUSWR_V31X_CLIENT_OFFS(wm));
> +}
> +
> +static bool ife_buswr_31x_is_fifo_full(struct ife_buswr_ctx *ctx, u8 wm)
> +{
> +	u32 rval = readl_relaxed(ctx->base + BUSWR_V31X_CLIENT_OFFS(wm) +
> +				 BUSWR_V31X_CLIENT_ADDR_STATUS_2_REG);
> +
> +	rval = FIELD_GET(BUSWR_V31X_CLIENT_ADDR_STATUS_2_IMAGE_ADDR_FIFO_WORD_CNT, rval);
> +
> +	return rval >= BUSWR_V31X_IMG_ADDR_FIFO_MAX_DEPTH;
> +}
> +
> +static void ife_buswr_31x_queue_buf(struct ife_buswr_ctx *ctx, const u8 num_wms, const u8 *wm,
> +				    dma_addr_t *addr)
> +{
> +	int i;
> +
> +	for (i = 0; i < num_wms; i++) {
> +		if (addr[i])
> +			writel_relaxed(addr[i], ctx->base + BUSWR_V31X_CLIENT_ADDR_IMAGE_REG +
> +				       BUSWR_V31X_CLIENT_OFFS(wm[i]));
> +	}
> +}
> +
> +static u32 ife_buswr_31x_consumed_addr(struct ife_buswr_ctx *ctx, u8 wm)
> +{
> +	return readl_relaxed(ctx->base + BUSWR_V31X_CLIENT_ADDR_STATUS_0_REG +
> +			     BUSWR_V31X_CLIENT_OFFS(wm));
> +}
> +
> +static u32 ife_buswr_31x_irq_status(struct ife_buswr_ctx *ctx)
> +{
> +	void __iomem *base = ctx->base;
> +	u32 bus_stat;
> +	u32 iif_stat;
> +
> +	bus_stat = readl_relaxed(base + BUSWR_V31X_OVERFLOW_STATUS_REG);
> +	writel_relaxed(BIT(0), base + BUSWR_V31X_STATUS_CLEAR_REG);
> +	if (IS_BUSWR_V31X_OVERFLOW_ERR(bus_stat)) {
> +		writel_relaxed(bus_stat, base + BUSWR_V31X_STATUS_CLEAR_REG);
> +		pr_warn("BUSWR common overflow: 0x%x\n", bus_stat);
> +	}
> +
> +	bus_stat = readl_relaxed(base + BUSWR_V31X_VIOLATION_STATUS_REG);
> +	if (IS_BUSWR_V31X_VIOLATION_ERR(bus_stat))
> +		pr_warn("BUSWR common violation: 0x%x\n", bus_stat);
> +
> +	bus_stat = readl_relaxed(base + BUSWR_V31X_IMG_SIZE_VIOLATION_REG);
> +	if (IS_BUSWR_V31X_IMG_SIZE_VIOLATION_ERR(bus_stat))
> +		pr_warn("BUSWR common image size violation: 0x%x\n", bus_stat);
> +
> +	iif_stat = readl_relaxed(base + BUSWR_V31X_IIF_IRQ_STATUS_S0);
> +	writel_relaxed(iif_stat, base + BUSWR_V31X_IIF_IRQ_CLEAR_S0);
> +	if (iif_stat) {
> +		if (IS_BUSWR_V31X_IIF_IRQ_S0_IMG_SIZE_VIOLATION(iif_stat))
> +			pr_warn("BUSWR image size violation from CCIF: 0x%x\n", iif_stat);
> +		if (IS_BUSWR_V31X_IIF_IRQ_S0_VIOLATION(iif_stat))
> +			pr_warn("BUSWR violation from CCIF: 0x%x\n", iif_stat);
> +	}
> +
> +	writel_relaxed(BUSWR_V31X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V31X_IIF_IRQ_CMD_REG);
> +
> +	return iif_stat;
> +}
> +
> +static void ife_buswr_31x_irq_enable(struct ife_buswr_ctx *ctx)
> +{
> +	writel_relaxed(~0, ctx->base + BUSWR_V31X_IIF_IRQ_MASK_S0);
> +}
> +
> +static void ife_buswr_31x_irq_disable(struct ife_buswr_ctx *ctx)
> +{
> +	void __iomem *base = ctx->base;
> +
> +	writel_relaxed(0, base + BUSWR_V31X_IIF_IRQ_MASK_S0);
> +	writel_relaxed(~0, base + BUSWR_V31X_IIF_IRQ_CLEAR_S0);
> +	writel_relaxed(BUSWR_V31X_IIF_IRQ_CMD_CLEAR, base + BUSWR_V31X_IIF_IRQ_CMD_REG);
> +}
> +
> +const struct ife_hw_buswr_ops ife_buswr_31x_ops = {
> +	.acquire	= ife_buswr_31x_acquire,
> +	.release	= ife_buswr_31x_release,
> +	.irq_enable	= ife_buswr_31x_irq_enable,
> +	.irq_disable	= ife_buswr_31x_irq_disable,
> +	.wm_config	= ife_buswr_31x_wm_config,
> +	.wm_enable	= ife_buswr_31x_wm_enable,
> +	.wm_disable	= ife_buswr_31x_wm_disable,
> +	.queue_buf	= ife_buswr_31x_queue_buf,
> +	.consumed_addr	= ife_buswr_31x_consumed_addr,
> +	.irq_status	= ife_buswr_31x_irq_status,
> +	.is_fifo_full	= ife_buswr_31x_is_fifo_full,
> +};
> diff --git a/drivers/media/platform/qcom/camss/camss-ife.h b/drivers/media/platform/qcom/camss/camss-ife.h
> index c163c336fcc3..efbb1e75d550 100644
> --- a/drivers/media/platform/qcom/camss/camss-ife.h
> +++ b/drivers/media/platform/qcom/camss/camss-ife.h
> @@ -397,6 +397,7 @@ camss_ife_pipe_for_stream_id(int nstreams,
>  extern const struct ife_pads_descriptors ife_pads_v20x;
>  extern const struct ife_hw_top_ops ife_top_20x_ops;
>  extern const struct ife_hw_buswr_ops ife_buswr_20x_ops;
> +extern const struct ife_hw_buswr_ops ife_buswr_31x_ops;
>  extern const struct camss_mod_ops ife_mod_v10x_ops;
>  
>  extern const struct ife_pads_descriptors ife_lite_pads_v20x;


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

* Re: [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant
  2026-10-06 22:39 ` [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-07  7:41   ` Krzysztof Kozlowski
  2026-10-07  7:55     ` Gjorgji Rosikopulos (Consultant)
  2026-10-07  9:52   ` Bryan O'Donoghue
  1 sibling, 1 reply; 57+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-07  7:41 UTC (permalink / raw)
  To: Gjorgji.Rosikopulos.gjorgji.rosikopulos, Bryan O'Donoghue,
	Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

On 07/10/2026 00:39,
Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> 
> Document the qcom,sm8250pix-camss compatible for CAMSS configurations
> that use the IFE pixel path. It uses the existing CAMSS property
> layout without additional properties.
> 
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>

Incomplete DCO.

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.


Best regards,
Krzysztof

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

* Re: [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant
  2026-10-07  7:41   ` Krzysztof Kozlowski
@ 2026-10-07  7:55     ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 0 replies; 57+ messages in thread
From: Gjorgji Rosikopulos (Consultant) @ 2026-10-07  7:55 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Gjorgji.Rosikopulos.gjorgji.rosikopulos,
	Bryan O'Donoghue, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	hariram.purushothaman, pshrotri, Vikram Sharma, johannes.goede,
	konradybcio

Hi Krzyztof,

On 10/7/2026 10:41 AM, Krzysztof Kozlowski wrote:
> On 07/10/2026 00:39,
> Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
>> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
>>
>> Document the qcom,sm8250pix-camss compatible for CAMSS configurations
>> that use the IFE pixel path. It uses the existing CAMSS property
>> layout without additional properties.
>>
>> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> 
> Incomplete DCO.
> 
> 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.

Thanks for pointing that it seems that i did not include all To/Cc entries,
from the get_maintainers.pl. I will resend the changes including all entries.

~Gjorgji


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

* Re: [PATCH 17/45] media: qcom: camss: add common IFE subdevice core
  2026-10-06 22:39 ` [PATCH 17/45] media: qcom: camss: add common IFE subdevice core Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-07  9:42   ` Bryan O'Donoghue
  2026-10-07  9:53     ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 1 reply; 57+ messages in thread
From: Bryan O'Donoghue @ 2026-10-07  9:42 UTC (permalink / raw)
  To: Gjorgji.Rosikopulos.gjorgji.rosikopulos, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

On 07/10/2026 00:39, 
Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> 
> Add the common IFE subdevice core. It manages routed streams and
> formats. It also handles buffers, hardware pipelines, and media
> entities.
> 
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
>   drivers/media/platform/qcom/camss/Makefile    |    1 +
>   drivers/media/platform/qcom/camss/camss-ife.c | 1617 +++++++++++++++++
>   drivers/media/platform/qcom/camss/camss.h     |   46 +
>   3 files changed, 1664 insertions(+)
>   create mode 100644 drivers/media/platform/qcom/camss/camss-ife.c
So, having a new IFE along side the existing IFE is not the right direction.

Please see my series on IFE for sm8650 Lanai - which rearchitects the 
driver to stop pretending write-master index and RDI are the right thing 
to be passing about - instead reformatting the existing code around 
lines and outputs - which is what the hardware spec itself discusses.

I'm very enthusiastic about the hardware enablement piece of this drop 
but having a whole new driver inside the existing driver is a workaround 
for the technical debt.

In fact the first and most basic thing to do is address the technical 
debt and _then_ add in hardware enablement.

I'll reup that series to V1 status - and lets look then about how to 
stack your hardware stuff for 480 on top, should be 1-4 patches extra at 
most.

---
bod

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

* Re: [PATCH 26/45] dt-bindings: media: qcom,sa8775p-camss: add pixel path variant
  2026-10-06 22:39 ` [PATCH 26/45] dt-bindings: media: qcom,sa8775p-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-07  9:49   ` Bryan O'Donoghue
  2026-10-07  9:57     ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 1 reply; 57+ messages in thread
From: Bryan O'Donoghue @ 2026-10-07  9:49 UTC (permalink / raw)
  To: Gjorgji.Rosikopulos.gjorgji.rosikopulos, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

On 07/10/2026 00:39, 
Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> 
> Document qcom,sa8775p-camss-pix for CAMSS configurations that use IFE
> pixel paths. Keep qcom,sa8775p-camss for the legacy VFE resources.
> 
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
>   .../devicetree/bindings/media/qcom,sa8775p-camss.yaml         | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
> index 48f280e99809..9206c7cfe197 100644
> --- a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
> +++ b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
> @@ -14,7 +14,9 @@ description:
>   
>   properties:
>     compatible:
> -    const: qcom,sa8775p-camss
> +    enum:
> +      - qcom,sa8775p-camss-pix
> +      - qcom,sa8775p-camss
>   
>     reg:
>       maxItems: 22

I don't get why a new compat is required.

Please look at my RFC - there's no DT depends at all in that.

The existing CAMSS node has been designed around the IFE SID - not by 
accident - I've been pushing this for months - to prepare the ground for 
not needing DT churn to enable IFE.

Sorry NAK to this.

---
bod

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

* Re: [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant
  2026-10-06 22:39 ` [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
  2026-10-07  7:41   ` Krzysztof Kozlowski
@ 2026-10-07  9:52   ` Bryan O'Donoghue
  2026-10-07 10:00     ` Gjorgji Rosikopulos (Consultant)
  1 sibling, 1 reply; 57+ messages in thread
From: Bryan O'Donoghue @ 2026-10-07  9:52 UTC (permalink / raw)
  To: Gjorgji.Rosikopulos.gjorgji.rosikopulos, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

On 07/10/2026 00:39, 
Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> 
> Document the qcom,sm8250pix-camss compatible for CAMSS configurations
> that use the IFE pixel path. It uses the existing CAMSS property
> layout without additional properties.
> 
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
>   .../devicetree/bindings/media/qcom,sm8250-camss.yaml          | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> index a509d4bbcb4a..d561179be48c 100644
> --- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> @@ -14,7 +14,9 @@ description: |
>   
>   properties:
>     compatible:
> -    const: qcom,sm8250-camss
> +    enum:
> +      - qcom,sm8250-camss
> +      - qcom,sm8250pix-camss
>   
>     clocks:
>       minItems: 37

Same feed back as on the other binding.

Enabling the PIX path shouldn't require a new compat string - 
Lanai/sm8650 operates this way and x1e/x2e will also follow this format 
in v2, v3.

Lets please align to that and work to a common cause.

---
bod

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

* Re: [PATCH 17/45] media: qcom: camss: add common IFE subdevice core
  2026-10-07  9:42   ` Bryan O'Donoghue
@ 2026-10-07  9:53     ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 0 replies; 57+ messages in thread
From: Gjorgji Rosikopulos (Consultant) @ 2026-10-07  9:53 UTC (permalink / raw)
  To: Bryan O'Donoghue, Gjorgji.Rosikopulos.gjorgji.rosikopulos,
	Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	hariram.purushothaman, pshrotri, Vikram Sharma, johannes.goede,
	konradybcio

Hi Bryan,

On 10/7/2026 12:42 PM, Bryan O'Donoghue wrote:
> On 07/10/2026 00:39, 
> Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
>> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
>>
>> Add the common IFE subdevice core. It manages routed streams and
>> formats. It also handles buffers, hardware pipelines, and media
>> entities.
>>
>> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
>> ---
>>   drivers/media/platform/qcom/camss/Makefile    |    1 +
>>   drivers/media/platform/qcom/camss/camss-ife.c | 1617 +++++++++++++++++
>>   drivers/media/platform/qcom/camss/camss.h     |   46 +
>>   3 files changed, 1664 insertions(+)
>>   create mode 100644 drivers/media/platform/qcom/camss/camss-ife.c
> So, having a new IFE along side the existing IFE is not the right direction.
> 
> Please see my series on IFE for sm8650 Lanai - which rearchitects the 
> driver to stop pretending write-master index and RDI are the right thing 
> to be passing about - instead reformatting the existing code around 
> lines and outputs - which is what the hardware spec itself discusses.
> 
> I'm very enthusiastic about the hardware enablement piece of this drop 
> but having a whole new driver inside the existing driver is a workaround 
> for the technical debt.
> 
> In fact the first and most basic thing to do is address the technical 
> debt and _then_ add in hardware enablement.
> 
> I'll reup that series to V1 status - and lets look then about how to 
> stack your hardware stuff for 480 on top, should be 1-4 patches extra at 
> most.

Do you not think it is time to left vfe and move to new code, but perhaps that discussion
will not came to an end, because you dont even want to argue whether to have VFE as it is :-).
If we go with existing VFE i think will be good to have per pad
descriptors and abstraction for the internal modules. And i dont think is ready 
to have multiple pads and streams as of today but that can be fixed.

However i know your opinion from the beginning and you will not gone give up
from that. I will wait to enable the 480 and move forward.

I am reviewing the series you have posted lets see how will look like.

BTW maybe other reviewers will be interested to look at the code as it is.

~Gjorgji

> 
> ---
> bod


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

* Re: [PATCH 26/45] dt-bindings: media: qcom,sa8775p-camss: add pixel path variant
  2026-10-07  9:49   ` Bryan O'Donoghue
@ 2026-10-07  9:57     ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 0 replies; 57+ messages in thread
From: Gjorgji Rosikopulos (Consultant) @ 2026-10-07  9:57 UTC (permalink / raw)
  To: Bryan O'Donoghue, Gjorgji.Rosikopulos.gjorgji.rosikopulos,
	Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	hariram.purushothaman, pshrotri, Vikram Sharma, johannes.goede,
	konradybcio

Hi Bryan,

On 10/7/2026 12:49 PM, Bryan O'Donoghue wrote:
> On 07/10/2026 00:39, 
> Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
>> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
>>
>> Document qcom,sa8775p-camss-pix for CAMSS configurations that use IFE
>> pixel paths. Keep qcom,sa8775p-camss for the legacy VFE resources.
>>
>> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
>> ---
>>   .../devicetree/bindings/media/qcom,sa8775p-camss.yaml         | 4 +++-
>>   1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
>> index 48f280e99809..9206c7cfe197 100644
>> --- a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
>> +++ b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml
>> @@ -14,7 +14,9 @@ description:
>>   
>>   properties:
>>     compatible:
>> -    const: qcom,sa8775p-camss
>> +    enum:
>> +      - qcom,sa8775p-camss-pix
>> +      - qcom,sa8775p-camss
>>   
>>     reg:
>>       maxItems: 22
> 
> I don't get why a new compat is required.
> 
> Please look at my RFC - there's no DT depends at all in that.
> 
> The existing CAMSS node has been designed around the IFE SID - not by 
> accident - I've been pushing this for months - to prepare the ground for 
> not needing DT churn to enable IFE.
> 
> Sorry NAK to this.

Yes i have expected NAK. And it was mention in cover letter that
this option may not be accepted, but it is convenient to test new
topology. Anyway as answered we will wait for your new series.

BTW just from curiosity do you run parallel streaming on pix and rdi?
Does the functionality for all streams to output in parallel along with the
statistics working?

~Gjorgji

> 
> ---
> bod


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

* Re: [PATCH 00/45] media: qcom: camss: add IFE pixel path support
  2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
                   ` (44 preceding siblings ...)
  2026-10-06 22:39 ` [PATCH 45/45] media: qcom: camss: sa8775p: add pixel path resources for QCS8300 Gjorgji.Rosikopulos.gjorgji.rosikopulos
@ 2026-10-07  9:59 ` Bryan O'Donoghue
  2026-10-07 10:03   ` Gjorgji Rosikopulos (Consultant)
  45 siblings, 1 reply; 57+ messages in thread
From: Bryan O'Donoghue @ 2026-10-07  9:59 UTC (permalink / raw)
  To: Gjorgji.Rosikopulos.gjorgji.rosikopulos, Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Gjorgji Rosikopulos, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, hariram.purushothaman,
	pshrotri, Vikram Sharma, johannes.goede, konradybcio

On 07/10/2026 00:39, 
Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
> NOTE: Bryan has posted RFC changes for pixel path params and
> statistics. This series was ready to be posted at that time, so it is
> not rebased on top of Bryan's implementation. Both implementations
> share common parts, but take a different direction towards enabling
> the pixel path: Bryan's series extends camss-vfe, while this series
> introduces the new camss-ife sub-device described above.

Indeed.

I have posted an evolutionary approach - shifting from and fixing the 
original sin of mixing write-master and rdi indexing - as a first up fix 
to the driver, and then placing new features on top.

What I don't support here - is a parallel in-driver new driver that 
leaves legacy stuff as-is and just shoots off and does something easier 
in its own isolated place.

Great to see the willingness of qcom to follow up on my lead, lets work 
together to integrate your stuff on top.

---
bod

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

* Re: [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant
  2026-10-07  9:52   ` Bryan O'Donoghue
@ 2026-10-07 10:00     ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 0 replies; 57+ messages in thread
From: Gjorgji Rosikopulos (Consultant) @ 2026-10-07 10:00 UTC (permalink / raw)
  To: Bryan O'Donoghue, Gjorgji.Rosikopulos.gjorgji.rosikopulos,
	Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	hariram.purushothaman, pshrotri, Vikram Sharma, johannes.goede,
	konradybcio

Hi Bryan,

On 10/7/2026 12:52 PM, Bryan O'Donoghue wrote:
> On 07/10/2026 00:39, 
> Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
>> From: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
>>
>> Document the qcom,sm8250pix-camss compatible for CAMSS configurations
>> that use the IFE pixel path. It uses the existing CAMSS property
>> layout without additional properties.
>>
>> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
>> ---
>>   .../devicetree/bindings/media/qcom,sm8250-camss.yaml          | 4 +++-
>>   1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
>> index a509d4bbcb4a..d561179be48c 100644
>> --- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
>> +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
>> @@ -14,7 +14,9 @@ description: |
>>   
>>   properties:
>>     compatible:
>> -    const: qcom,sm8250-camss
>> +    enum:
>> +      - qcom,sm8250-camss
>> +      - qcom,sm8250pix-camss
>>   
>>     clocks:
>>       minItems: 37
> 
> Same feed back as on the other binding.
> 
> Enabling the PIX path shouldn't require a new compat string - 
> Lanai/sm8650 operates this way and x1e/x2e will also follow this format 
> in v2, v3.

I agree, maybe pix should always be enabled, hope userspace not break because
topology will have more outputs. Not sure how to handle this case for backward 
compatibility. In theory if userspace apps are well written they should just work.

As an option do you think that camss-ife can be migrated to camss-pix and connected
the pix sub-device just for pixel pipeline?

~Gjorgji

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

* Re: [PATCH 00/45] media: qcom: camss: add IFE pixel path support
  2026-10-07  9:59 ` [PATCH 00/45] media: qcom: camss: add IFE pixel path support Bryan O'Donoghue
@ 2026-10-07 10:03   ` Gjorgji Rosikopulos (Consultant)
  0 siblings, 0 replies; 57+ messages in thread
From: Gjorgji Rosikopulos (Consultant) @ 2026-10-07 10:03 UTC (permalink / raw)
  To: Bryan O'Donoghue, Gjorgji.Rosikopulos.gjorgji.rosikopulos,
	Mauro Carvalho Chehab
  Cc: Vladimir Zapolskiy, Loic Poulain, Dmitry Baryshkov,
	Atanas Filipov, Jigarkumar Zala, linux-media, linux-arm-msm,
	linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	hariram.purushothaman, pshrotri, Vikram Sharma, johannes.goede,
	konradybcio



On 10/7/2026 12:59 PM, Bryan O'Donoghue wrote:
> On 07/10/2026 00:39, 
> Gjorgji.Rosikopulos.gjorgji.rosikopulos@oss.qualcomm.com wrote:
>> NOTE: Bryan has posted RFC changes for pixel path params and
>> statistics. This series was ready to be posted at that time, so it is
>> not rebased on top of Bryan's implementation. Both implementations
>> share common parts, but take a different direction towards enabling
>> the pixel path: Bryan's series extends camss-vfe, while this series
>> introduces the new camss-ife sub-device described above.
> 
> Indeed.
> 
> I have posted an evolutionary approach - shifting from and fixing the 
> original sin of mixing write-master and rdi indexing - as a first up fix 
> to the driver, and then placing new features on top.
> 
> What I don't support here - is a parallel in-driver new driver that 
> leaves legacy stuff as-is and just shoots off and does something easier 
> in its own isolated place.
> 
> Great to see the willingness of qcom to follow up on my lead, lets work 
> together to integrate your stuff on top.

I agree, lets work together to move forward on this topic.

~Gjorgji

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

end of thread, other threads:[~2026-10-07 10:03 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-06 22:39 [PATCH 00/45] media: qcom: camss: add IFE pixel path support Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 01/45] media: qcom: camss: support CSID streams on one source pad Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 02/45] media: qcom: camss: add CSID gen2 pixel path Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 03/45] media: qcom: camss: report probe failures Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 04/45] dt-bindings: media: qcom,sm8250-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-07  7:41   ` Krzysztof Kozlowski
2026-10-07  7:55     ` Gjorgji Rosikopulos (Consultant)
2026-10-07  9:52   ` Bryan O'Donoghue
2026-10-07 10:00     ` Gjorgji Rosikopulos (Consultant)
2026-10-06 22:39 ` [PATCH 05/45] media: qcom: camss: keep CSIPHY lanes active while streaming Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 06/45] media: qcom: camss: add IFE subdevice interface Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 07/45] media: qcom: camss: sm8250: add IFE BUS v20x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 08/45] media: qcom: camss: sm8250: add IFE CamIF v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 09/45] media: qcom: camss: sm8250: add IFE scaler v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 10/45] media: qcom: camss: sm8250: add IFE module engine v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 11/45] media: qcom: camss: sm8250: add IFE top v20x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 12/45] media: qcom: camss: sm8250: add IFE Lite top v10x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 13/45] media: qcom: camss: sm8250: add IFE pad descriptors Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 14/45] media: qcom: camss: formats: add pixel format lookup helpers Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 15/45] media: qcom: camss: video: use helpers for line-based formats Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 16/45] media: qcom: camss: video: add managed streaming callbacks Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 17/45] media: qcom: camss: add common IFE subdevice core Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-07  9:42   ` Bryan O'Donoghue
2026-10-07  9:53     ` Gjorgji Rosikopulos (Consultant)
2026-10-06 22:39 ` [PATCH 18/45] media: qcom: camss: integrate IFE devices Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 19/45] media: qcom: camss: sm8250: add pixel path resources Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 20/45] media: uapi: add Qualcomm IFE statistics Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 21/45] media: v4l2: add Qualcomm IFE statistics format Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 22/45] media: qcom: camss: add IFE statistics output Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 23/45] media: uapi: add Qualcomm IFE parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 24/45] media: v4l2: add Qualcomm IFE parameters format Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 25/45] media: qcom: camss: sm8250: apply IFE parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 26/45] dt-bindings: media: qcom,sa8775p-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-07  9:49   ` Bryan O'Donoghue
2026-10-07  9:57     ` Gjorgji Rosikopulos (Consultant)
2026-10-06 22:39 ` [PATCH 27/45] media: qcom: camss: sa8775p: add IFE BUS v3.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-07  6:44   ` Gjorgji Rosikopulos (Consultant)
2026-10-06 22:39 ` [PATCH 28/45] media: qcom: camss: sa8775p: add IFE scaler v2.3x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 29/45] media: qcom: camss: sa8775p: add IFE top v4.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 30/45] media: qcom: camss: sa8775p: add IFE lite top v3.0x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 31/45] media: qcom: camss: sa8775p: add IFE pad descriptors Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 32/45] media: qcom: camss: sa8775p: add IFE completion groups Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 33/45] media: qcom: camss: sa8775p: add CSID pixel path Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 34/45] media: qcom: camss: sa8775p: add IFE modules v4.1x Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 35/45] media: qcom: camss: sa8775p: add pixel path resources Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 36/45] media: qcom: camss: sa8775p: add CCM parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 37/45] media: qcom: camss: sa8775p: add LSC parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 38/45] media: qcom: camss: sa8775p: add gamma parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 39/45] media: qcom: camss: sa8775p: add white balance parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 40/45] media: qcom: camss: sa8775p: add AWB BG parameters Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 41/45] media: qcom: camss: create TPG links for IFE Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 42/45] media: qcom: camss: use one lane for TPG internal path Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 43/45] media: qcom: camss: ife: queue params buffers in FIFO order Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 44/45] dt-bindings: media: qcom,qcs8300-camss: add pixel path variant Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-06 22:39 ` [PATCH 45/45] media: qcom: camss: sa8775p: add pixel path resources for QCS8300 Gjorgji.Rosikopulos.gjorgji.rosikopulos
2026-10-07  9:59 ` [PATCH 00/45] media: qcom: camss: add IFE pixel path support Bryan O'Donoghue
2026-10-07 10:03   ` Gjorgji Rosikopulos (Consultant)

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

all inboxes | Powered by JetHome®