On Tue Nov 25 15:39:21 2025 +0200, Sakari Ailus wrote:
> For each virtual channel, start streaming once all buffer queues connected
> via enabled routes have been set streaming, similarly stop when the first
> queue is stopped.
> 
> Signed-off-by: Sakari Ailus <[email protected]>
> Reviewed-by: Antti Laakso <[email protected]>

Patch committed.

Thanks,
Sakari Ailus

 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c  | 112 ++++++++++++++++++++++++-
 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h  |   1 +
 drivers/media/pci/intel/ipu6/ipu6-isys-queue.c |   7 +-
 3 files changed, 110 insertions(+), 10 deletions(-)

---

diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c 
b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
index f92577054d89..3c0df92bc6c7 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
@@ -12,6 +12,7 @@
 #include <linux/io.h>
 #include <linux/minmax.h>
 #include <linux/sprintf.h>
+#include <linux/string_choices.h>
 
 #include <media/media-entity.h>
 #include <media/v4l2-ctrls.h>
@@ -435,6 +436,91 @@ static int ipu7_isys_csi2_stream_enable(struct 
ipu6_isys_csi2 *csi2)
        return 0;
 }
 
+static int ipu6_isys_csi2_streaming_change(struct ipu6_isys_subdev *asd,
+                                          struct v4l2_subdev_state *state,
+                                          u32 pad, u8 *vc, bool enable)
+{
+       struct v4l2_mbus_frame_desc_entry *this_entry = NULL;
+       struct v4l2_subdev_route *route, *this_route = NULL;
+       u32 streams_enabled = 0, nodes_streaming = 0;
+       struct v4l2_mbus_frame_desc desc = { 0 };
+       int ret;
+
+       for_each_active_route(&state->routing, this_route)
+               if (pad == this_route->source_pad)
+                       break;
+       if (!this_route) {
+               dev_dbg(asd->sd.dev, "no route found for pad %u\n", pad);
+               return -EINVAL;
+       }
+
+       struct media_pad *remote_pad =
+               
media_pad_remote_pad_first(&asd->sd.entity.pads[this_route->sink_pad]);
+       struct v4l2_subdev *remote_sd =
+               media_entity_to_v4l2_subdev(remote_pad->entity);
+       ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc,
+                              remote_pad->index, &desc);
+       if (ret)
+               return ret;
+
+       for (unsigned int i = 0; i < desc.num_entries; i++) {
+               if (desc.entry[i].stream == this_route->sink_stream) {
+                       this_entry = &desc.entry[i];
+                       break;
+               }
+       }
+       if (!this_entry) {
+               dev_dbg(asd->sd.dev,
+                       "no frame descriptor entry found for stream %u\n",
+                       this_route->sink_stream);
+               return -EINVAL;
+       }
+
+       for_each_active_route(&state->routing, route) {
+               struct v4l2_mbus_frame_desc_entry *entry = NULL;
+
+               for (unsigned int i = 0; i < desc.num_entries; i++) {
+                       if (desc.entry[i].stream == route->sink_stream) {
+                               entry = &desc.entry[i];
+                               break;
+                       }
+               }
+
+               if (!entry) {
+                       dev_dbg(asd->sd.dev, "cannot find stream %u from frame 
descriptor\n",
+                               route->sink_stream);
+                       return -EINVAL;
+               }
+
+               if (entry->bus.csi2.vc != this_entry->bus.csi2.vc)
+                       continue;
+
+               struct media_pad *video_pad =
+                       
media_pad_remote_pad_first(&asd->sd.entity.pads[route->source_pad]);
+               if (!video_pad)
+                       return -EINVAL;
+
+               struct ipu6_isys_video *av =
+                       container_of_const(video_pad, struct ipu6_isys_video,
+                                          pad);
+
+               streams_enabled++;
+               if (av->streaming || (enable && pad == route->source_pad))
+                       nodes_streaming++;
+       }
+
+       *vc = this_entry->bus.csi2.vc;
+
+       if (streams_enabled == nodes_streaming) {
+               dev_dbg(asd->sd.dev,
+                       "changing streaming state to %s on \"%s\":%u\n",
+                       str_enabled_disabled(enable), asd->sd.entity.name, pad);
+               return 1;
+       }
+
+       return 0;
+}
+
 static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd,
                                         struct v4l2_subdev_state *state,
                                         u32 pad, u64 streams_mask)
