staging: media: ipu3: Stop streaming in inverse order of starting

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>
This commit is contained in:
Max Staudt 2024-06-20 23:45:43 +09:00 committed by Hans Verkuil
parent cf27c5a1bd
commit 5256cfec79

View 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);
}