[PATCH 7/8] media: qcom: camss: add streams API support in camss-video
From: Gjorgji . Rosikopulos . gjorgji . rosikopulos
Date: Fri Sep 11 2026 - 02:32:07 EST
From: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>
Generalize the video device pipeline walk to be streams-API-aware:
when a directly-connected subdev supports enable_streams/
disable_streams, use it; otherwise fall back to the existing legacy
start/stop path, which walks the pipeline one subdev at a time via
.s_stream(), unchanged.
video_stop_streaming() must always call video_device_pipeline_stop()
and flush_buffers(), regardless of whether the disable path succeeded:
vb2 requires stop_streaming() to return all buffers, and a successful
pipeline_alloc_start() must always be matched by a pipeline_stop().
Make the legacy fallback branch match the streams-API branch by
logging a failure instead of returning early on it.
Signed-off-by: Gjorgji Rosikopulos <gjorgji.rosikopulos@xxxxxxxxxxxxxxxx>
---
.../media/platform/qcom/camss/camss-video.c | 119 ++++++++++++++----
1 file changed, 94 insertions(+), 25 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 0852eb6f1315..7f7212996c4a 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -249,26 +249,27 @@ static int video_prepare_streaming(struct vb2_queue *q)
return ret;
}
-static int video_start_streaming(struct vb2_queue *q, unsigned int count)
+/*
+ * video_start_streaming_legacy - Walk the whole subdev chain and enable
+ * streaming via .s_stream()
+ * @video: Video device
+ * @sink_pad: Video device's own (single) pad
+ *
+ * Fallback used when the subdev directly connected to @sink_pad has no
+ * .enable_streams op. Mirrors the pre-streams-API pipeline walk: each
+ * subdev's pad 0 is assumed to be its sink pad, and streaming is started
+ * one subdev at a time via .s_stream() rather than the streams API.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int video_start_streaming_legacy(struct camss_video *video,
+ struct media_pad *sink_pad)
{
- struct camss_video *video = vb2_get_drv_priv(q);
- struct video_device *vdev = &video->vdev;
- struct media_entity *entity;
+ struct media_entity *entity = sink_pad->entity;
struct media_pad *pad;
struct v4l2_subdev *subdev;
int ret;
- ret = video_device_pipeline_alloc_start(vdev);
- if (ret < 0) {
- dev_err(video->camss->dev, "Failed to start media pipeline: %d\n", ret);
- goto flush_buffers;
- }
-
- ret = video_check_format(video);
- if (ret < 0)
- goto error;
-
- entity = &vdev->entity;
while (1) {
pad = &entity->pads[0];
if (!(pad->flags & MEDIA_PAD_FL_SINK))
@@ -283,6 +284,42 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int video_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct camss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+ struct media_pad *sink_pad = &vdev->entity.pads[0];
+ struct media_pad *src_pad;
+ struct v4l2_subdev *subdev = NULL;
+ int ret;
+
+ ret = video_device_pipeline_alloc_start(vdev);
+ if (ret < 0) {
+ dev_err(video->camss->dev, "Failed to start media pipeline: %d\n", ret);
+ goto flush_buffers;
+ }
+
+ ret = video_check_format(video);
+ if (ret < 0)
+ goto error;
+
+ 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);
+
+ if (subdev && v4l2_subdev_has_op(subdev, pad, enable_streams)) {
+ ret = v4l2_subdev_enable_streams(subdev, src_pad->index, BIT_ULL(0));
+ if (ret < 0)
+ goto error;
+ } else {
+ ret = video_start_streaming_legacy(video, sink_pad);
+ if (ret < 0)
goto error;
}
@@ -297,16 +334,27 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
return ret;
}
-static void video_stop_streaming(struct vb2_queue *q)
+/*
+ * video_stop_streaming_legacy - Walk the whole subdev chain and disable
+ * streaming via .s_stream()
+ * @video: Video device
+ * @sink_pad: Video device's own (single) pad
+ *
+ * Fallback used when the subdev directly connected to @sink_pad has no
+ * .disable_streams op. Mirrors the pre-streams-API pipeline walk: each
+ * subdev's pad 0 is assumed to be its sink pad, and streaming is stopped
+ * one subdev at a time via .s_stream() rather than the streams API.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int video_stop_streaming_legacy(struct camss_video *video,
+ struct media_pad *sink_pad)
{
- struct camss_video *video = vb2_get_drv_priv(q);
- struct video_device *vdev = &video->vdev;
- struct media_entity *entity;
+ struct media_entity *entity = sink_pad->entity;
struct media_pad *pad;
struct v4l2_subdev *subdev;
int ret;
- entity = &vdev->entity;
while (1) {
pad = &entity->pads[0];
if (!(pad->flags & MEDIA_PAD_FL_SINK))
@@ -320,13 +368,34 @@ static void video_stop_streaming(struct vb2_queue *q)
subdev = media_entity_to_v4l2_subdev(entity);
ret = v4l2_subdev_call(subdev, video, s_stream, 0);
-
- if (ret) {
- dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
- return;
- }
+ if (ret)
+ return ret;
}
+ return 0;
+}
+
+static void video_stop_streaming(struct vb2_queue *q)
+{
+ struct camss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+ struct media_pad *sink_pad = &vdev->entity.pads[0];
+ struct media_pad *src_pad;
+ struct v4l2_subdev *subdev = NULL;
+ int ret;
+
+ 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);
+
+ if (subdev && v4l2_subdev_has_op(subdev, pad, disable_streams))
+ ret = v4l2_subdev_disable_streams(subdev, src_pad->index, BIT_ULL(0));
+ else
+ ret = video_stop_streaming_legacy(video, sink_pad);
+
+ if (ret)
+ dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
+
video_device_pipeline_stop(vdev);
video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
--
2.34.1