@@ -450,6 +536,7 @@ static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev 
*sd,
        struct ipu6_isys_buffer_list bl;
        u64 sink_streams;
        int ret;
+       u8 vc;
 
        ret = ipu6_isys_buffer_list_get(av->stream, &bl);
        if (ret < 0) {
@@ -459,6 +546,10 @@ static int ipu6_isys_csi2_enable_streams(struct 
v4l2_subdev *sd,
 
        list_add(&av->csi2_entry, &csi2->av_head);
 
+       ret = ipu6_isys_csi2_streaming_change(asd, state, pad, &vc, true);
+       if (ret <= 0)
+               return ret;
+
        ret = ipu6_isys_start_stream_firmware(av, &bl);
        if (ret) {
                dev_err(sd->dev, "start stream of firmware failed\n");
@@ -472,16 +563,20 @@ static int ipu6_isys_csi2_enable_streams(struct 
v4l2_subdev *sd,
                v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
                                                &streams_mask);
 
-       ret = IS_IPU7(isp) ? ipu7_isys_csi2_stream_enable(csi2) :
-                            ipu6_isys_csi2_stream_enable(csi2);
-       if (ret)
-               goto err_stop_stream_firmware;
+       if (!csi2->streaming_vc) {
+               ret = IS_IPU7(isp) ? ipu7_isys_csi2_stream_enable(csi2) :
+                                    ipu6_isys_csi2_stream_enable(csi2);
+               if (ret)
+                       goto err_stop_stream_firmware;
+       }
 
        ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index,
                                         sink_streams);
        if (ret)
                goto err_stop_stream_csi2;
 
+       csi2->streaming_vc |= BIT(vc);
+
        return 0;
 
 err_stop_stream_csi2:
@@ -514,6 +609,14 @@ static int ipu6_isys_csi2_disable_streams(struct 
v4l2_subdev *sd,
        struct ipu6_device *isp = asd->isys->adev->isp;
        struct v4l2_subdev *remote_sd;
        u64 sink_streams;
+       int ret;
+       u8 vc;
+
+       ret = ipu6_isys_csi2_streaming_change(asd, state, pad, &vc, false);
+       if (ret <= 0)
+               goto out_del_csi2_entry;
+
+       csi2->streaming_vc &= ~BIT(vc);
 
        sink_streams =
                v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
@@ -533,6 +636,7 @@ static int ipu6_isys_csi2_disable_streams(struct 
v4l2_subdev *sd,
 
        ipu6_isys_close_streaming_firmware(av);
 
+out_del_csi2_entry:
        list_del(&av->csi2_entry);
 
        return 0;
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h 
b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
index 7c936154691e..a98a60e443c6 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
@@ -49,6 +49,7 @@ struct ipu6_isys_csi2 {
        unsigned int port;
        u32 legacy_irq_mask;
        unsigned int phy_mode;
+       u32 streaming_vc;
 };
 
 struct ipu6_isys_csi2_timing {
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c 
b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
index 35cfd7a10882..f70c6f926229 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
@@ -533,14 +533,10 @@ static int start_streaming(struct vb2_queue *q, unsigned 
int count)
        ipu6_isys_configure_stream_watermark(av, source_pad->entity);
        ipu6_isys_update_stream_watermark(av, true);
 
-       if (stream->nr_streaming != stream->nr_queues)
-               goto out;
-
        ret = ipu6_isys_stream_start(av);
        if (ret)
                goto out_stream_start;
 
-out:
        mutex_unlock(&stream->mutex);
 
        return 0;
@@ -574,8 +570,7 @@ static void stop_streaming(struct vb2_queue *q)
        ipu6_isys_update_stream_watermark(av, false);
 
        mutex_lock(&av->isys->stream_mutex);
-       if (stream->nr_streaming == stream->nr_queues)
-               ipu6_isys_video_set_streaming(av, 0);
+       ipu6_isys_video_set_streaming(av, 0);
        list_del(&aq->node);
        mutex_unlock(&av->isys->stream_mutex);
 
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to