]> www.infradead.org Git - linux.git/commitdiff
staging: media: ipu3: Stop streaming in inverse order of starting
authorMax Staudt <mstaudt@chromium.org>
Thu, 20 Jun 2024 14:45:43 +0000 (23:45 +0900)
committerHans Verkuil <hverkuil-cisco@xs4all.nl>
Sat, 31 Aug 2024 07:40:42 +0000 (09:40 +0200)
imgu_vb2_stop_streaming() did not order shutdown items in the inverse
order and count of what imgu_vb2_start_streaming() does. Consequently,
v6.7's new WARN_ON in call_s_stream() started screaming because it was
called multiple times on the entire pipe, yet it should only be called
when the pipe is interrupted by any first node being taken offline.

This reorders streamoff to be the inverse of streamon, and uses
analogous conditions to decide when and how often to call additional
teardown functions.

v4l2_subdev_call(s_stream, 0) remains outside the streaming_lock,
analogously to imgu_vb2_start_streaming().

Signed-off-by: Max Staudt <mstaudt@chromium.org>
Reviewed-by: Bingbu Cao <bingbu.cao@intel.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
drivers/staging/media/ipu3/ipu3-v4l2.c

index 3ff390b04e1ab83049a3d517f856bebe4d905c0a..e7aee7e3db5bbdb8c0cb555e5d2a5c2e4ae0c161 100644 (file)
@@ -535,29 +535,51 @@ static void imgu_vb2_stop_streaming(struct vb2_queue *vq)
                container_of(vq, struct imgu_video_device, vbq);
        int r;
        unsigned int pipe;
+       bool stop_streaming = false;
 
+       /* Verify that the node had been setup with imgu_v4l2_node_setup() */
        WARN_ON(!node->enabled);
 
        pipe = node->pipe;
        dev_dbg(dev, "Try to stream off node [%u][%u]", pipe, node->id);
-       imgu_pipe = &imgu->imgu_pipe[pipe];
-       r = v4l2_subdev_call(&imgu_pipe->imgu_sd.subdev, video, s_stream, 0);
-       if (r)
-               dev_err(&imgu->pci_dev->dev,
-                       "failed to stop subdev streaming\n");
 
+       /*
+        * When the first node of a streaming setup is stopped, the entire
+        * pipeline needs to stop before individual nodes are disabled.
+        * Perform the inverse of the initial setup.
+        *
+        * Part 1 - s_stream on the entire pipeline
+        */
        mutex_lock(&imgu->streaming_lock);
-       /* Was this the first node with streaming disabled? */
        if (imgu->streaming) {
                /* Yes, really stop streaming now */
                dev_dbg(dev, "IMGU streaming is ready to stop");
                r = imgu_s_stream(imgu, false);
                if (!r)
                        imgu->streaming = false;
+               stop_streaming = true;
        }
-
        mutex_unlock(&imgu->streaming_lock);
 
+       /* Part 2 - s_stream on subdevs
+        *
+        * If we call s_stream multiple times, Linux v6.7's call_s_stream()
+        * WARNs and aborts. Thus, disable all pipes at once, and only once.
+        */
+       if (stop_streaming) {
+               for_each_set_bit(pipe, imgu->css.enabled_pipes,
+                                IMGU_MAX_PIPE_NUM) {
+                       imgu_pipe = &imgu->imgu_pipe[pipe];
+
+                       r = v4l2_subdev_call(&imgu_pipe->imgu_sd.subdev,
+                                            video, s_stream, 0);
+                       if (r)
+                               dev_err(&imgu->pci_dev->dev,
+                                       "failed to stop subdev streaming\n");
+               }
+       }
+
+       /* Part 3 - individual node teardown */
        video_device_pipeline_stop(&node->vdev);
        imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR);
 }