diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index ee159b4341ab..f481d1ca32ab 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -359,6 +359,18 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) { int ret; + /* + * The .s_stream() operation must never be called to start or stop an + * already started or stopped subdev. Catch offenders but don't return + * an error yet to avoid regressions. + * + * As .s_stream() is mutually exclusive with the .enable_streams() and + * .disable_streams() operation, we can use the enabled_streams field + * to store the subdev streaming state. + */ + if (WARN_ON(!!sd->enabled_streams == !!enable)) + return 0; + #if IS_REACHABLE(CONFIG_LEDS_CLASS) if (!IS_ERR_OR_NULL(sd->privacy_led)) { if (enable) @@ -372,9 +384,12 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) if (!enable && ret < 0) { dev_warn(sd->dev, "disabling streaming failed (%d)\n", ret); - return 0; + ret = 0; } + if (!ret) + sd->enabled_streams = enable ? BIT(0) : 0; + return ret; } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index d9fca929c10b..ab2a7ef61d42 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -446,7 +446,9 @@ enum v4l2_subdev_pre_streamon_flags { * @s_stream: start (enabled == 1) or stop (enabled == 0) streaming on the * sub-device. Failure on stop will remove any resources acquired in * streaming start, while the error code is still returned by the driver. - * Also see call_s_stream wrapper in v4l2-subdev.c. + * The caller shall track the subdev state, and shall not start or stop an + * already started or stopped subdev. Also see call_s_stream wrapper in + * v4l2-subdev.c. * * @g_pixelaspect: callback to return the pixelaspect ratio. *