Re: [PATCH v5 09/10] media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 24/04/2024 16:17, Laurent Pinchart wrote:
On Wed, Apr 24, 2024 at 04:11:36PM +0300, Tomi Valkeinen wrote:
On 24/04/2024 16:09, Laurent Pinchart wrote:
On Wed, Apr 24, 2024 at 04:05:31PM +0300, Tomi Valkeinen wrote:
On 19/04/2024 18:36, Laurent Pinchart wrote:
On Tue, Apr 16, 2024 at 04:55:12PM +0300, Tomi Valkeinen wrote:
At the moment the v4l2_subdev_enable/disable_streams() functions call
fallback helpers to handle the case where the subdev only implements
.s_stream(), and the main function handles the case where the subdev
implements streams (V4L2_SUBDEV_FL_STREAMS, which implies
.enable/disable_streams()).

What is missing is support for subdevs which do not implement streams
support, but do implement .enable/disable_streams(). Example cases of
these subdevices are single-stream cameras, where using
.enable/disable_streams() is not required but helps us remove the users
of the legacy .s_stream(), and subdevices with multiple source pads (but
single stream per pad), where .enable/disable_streams() allows the
subdevice to control the enable/disable state per pad.

The two single-streams cases (.s_stream() and .enable/disable_streams())
are very similar, and with small changes we can change the
v4l2_subdev_enable/disable_streams() functions to support all three
cases, without needing separate fallback functions.

A few potentially problematic details, though:

- For the single-streams cases we use sd->enabled_pads field, which
     limits the number of pads for the subdevice to 64. For simplicity I
     added the check for this limitation to the beginning of the function,
     and it also applies to the streams case.

- The fallback functions only allowed the target pad to be a source pad.
     It is not very clear to me why this check was needed, but it was not
     needed in the streams case. However, I doubt the
     v4l2_subdev_enable/disable_streams() code has ever been tested with
     sink pads, so to be on the safe side, I added the same check
     to the v4l2_subdev_enable/disable_streams() functions.


Now that v4l2_subdev_enable_streams() is usable by (almost) every
driver, should we update documentation to indicate that manual calls to
.s_stream() should be avoided ?

How about:

diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index dabe1b5dfe4a..f52bb790773c 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -450,6 +450,11 @@ enum v4l2_subdev_pre_streamon_flags {
    *     already started or stopped subdev. Also see call_s_stream wrapper in
    *     v4l2-subdev.c.
    *
+ *     New drivers should instead implement &v4l2_subdev_pad_ops.enable_streams
+ *     and &v4l2_subdev_pad_ops.disable_streams operations, and use
+ *     v4l2_subdev_s_stream_helper for the &v4l2_subdev_video_ops.s_stream
+ *     operation to support legacy users.A
+ *

Add

   *	Drivers should also not call the .s_stream() subdev operation directly,
   *	but use the v4l2_subdev_enable_streams() and
   *	v4l2_subdev_disable_streams() helpers.

    * @g_pixelaspect: callback to return the pixelaspect ratio.
    *
    * @s_dv_timings: Set custom dv timings in the sub device. This is used


Although now that I think about it, can we "ever" get rid of s_stream?
enable_streams is only usable if the subdev has pads.

Do we have pad-less subdevs that can stream ?

Probably not, but isn't s_stream used to enable any type of subdev? Or
is there a different op to power on padless subdevs?

.s_stream() is supposed to start/stop streaming, and you can't really
have streams without pads, as they have to come from/go somewhere.

Ok. I was under the impression that .s_stream() is also used to enable non-streaming subdevs when the pipeline's streaming is started. If that's not the case, I'll update the doc.

 Tomi

If that's the
case, the text above should probably clarify when one should continue
using s_stream.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@xxxxxxxxxxxxxxxx>
---
    drivers/media/v4l2-core/v4l2-subdev.c | 187 ++++++++++++++--------------------
    1 file changed, 79 insertions(+), 108 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index e45fd42da1e3..10406acfb9d0 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -2106,6 +2106,13 @@ static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd,
    					u64 *found_streams,
    					u64 *enabled_streams)
    {
+	if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) {
+		*found_streams = BIT_ULL(0);
+		*enabled_streams =
+			(sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0;
+		return;
+	}
+
    	*found_streams = 0;
    	*enabled_streams = 0;
@@ -2127,6 +2134,14 @@ static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd,
    					    u32 pad, u64 streams_mask,
    					    bool enabled)
    {
+	if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) {
+		if (enabled)
+			sd->enabled_pads |= BIT_ULL(pad);
+		else
+			sd->enabled_pads &= ~BIT_ULL(pad);
+		return;
+	}
+
    	for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) {
    		struct v4l2_subdev_stream_config *cfg =
    			&state->stream_configs.configs[i];
@@ -2136,51 +2151,6 @@ static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd,
    	}
    }
