[PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API
From: Hitesh Patel
Date: Mon Sep 14 2026 - 10:00:34 EST
video_start_streaming() and video_stop_streaming() drive every subdev
of the pipeline through video.s_stream. That op is per subdev, and
the core allows one transition per subdev, so a transmitter that is
shared by two independent pipelines can only be started once and is
stopped by the first pipeline that stops.
A GMSL deserializer with two CSI-2 output ports, one camera routed to
each, is exactly that case: the same subdev sits at the head of two
pipelines that otherwise share nothing (each has its own CSIPHY, CSID
and VFE). The second camera never starts, and stopping either one
kills the other.
Such transmitters implement the V4L2 streams API and advertise it
with V4L2_SUBDEV_FL_STREAMS. For those, enable or disable only the
streams routed to the source pad the pipeline arrived through, using
v4l2_subdev_enable_streams()/v4l2_subdev_disable_streams(). The core
refcounts enabled streams per pad, so the two pipelines no longer
interfere. Enabling streams on a source pad propagates upstream to
the sensor by itself, so the walk ends there rather than starting
the rest of the chain a second time through s_stream.
Subdevs without the flag keep using video.s_stream exactly as
before.
Signed-off-by: Hitesh Patel <hitesh@xxxxxxxxxxxxxx>
---
.../media/platform/qcom/camss/camss-video.c | 56 +++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 16c5f3748..90f22ce76 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -249,6 +249,32 @@ static int video_prepare_streaming(struct vb2_queue *q)
return ret;
}
+/*
+ * video_source_pad_streams - Streams routed to a subdev source pad
+ * @sd: Streams-aware subdevice
+ * @pad: Source pad index on @sd
+ *
+ * Return the mask of streams of the active routes ending on @pad.
+ */
+static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *route;
+ u64 mask = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ if (!state)
+ return 0;
+
+ for_each_active_route(&state->routing, route)
+ if (route->source_pad == pad)
+ mask |= BIT_ULL(route->source_stream);
+
+ v4l2_subdev_unlock_state(state);
+
+ return mask;
+}
+
/*
* video_subdev_set_stream - Start or stop a subdev of the pipeline
* @video: CAMSS video device
@@ -324,6 +350,20 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
entity = pad->entity;
subdev = media_entity_to_v4l2_subdev(entity);
+ if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
+ u64 mask = video_source_pad_streams(subdev, pad->index);
+
+ if (!mask)
+ break;
+
+ ret = v4l2_subdev_enable_streams(subdev, pad->index,
+ mask);
+ if (ret && ret != -EALREADY)
+ goto error;
+
+ break;
+ }
+
ret = video_subdev_set_stream(video, subdev, true);
if (ret < 0 && ret != -ENOIOCTLCMD)
goto error;
@@ -362,6 +402,22 @@ static void video_stop_streaming(struct vb2_queue *q)
entity = pad->entity;
subdev = media_entity_to_v4l2_subdev(entity);
+ if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
+ u64 mask = video_source_pad_streams(subdev, pad->index);
+
+ if (!mask)
+ break;
+
+ ret = v4l2_subdev_disable_streams(subdev, pad->index,
+ mask);
+ if (ret && ret != -EALREADY)
+ dev_err(video->camss->dev,
+ "Failed to disable streams %#llx on %s:%u: %d\n",
+ mask, subdev->name, pad->index, ret);
+
+ break;
+ }
+
ret = video_subdev_set_stream(video, subdev, false);
if (ret) {
dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
--
2.43.0