-static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
-					       u64 streams_mask)
-{
-	struct device *dev = sd->entity.graph_obj.mdev->dev;
-	int ret;
-
-	/*
-	 * The subdev doesn't implement pad-based stream enable, fall back
-	 * to the .s_stream() operation.
-	 */
-	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
-		return -EOPNOTSUPP;
-
-	/*
-	 * .s_stream() means there is no streams support, so the only allowed
-	 * stream is the implicit stream 0.
-	 */
-	if (streams_mask != BIT_ULL(0))
-		return -EOPNOTSUPP;
-
-	/*
-	 * We use a 64-bit bitmask for tracking enabled pads, so only subdevices
-	 * with 64 pads or less can be supported.
-	 */
-	if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE)
-		return -EOPNOTSUPP;
-
-	if (sd->enabled_pads & BIT_ULL(pad)) {
-		dev_dbg(dev, "pad %u already enabled on %s\n",
-			pad, sd->entity.name);
-		return -EALREADY;
-	}
-
-	/* Start streaming when the first pad is enabled. */
-	if (!sd->enabled_pads) {
-		ret = v4l2_subdev_call(sd, video, s_stream, 1);
-		if (ret)
-			return ret;
-	}
-
-	sd->enabled_pads |= BIT_ULL(pad);
-
-	return 0;
-}
-
    int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
    			       u64 streams_mask)
    {
@@ -2189,21 +2159,33 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
    	bool already_streaming;
    	u64 enabled_streams;
    	u64 found_streams;
+	bool use_s_stream;
    	int ret;
/* A few basic sanity checks first. */
    	if (pad >= sd->entity.num_pads)
    		return -EINVAL;
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+		return -EOPNOTSUPP;
+
+	/*
+	 * We use a 64-bit bitmask for tracking enabled pads, so only subdevices
+	 * with 64 pads or less can be supported.
+	 */
+	if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE)
+		return -EOPNOTSUPP;
+
    	if (!streams_mask)
    		return 0;
/* Fallback on .s_stream() if .enable_streams() isn't available. */
-	if (!v4l2_subdev_has_op(sd, pad, enable_streams))
-		return v4l2_subdev_enable_streams_fallback(sd, pad,
-							   streams_mask);
+	use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams);
- state = v4l2_subdev_lock_and_get_active_state(sd);
+	if (!use_s_stream)
+		state = v4l2_subdev_lock_and_get_active_state(sd);
+	else
+		state = NULL;
/*
    	 * Verify that the requested streams exist and that they are not
@@ -2231,9 +2213,18 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
already_streaming = v4l2_subdev_is_streaming(sd); - /* Call the .enable_streams() operation. */
-	ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
-			       streams_mask);
+	if (!use_s_stream) {
+		/* Call the .enable_streams() operation. */
+		ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
+				       streams_mask);
+	} else {
+		/* Start streaming when the first pad is enabled. */
+		if (!already_streaming)
+			ret = v4l2_subdev_call(sd, video, s_stream, 1);
+		else
+			ret = 0;
+	}
+
    	if (ret) {
    		dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad,
    			streams_mask, ret);
@@ -2243,34 +2234,32 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
    	/* Mark the streams as enabled. */
    	v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true);

state gets set to a non-null value above if the subdev has an
enable_streams operation, while in v4l2_subdev_set_streams_enabled(),

That's not quite true, v4l2_subdev_lock_and_get_active_state() returns
NULL if there's no state. If the driver has .enable_streams but no
subdev-state, we'll have NULL state. But is that a driver bug? Probably,
although I'm not quite sure. Whereas V4L2_SUBDEV_FL_STREAMS without
state is a driver bug.

you use it if the V4L2_SUBDEV_FL_STREAMS flag is set. The second
condition is a subset of the first one, so it should be safe now, but I
wonder if we're at risk of crashes in the future when reworking the
code. I'm probably worrying needlessly.

I agree that this is a bit messy. We have multiple different scenarios
which we handle in the same functions, and it's somewhat unclear what
op/state/flag combinations the drivers are allowed to have and what not.

I could add a !state check here, and then use sd->enabled_pads. But that
doesn't feel correct, and I think it's just better to crash and then fix
it if we end up there.

- if (!already_streaming)
+	if (!use_s_stream && !already_streaming)
    		v4l2_subdev_enable_privacy_led(sd);

Once everybody switches to v4l2_subdev_enable_streams() (am I dreaming?)
we should move LED handling from the .s_stream() wrapper to here for the
fallback case too. How about a TODO comment ?

Let's get back to this after we figure out if s_stream can ever be removed.

done:
-	v4l2_subdev_unlock_state(state);
+	if (!use_s_stream)

	if (state)

would be better I think.

We get the state and state lock if (!use_s_stream), so I think it makes
sense to use the same condition when unlocking to be symmetric. I'm sure
I can be convinced to use if (state) too, though =).

+		v4l2_subdev_unlock_state(state);
return ret;
    }
    EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams);
-static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
-						u64 streams_mask)
+int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
+				u64 streams_mask)
    {
    	struct device *dev = sd->entity.graph_obj.mdev->dev;
+	struct v4l2_subdev_state *state;
+	u64 enabled_streams;
+	u64 found_streams;
+	bool use_s_stream;
    	int ret;
- /*
-	 * If the subdev doesn't implement pad-based stream enable, fall back
-	 * to the .s_stream() operation.
-	 */
-	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
-		return -EOPNOTSUPP;
+	/* A few basic sanity checks first. */
+	if (pad >= sd->entity.num_pads)
+		return -EINVAL;
- /*
-	 * .s_stream() means there is no streams support, so the only allowed
-	 * stream is the implicit stream 0.
-	 */
-	if (streams_mask != BIT_ULL(0))
+	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
    		return -EOPNOTSUPP;
/*
@@ -2280,46 +2269,16 @@ static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
    	if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE)
    		return -EOPNOTSUPP;
- if (!(sd->enabled_pads & BIT_ULL(pad))) {
-		dev_dbg(dev, "pad %u already disabled on %s\n",
-			pad, sd->entity.name);
-		return -EALREADY;
-	}
-
-	/* Stop streaming when the last streams are disabled. */
-	if (!(sd->enabled_pads & ~BIT_ULL(pad))) {
-		ret = v4l2_subdev_call(sd, video, s_stream, 0);
-		if (ret)
-			return ret;
-	}
-
-	sd->enabled_pads &= ~BIT_ULL(pad);
-
-	return 0;
-}
-
-int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
-				u64 streams_mask)
-{
-	struct device *dev = sd->entity.graph_obj.mdev->dev;
-	struct v4l2_subdev_state *state;
-	u64 enabled_streams;
-	u64 found_streams;
-	int ret;
-
-	/* A few basic sanity checks first. */
-	if (pad >= sd->entity.num_pads)
-		return -EINVAL;
-
    	if (!streams_mask)
    		return 0;
/* Fallback on .s_stream() if .disable_streams() isn't available. */
-	if (!v4l2_subdev_has_op(sd, pad, disable_streams))
-		return v4l2_subdev_disable_streams_fallback(sd, pad,
-							    streams_mask);
+	use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams);
- state = v4l2_subdev_lock_and_get_active_state(sd);
+	if (!use_s_stream)
+		state = v4l2_subdev_lock_and_get_active_state(sd);
+	else
+		state = NULL;
/*
    	 * Verify that the requested streams exist and that they are not
@@ -2345,9 +2304,19 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); - /* Call the .disable_streams() operation. */
-	ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad,
-			       streams_mask);
+	if (!use_s_stream) {
+		/* Call the .disable_streams() operation. */
+		ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad,
+				       streams_mask);
+	} else {
+		/* Stop streaming when the last streams are disabled. */
+
+		if (!(sd->enabled_pads & ~BIT_ULL(pad)))
+			ret = v4l2_subdev_call(sd, video, s_stream, 0);
+		else
+			ret = 0;
+	}
+
    	if (ret) {
    		dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad,
    			streams_mask, ret);
@@ -2357,10 +2326,12 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
    	v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false);
done:
-	if (!v4l2_subdev_is_streaming(sd))
-		v4l2_subdev_disable_privacy_led(sd);
+	if (!use_s_stream) {
+		if (!v4l2_subdev_is_streaming(sd))
+			v4l2_subdev_disable_privacy_led(sd);

Do we want to disable the privacy LED on failure in all cases, in
particular when the .s_stream() or .disable_streams() operations are not
even called ?

And now that I wrote that, I realize the !v4l2_subdev_is_streaming()
condition will not be true if the operations failed, so it should be
fine.

Reviewed-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>

- v4l2_subdev_unlock_state(state);
+		v4l2_subdev_unlock_state(state);
+	}
return ret;
    }







[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux