mbox series

[v3,0/9] media: subdev: Improve stream enable/disable machinery

Message ID 20240410-enable-streams-impro-v3-0-e5e7a5da7420@ideasonboard.com
Headers show
Series media: subdev: Improve stream enable/disable machinery | expand

Message

Tomi Valkeinen April 10, 2024, 12:35 p.m. UTC
This series works on the .s_stream, .enable_streams, .disable_streams
related code. The main feature introduced here, in the last patch, is
adding support for .enable/disable_streams() for subdevs that do not
implement full streams support.

Additionally we add support for the privacy led when
v4l2_subdev_enable/disable_streams() is used.

I have tested this on RPi5.

 Tomi

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
Changes in v3:
- Rebased on top of "[PATCH v2 1/1] media: v4l: Don't turn on privacy LED if streamon fails"
- Drop extra "!!" in "media: subdev: Fix use of sd->enabled_streams in  call_s_stream()"
- Enable privacy LED after a succesfull stream enable in  "media: subdev: Support privacy led in v4l2_subdev_enable/disable_streams()"
- Init 'cfg' variable when declaring in "media: subdev: Refactor v4l2_subdev_enable/disable_streams()"
- Link to v2: https://lore.kernel.org/r/20240405-enable-streams-impro-v2-0-22bca967665d@ideasonboard.com

Changes in v2:
- New patches for privacy led
- Use v4l2_subdev_has_op()
- Use BITS_PER_BYTE instead of 8
- Fix 80+ line issues
- Fix typos
- Check for pad < 64 also in the non-routing .enable/disable_streams code path
- Dropped "media: subdev: Support enable/disable_streams for non-streams
  subdevs", which is implemented in a different way in new patches in this series
- Link to v1: https://lore.kernel.org/r/20240404-enable-streams-impro-v1-0-1017a35bbe07@ideasonboard.com

---
Tomi Valkeinen (9):
      media: subdev: Add privacy led helpers
      media: subdev: Use v4l2_subdev_has_op() in v4l2_subdev_enable/disable_streams()
      media: subdev: Add checks for subdev features
      media: subdev: Fix use of sd->enabled_streams in call_s_stream()
      media: subdev: Improve v4l2_subdev_enable/disable_streams_fallback
      media: subdev: Add v4l2_subdev_is_streaming()
      media: subdev: Support privacy led in v4l2_subdev_enable/disable_streams()
      media: subdev: Refactor v4l2_subdev_enable/disable_streams()
      media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams()

 drivers/media/v4l2-core/v4l2-subdev.c | 355 ++++++++++++++++++++--------------
 include/media/v4l2-subdev.h           |  25 ++-
 2 files changed, 229 insertions(+), 151 deletions(-)
---
base-commit: 6547a87b1ffc9b3c3a17f20f71016de98c169bbb
change-id: 20240404-enable-streams-impro-db8bcd898471

Best regards,

Comments

Umang Jain April 11, 2024, 4:43 a.m. UTC | #1
Hi Tomi,

Thank you for the patch.

On 10/04/24 6:05 pm, Tomi Valkeinen wrote:
> Add helper functions to enable and disable the privacy led. This moves
> the #if from the call site to the privacy led functions, and makes
> adding privacy led support to v4l2_subdev_enable/disable_streams()
> cleaner.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>

Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>

> ---
>   drivers/media/v4l2-core/v4l2-subdev.c | 30 +++++++++++++++++++++---------
>   1 file changed, 21 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 012b757eac9f..13957543d153 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -148,6 +148,23 @@ static int subdev_close(struct file *file)
>   }
>   #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
>   
> +static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd)
> +{
> +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> +	if (!IS_ERR_OR_NULL(sd->privacy_led))
> +		led_set_brightness(sd->privacy_led,
> +				   sd->privacy_led->max_brightness);
> +#endif
> +}
> +
> +static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd)
> +{
> +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> +	if (!IS_ERR_OR_NULL(sd->privacy_led))
> +		led_set_brightness(sd->privacy_led, 0);
> +#endif
> +}
> +
>   static inline int check_which(u32 which)
>   {
>   	if (which != V4L2_SUBDEV_FORMAT_TRY &&
> @@ -422,15 +439,10 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
>   	if (!ret) {
>   		sd->enabled_streams = enable ? BIT(0) : 0;
>   
> -#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> -		if (!IS_ERR_OR_NULL(sd->privacy_led)) {
> -			if (enable)
> -				led_set_brightness(sd->privacy_led,
> -						   sd->privacy_led->max_brightness);
> -			else
> -				led_set_brightness(sd->privacy_led, 0);
> -		}
> -#endif
> +		if (enable)
> +			v4l2_subdev_enable_privacy_led(sd);
> +		else
> +			v4l2_subdev_disable_privacy_led(sd);
>   	}
>   
>   	return ret;
>
Umang Jain April 11, 2024, 4:58 a.m. UTC | #2
Hi Tomi,

Thank you for the patch.

On 10/04/24 6:05 pm, Tomi Valkeinen wrote:
> We support camera privacy leds with the .s_stream, in call_s_stream, but
> we don't have that support when the subdevice implements
> .enable/disable_streams.
>
> Add the support by enabling the led when the first stream for a
> subdevice is enabled, and disabling the led then the last stream is
> disabled.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>

Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>

> ---
>   drivers/media/v4l2-core/v4l2-subdev.c | 9 +++++++++
>   1 file changed, 9 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 20b5a00cbeeb..f44aaa4e1fab 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -2150,6 +2150,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
>   {
>   	struct device *dev = sd->entity.graph_obj.mdev->dev;
>   	struct v4l2_subdev_state *state;
> +	bool already_streaming;
>   	u64 found_streams = 0;
>   	unsigned int i;
>   	int ret;
> @@ -2198,6 +2199,8 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
>   
>   	dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask);
>   
> +	already_streaming = v4l2_subdev_is_streaming(sd);
> +
>   	/* Call the .enable_streams() operation. */
>   	ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
>   			       streams_mask);
> @@ -2216,6 +2219,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
>   			cfg->enabled = true;
>   	}
>   
> +	if (!already_streaming)
> +		v4l2_subdev_enable_privacy_led(sd);
> +
>   done:
>   	v4l2_subdev_unlock_state(state);
>   
> @@ -2340,6 +2346,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
>   	}
>   
>   done:
> +	if (!v4l2_subdev_is_streaming(sd))
> +		v4l2_subdev_disable_privacy_led(sd);
> +
>   	v4l2_subdev_unlock_state(state);
>   
>   	return ret;
>
Umang Jain April 11, 2024, 5:34 a.m. UTC | #3
Hi Tomi,

On 10/04/24 6:05 pm, Tomi Valkeinen wrote:
> Add some checks to verify that the subdev driver implements required
> features.
>
> A subdevice that supports streams (V4L2_SUBDEV_FL_STREAMS) must do one
> of the following:
>
> - Implement neither .enable/disable_streams() nor .s_stream(), if the
>    subdev is part of a video driver that uses an internal method to
>    enable the subdev.
> - Implement only .enable/disable_streams(), if support for legacy
>    sink-side subdevices is not needed.
> - Implement .enable/disable_streams() and .s_stream(), if support for
>    legacy sink-side subdevices is needed.
>
> At the moment the framework doesn't check this requirement. Add the
> check.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>

The code looks aligned with the restrictions mentioned in the commit 
message.

Only one question in case 3), does the .s_stream() needs to be helper 
function I think, can we (or do we) need to check that as well?

Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>

> ---
>   drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++++++++++
>   1 file changed, 27 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 4a531c2b16c4..606a909cd778 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -1533,6 +1533,33 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name,
>   				struct lock_class_key *key)
>   {
>   	struct v4l2_subdev_state *state;
> +	struct device *dev = sd->dev;
> +	bool has_disable_streams;
> +	bool has_enable_streams;
> +	bool has_s_stream;
> +
> +	/* Check that the subdevice implements the required features */
> +
> +	has_s_stream = v4l2_subdev_has_op(sd, video, s_stream);
> +	has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams);
> +	has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams);
> +
> +	if (has_enable_streams != has_disable_streams) {
> +		dev_err(dev,
> +			"subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n",
> +			sd->name);
> +		return -EINVAL;
> +	}
> +
> +	if (sd->flags & V4L2_SUBDEV_FL_STREAMS) {
> +		if (has_s_stream && !has_enable_streams) {
> +			dev_err(dev,
> +				"subdev '%s' must implement .enable/disable_streams()\n",
> +				sd->name);
> +
> +			return -EINVAL;
> +		}
> +	}
>   
>   	state = __v4l2_subdev_state_alloc(sd, name, key);
>   	if (IS_ERR(state))
>
Tomi Valkeinen April 11, 2024, 6:18 a.m. UTC | #4
On 11/04/2024 08:34, Umang Jain wrote:
> Hi Tomi,
> 
> On 10/04/24 6:05 pm, Tomi Valkeinen wrote:
>> Add some checks to verify that the subdev driver implements required
>> features.
>>
>> A subdevice that supports streams (V4L2_SUBDEV_FL_STREAMS) must do one
>> of the following:
>>
>> - Implement neither .enable/disable_streams() nor .s_stream(), if the
>>    subdev is part of a video driver that uses an internal method to
>>    enable the subdev.
>> - Implement only .enable/disable_streams(), if support for legacy
>>    sink-side subdevices is not needed.
>> - Implement .enable/disable_streams() and .s_stream(), if support for
>>    legacy sink-side subdevices is needed.
>>
>> At the moment the framework doesn't check this requirement. Add the
>> check.
>>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> 
> The code looks aligned with the restrictions mentioned in the commit 
> message.
> 
> Only one question in case 3), does the .s_stream() needs to be helper 
> function I think, can we (or do we) need to check that as well?

Do you mean if in case 3, the s_stream should always be set to 
v4l2_subdev_s_stream_helper()?

I don't think so. The helper only works for subdevices with a single 
source pad. And even if the helper worked for multiple source pads, I 
don't see any specific reason to prevent the drivers from having their 
own implementation.

  Tomi

> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> 
>> ---
>>   drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++++++++++
>>   1 file changed, 27 insertions(+)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c 
>> b/drivers/media/v4l2-core/v4l2-subdev.c
>> index 4a531c2b16c4..606a909cd778 100644
>> --- a/drivers/media/v4l2-core/v4l2-subdev.c
>> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
>> @@ -1533,6 +1533,33 @@ int __v4l2_subdev_init_finalize(struct 
>> v4l2_subdev *sd, const char *name,
>>                   struct lock_class_key *key)
>>   {
>>       struct v4l2_subdev_state *state;
>> +    struct device *dev = sd->dev;
>> +    bool has_disable_streams;
>> +    bool has_enable_streams;
>> +    bool has_s_stream;
>> +
>> +    /* Check that the subdevice implements the required features */
>> +
>> +    has_s_stream = v4l2_subdev_has_op(sd, video, s_stream);
>> +    has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams);
>> +    has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams);
>> +
>> +    if (has_enable_streams != has_disable_streams) {
>> +        dev_err(dev,
>> +            "subdev '%s' must implement both or neither of 
>> .enable_streams() and .disable_streams()\n",
>> +            sd->name);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (sd->flags & V4L2_SUBDEV_FL_STREAMS) {
>> +        if (has_s_stream && !has_enable_streams) {
>> +            dev_err(dev,
>> +                "subdev '%s' must implement 
>> .enable/disable_streams()\n",
>> +                sd->name);
>> +
>> +            return -EINVAL;
>> +        }
>> +    }
>>       state = __v4l2_subdev_state_alloc(sd, name, key);
>>       if (IS_ERR(state))
>>
>
Umang Jain April 11, 2024, 11:02 a.m. UTC | #5
Hi Tomi,

On 10/04/24 6:05 pm, 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:

Does this mean the patch needs to be worked upon more ?

I quickly tested the series by applying it locally with my use case of 
IMX283 .enable/disable streams and s_stream as the helper function and 
it seems I am still seeing the same behaviour as before (i.e. not being 
streamed) and have to carry the workaround as mentioned in [1] **NOTE**

[1] 
https://lore.kernel.org/linux-media/20240313070705.91140-1-umang.jain@ideasonboard.com/

>
> - 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.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
>   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 0d376d72ecc7..4a73886741f9 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);
> +		if (sd->enabled_pads & BIT_ULL(pad))
> +			*enabled_streams = BIT_ULL(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 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);
>   
> -	if (!already_streaming)
> +	if (!use_s_stream && !already_streaming)
>   		v4l2_subdev_enable_privacy_led(sd);
>   
>   done:
> -	v4l2_subdev_unlock_state(state);
> +	if (!use_s_stream)
> +		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 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);
>   
> -	v4l2_subdev_unlock_state(state);
> +		v4l2_subdev_unlock_state(state);
> +	}
>   
>   	return ret;
>   }
>
Tomi Valkeinen April 11, 2024, 11:07 a.m. UTC | #6
On 11/04/2024 14:02, Umang Jain wrote:
> Hi Tomi,
> 
> On 10/04/24 6:05 pm, 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:
> 
> Does this mean the patch needs to be worked upon more ?

I don't see the two issues below as blockers.

> I quickly tested the series by applying it locally with my use case of 
> IMX283 .enable/disable streams and s_stream as the helper function and 
> it seems I am still seeing the same behaviour as before (i.e. not being 
> streamed) and have to carry the workaround as mentioned in [1] **NOTE**

Ok... Then something bugs here, as it is supposed to fix the problem. 
Can you trace the code a bit to see where it goes wrong?

The execution should go to the "if (!(sd->flags & 
V4L2_SUBDEV_FL_STREAMS))" blocks in v4l2_subdev_collect_streams() and 
v4l2_subdev_set_streams_enabled(),

  Tomi

> 
> [1] 
> https://lore.kernel.org/linux-media/20240313070705.91140-1-umang.jain@ideasonboard.com/
> 
>>
>> - 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.
>>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>> ---
>>   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 0d376d72ecc7..4a73886741f9 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);
>> +        if (sd->enabled_pads & BIT_ULL(pad))
>> +            *enabled_streams = BIT_ULL(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 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);
>> -    if (!already_streaming)
>> +    if (!use_s_stream && !already_streaming)
>>           v4l2_subdev_enable_privacy_led(sd);
>>   done:
>> -    v4l2_subdev_unlock_state(state);
>> +    if (!use_s_stream)
>> +        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 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);
>> -    v4l2_subdev_unlock_state(state);
>> +        v4l2_subdev_unlock_state(state);
>> +    }
>>       return ret;
>>   }
>>
>
Umang Jain April 11, 2024, 11:48 a.m. UTC | #7
Hi Tomi,

On 11/04/24 4:37 pm, Tomi Valkeinen wrote:
> On 11/04/2024 14:02, Umang Jain wrote:
>> Hi Tomi,
>>
>> On 10/04/24 6:05 pm, 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:
>>
>> Does this mean the patch needs to be worked upon more ?
>
> I don't see the two issues below as blockers.
>
>> I quickly tested the series by applying it locally with my use case 
>> of IMX283 .enable/disable streams and s_stream as the helper function 
>> and it seems I am still seeing the same behaviour as before (i.e. not 
>> being streamed) and have to carry the workaround as mentioned in [1] 
>> **NOTE**
>
> Ok... Then something bugs here, as it is supposed to fix the problem. 
> Can you trace the code a bit to see where it goes wrong?
>
> The execution should go to the "if (!(sd->flags & 
> V4L2_SUBDEV_FL_STREAMS))" blocks in v4l2_subdev_collect_streams() and 
> v4l2_subdev_set_streams_enabled(),

The execution is not reaching in v4l2_subdev_collect streams() even, it 
returns at

     if (!streams_mask)
                 return 0;

in v4l2_subdev_enable_streams()

Refer to : https://paste.debian.net/1313760/

My tree is based on v6.8 currently, but the series applies cleanly, so I 
have not introduced any  rebase artifacts. If you think, v6.8 might be 
causing issues, I'll then try to test on RPi 5 with the latest media 
tree perhaps.

>
>  Tomi
>
>>
>> [1] 
>> https://lore.kernel.org/linux-media/20240313070705.91140-1-umang.jain@ideasonboard.com/
>>
>>>
>>> - 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.
>>>
>>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>>> ---
>>>   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 0d376d72ecc7..4a73886741f9 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);
>>> +        if (sd->enabled_pads & BIT_ULL(pad))
>>> +            *enabled_streams = BIT_ULL(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 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);
>>> -    if (!already_streaming)
>>> +    if (!use_s_stream && !already_streaming)
>>>           v4l2_subdev_enable_privacy_led(sd);
>>>   done:
>>> -    v4l2_subdev_unlock_state(state);
>>> +    if (!use_s_stream)
>>> +        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 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);
>>> -    v4l2_subdev_unlock_state(state);
>>> +        v4l2_subdev_unlock_state(state);
>>> +    }
>>>       return ret;
>>>   }
>>>
>>
>
Tomi Valkeinen April 11, 2024, 11:53 a.m. UTC | #8
On 11/04/2024 14:48, Umang Jain wrote:
> Hi Tomi,
> 
> On 11/04/24 4:37 pm, Tomi Valkeinen wrote:
>> On 11/04/2024 14:02, Umang Jain wrote:
>>> Hi Tomi,
>>>
>>> On 10/04/24 6:05 pm, 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:
>>>
>>> Does this mean the patch needs to be worked upon more ?
>>
>> I don't see the two issues below as blockers.
>>
>>> I quickly tested the series by applying it locally with my use case 
>>> of IMX283 .enable/disable streams and s_stream as the helper function 
>>> and it seems I am still seeing the same behaviour as before (i.e. not 
>>> being streamed) and have to carry the workaround as mentioned in [1] 
>>> **NOTE**
>>
>> Ok... Then something bugs here, as it is supposed to fix the problem. 
>> Can you trace the code a bit to see where it goes wrong?
>>
>> The execution should go to the "if (!(sd->flags & 
>> V4L2_SUBDEV_FL_STREAMS))" blocks in v4l2_subdev_collect_streams() and 
>> v4l2_subdev_set_streams_enabled(),
> 
> The execution is not reaching in v4l2_subdev_collect streams() even, it 
> returns at
> 
>      if (!streams_mask)
>                  return 0;
> 
> in v4l2_subdev_enable_streams()
> 
> Refer to : https://paste.debian.net/1313760/
> 
> My tree is based on v6.8 currently, but the series applies cleanly, so I 
> have not introduced any  rebase artifacts. If you think, v6.8 might be 
> causing issues, I'll then try to test on RPi 5 with the latest media 
> tree perhaps.

So who is calling the v4l2_subdev_enable_streams? I presume it comes 
from v4l2_subdev_s_stream_helper(), in other words the sink side in your 
pipeline is using legacy s_stream?

Indeed, that helper still needs work. It needs to detect if there's no 
routing, and use the implicit stream 0. I missed that one.

  Tomi
Umang Jain April 11, 2024, 11:56 a.m. UTC | #9
Hi Tomi

On 11/04/24 5:23 pm, Tomi Valkeinen wrote:
> On 11/04/2024 14:48, Umang Jain wrote:
>> Hi Tomi,
>>
>> On 11/04/24 4:37 pm, Tomi Valkeinen wrote:
>>> On 11/04/2024 14:02, Umang Jain wrote:
>>>> Hi Tomi,
>>>>
>>>> On 10/04/24 6:05 pm, 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:
>>>>
>>>> Does this mean the patch needs to be worked upon more ?
>>>
>>> I don't see the two issues below as blockers.
>>>
>>>> I quickly tested the series by applying it locally with my use case 
>>>> of IMX283 .enable/disable streams and s_stream as the helper 
>>>> function and it seems I am still seeing the same behaviour as 
>>>> before (i.e. not being streamed) and have to carry the workaround 
>>>> as mentioned in [1] **NOTE**
>>>
>>> Ok... Then something bugs here, as it is supposed to fix the 
>>> problem. Can you trace the code a bit to see where it goes wrong?
>>>
>>> The execution should go to the "if (!(sd->flags & 
>>> V4L2_SUBDEV_FL_STREAMS))" blocks in v4l2_subdev_collect_streams() 
>>> and v4l2_subdev_set_streams_enabled(),
>>
>> The execution is not reaching in v4l2_subdev_collect streams() even, 
>> it returns at
>>
>>      if (!streams_mask)
>>                  return 0;
>>
>> in v4l2_subdev_enable_streams()
>>
>> Refer to : https://paste.debian.net/1313760/
>>
>> My tree is based on v6.8 currently, but the series applies cleanly, 
>> so I have not introduced any  rebase artifacts. If you think, v6.8 
>> might be causing issues, I'll then try to test on RPi 5 with the 
>> latest media tree perhaps.
>
> So who is calling the v4l2_subdev_enable_streams? I presume it comes 
> from v4l2_subdev_s_stream_helper(), in other words the sink side in 
> your pipeline is using legacy s_stream?

Yes it comes from the helper function

static const struct v4l2_subdev_video_ops imx283_video_ops = {
         .s_stream = v4l2_subdev_s_stream_helper,
};

>
> Indeed, that helper still needs work. It needs to detect if there's no 
> routing, and use the implicit stream 0. I missed that one.

Yes, no routing in the driver.
>
>  Tomi
>
Laurent Pinchart April 12, 2024, 5:36 p.m. UTC | #10
Hi Tomi,

Thank you for the patch.

On Wed, Apr 10, 2024 at 03:35:48PM +0300, Tomi Valkeinen wrote:
> Add helper functions to enable and disable the privacy led. This moves
> the #if from the call site to the privacy led functions, and makes
> adding privacy led support to v4l2_subdev_enable/disable_streams()
> cleaner.
> 
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
>  drivers/media/v4l2-core/v4l2-subdev.c | 30 +++++++++++++++++++++---------
>  1 file changed, 21 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 012b757eac9f..13957543d153 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -148,6 +148,23 @@ static int subdev_close(struct file *file)
>  }
>  #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
>  
> +static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd)
> +{
> +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> +	if (!IS_ERR_OR_NULL(sd->privacy_led))
> +		led_set_brightness(sd->privacy_led,
> +				   sd->privacy_led->max_brightness);
> +#endif
> +}
> +
> +static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd)
> +{
> +#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> +	if (!IS_ERR_OR_NULL(sd->privacy_led))
> +		led_set_brightness(sd->privacy_led, 0);
> +#endif
> +}

I would have written this as

#if IS_REACHABLE(CONFIG_LEDS_CLASS)
static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd)
{
	if (!IS_ERR_OR_NULL(sd->privacy_led))
		led_set_brightness(sd->privacy_led,
				   sd->privacy_led->max_brightness);
}

static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd)
{
	if (!IS_ERR_OR_NULL(sd->privacy_led))
		led_set_brightness(sd->privacy_led, 0);
}
#else
static inline void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd)
{
}

static inline void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd)
{
}
#endif /* CONFIG_LEDS_CLASS */

to avoid multipe #if but that likely makes no difference in the
generated code. Either way,

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

> +
>  static inline int check_which(u32 which)
>  {
>  	if (which != V4L2_SUBDEV_FORMAT_TRY &&
> @@ -422,15 +439,10 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
>  	if (!ret) {
>  		sd->enabled_streams = enable ? BIT(0) : 0;
>  
> -#if IS_REACHABLE(CONFIG_LEDS_CLASS)
> -		if (!IS_ERR_OR_NULL(sd->privacy_led)) {
> -			if (enable)
> -				led_set_brightness(sd->privacy_led,
> -						   sd->privacy_led->max_brightness);
> -			else
> -				led_set_brightness(sd->privacy_led, 0);
> -		}
> -#endif
> +		if (enable)
> +			v4l2_subdev_enable_privacy_led(sd);
> +		else
> +			v4l2_subdev_disable_privacy_led(sd);
>  	}
>  
>  	return ret;
>
Laurent Pinchart April 12, 2024, 5:41 p.m. UTC | #11
Hi Tomi,

Thank you for the patch.

On Wed, Apr 10, 2024 at 03:35:50PM +0300, Tomi Valkeinen wrote:
> Add some checks to verify that the subdev driver implements required
> features.
> 
> A subdevice that supports streams (V4L2_SUBDEV_FL_STREAMS) must do one
> of the following:
> 
> - Implement neither .enable/disable_streams() nor .s_stream(), if the
>   subdev is part of a video driver that uses an internal method to
>   enable the subdev.
> - Implement only .enable/disable_streams(), if support for legacy
>   sink-side subdevices is not needed.
> - Implement .enable/disable_streams() and .s_stream(), if support for
>   legacy sink-side subdevices is needed.
> 
> At the moment the framework doesn't check this requirement. Add the
> check.
> 
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>

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

> ---
>  drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 4a531c2b16c4..606a909cd778 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -1533,6 +1533,33 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name,
>  				struct lock_class_key *key)
>  {
>  	struct v4l2_subdev_state *state;
> +	struct device *dev = sd->dev;
> +	bool has_disable_streams;
> +	bool has_enable_streams;
> +	bool has_s_stream;
> +
> +	/* Check that the subdevice implements the required features */
> +
> +	has_s_stream = v4l2_subdev_has_op(sd, video, s_stream);
> +	has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams);
> +	has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams);
> +
> +	if (has_enable_streams != has_disable_streams) {
> +		dev_err(dev,
> +			"subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n",
> +			sd->name);
> +		return -EINVAL;
> +	}
> +
> +	if (sd->flags & V4L2_SUBDEV_FL_STREAMS) {
> +		if (has_s_stream && !has_enable_streams) {
> +			dev_err(dev,
> +				"subdev '%s' must implement .enable/disable_streams()\n",
> +				sd->name);
> +
> +			return -EINVAL;
> +		}
> +	}
>  
>  	state = __v4l2_subdev_state_alloc(sd, name, key);
>  	if (IS_ERR(state))
>
Laurent Pinchart April 12, 2024, 6:13 p.m. UTC | #12
Hi Tomi,

Thank you for the patch.

On Wed, Apr 10, 2024 at 03:35:52PM +0300, Tomi Valkeinen wrote:
> v4l2_subdev_enable/disable_streams_fallback() supports falling back to
> .s_stream() for subdevs with a single source pad. It also tracks the
> enabled streams for that one pad in the sd->enabled_streams field.
> 
> Tracking the enabled streams with sd->enabled_streams does not make
> sense, as with .s_stream() there can only be a single stream per pad.
> Thus, as the v4l2_subdev_enable/disable_streams_fallback() only supports
> a single source pad, all we really need is a boolean which tells whether
> streaming has been enabled on this pad or not.
> 
> However, as we only need a true/false state for a pad (instead of
> tracking which streams have been enabled for a pad), we can easily
> extend the fallback mechanism to support multiple source pads as we only
> need to keep track of which pads have been enabled.
> 
> Change the sd->enabled_streams field to sd->enabled_pads, which is a
> 64-bit bitmask tracking the enabled source pads. With this change we can
> remove the restriction that
> v4l2_subdev_enable/disable_streams_fallback() only supports a single
> source pad.
> 
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
>  drivers/media/v4l2-core/v4l2-subdev.c | 68 ++++++++++++++++++++---------------
>  include/media/v4l2-subdev.h           |  9 +++--
>  2 files changed, 44 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 6cd353d83dfc..3d2c9c224b8f 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -2104,37 +2104,43 @@ 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;
> -	unsigned int i;
>  	int ret;
>  
>  	/*
>  	 * The subdev doesn't implement pad-based stream enable, fall back
> -	 * on the .s_stream() operation. This can only be done for subdevs that
> -	 * have a single source pad, as sd->enabled_streams is global to the
> -	 * subdev.
> +	 * to the .s_stream() operation.
>  	 */
>  	if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
>  		return -EOPNOTSUPP;
>  
> -	for (i = 0; i < sd->entity.num_pads; ++i) {
> -		if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
> -			return -EOPNOTSUPP;
> -	}
> +	/*
> +	 * .s_stream() means there is no streams support, so only allowed stream

s/only/the only/

> +	 * 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_streams & streams_mask) {
> -		dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n",
> -			streams_mask, sd->entity.name, pad);
> +	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 streams are enabled. */
> -	if (!sd->enabled_streams) {
> +	/* 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_streams |= streams_mask;
> +	sd->enabled_pads |= BIT_ULL(pad);
>  
>  	return 0;
>  }
> @@ -2221,37 +2227,43 @@ static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
>  						u64 streams_mask)
>  {
>  	struct device *dev = sd->entity.graph_obj.mdev->dev;
> -	unsigned int i;
>  	int ret;
>  
>  	/*
> -	 * If the subdev doesn't implement pad-based stream enable, fall  back
> -	 * on the .s_stream() operation. This can only be done for subdevs that
> -	 * have a single source pad, as sd->enabled_streams is global to the
> -	 * subdev.
> +	 * 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;
>  
> -	for (i = 0; i < sd->entity.num_pads; ++i) {
> -		if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
> -			return -EOPNOTSUPP;
> -	}
> +	/*
> +	 * .s_stream() means there is no streams support, so only allowed stream

Same here.

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

> +	 * 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_streams & streams_mask) != streams_mask) {
> -		dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n",
> -			streams_mask, sd->entity.name, pad);
> +	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_streams & ~streams_mask)) {
> +	if (!(sd->enabled_pads & ~BIT_ULL(pad))) {
>  		ret = v4l2_subdev_call(sd, video, s_stream, 0);
>  		if (ret)
>  			return ret;
>  	}
>  
> -	sd->enabled_streams &= ~streams_mask;
> +	sd->enabled_pads &= ~BIT_ULL(pad);
>  
>  	return 0;
>  }
> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
> index f55d03e0acc1..d6867511e9cf 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -1039,10 +1039,9 @@ struct v4l2_subdev_platform_data {
>   * @active_state: Active state for the subdev (NULL for subdevs tracking the
>   *		  state internally). Initialized by calling
>   *		  v4l2_subdev_init_finalize().
> - * @enabled_streams: Bitmask of enabled streams used by
> - *		     v4l2_subdev_enable_streams() and
> - *		     v4l2_subdev_disable_streams() helper functions for fallback
> - *		     cases.
> + * @enabled_pads: Bitmask of enabled pads used by v4l2_subdev_enable_streams()
> + *		  and v4l2_subdev_disable_streams() helper functions for
> + *		  fallback cases.
>   * @streaming_enabled: Tracks whether streaming has been enabled with s_stream.
>   *                     This is only for call_s_stream() internal use.
>   *
> @@ -1092,7 +1091,7 @@ struct v4l2_subdev {
>  	 * doesn't support it.
>  	 */
>  	struct v4l2_subdev_state *active_state;
> -	u64 enabled_streams;
> +	u64 enabled_pads;
>  	bool streaming_enabled;
>  };
>  
>
Laurent Pinchart April 12, 2024, 6:20 p.m. UTC | #13
Hi Tomi,

Thank you for the patch.

On Wed, Apr 10, 2024 at 03:35:54PM +0300, Tomi Valkeinen wrote:
> We support camera privacy leds with the .s_stream, in call_s_stream, but

s/the .s_stream/the .s_stream() operation/

> we don't have that support when the subdevice implements
> .enable/disable_streams.
> 
> Add the support by enabling the led when the first stream for a
> subdevice is enabled, and disabling the led then the last stream is
> disabled.

I wonder if that will always be the correct constraint for all devices,
but I suppose we can worry about it later.

> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
>  drivers/media/v4l2-core/v4l2-subdev.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> index 20b5a00cbeeb..f44aaa4e1fab 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -2150,6 +2150,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
>  {
>  	struct device *dev = sd->entity.graph_obj.mdev->dev;
>  	struct v4l2_subdev_state *state;
> +	bool already_streaming;
>  	u64 found_streams = 0;
>  	unsigned int i;
>  	int ret;
> @@ -2198,6 +2199,8 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
>  
>  	dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask);
>  
> +	already_streaming = v4l2_subdev_is_streaming(sd);
> +
>  	/* Call the .enable_streams() operation. */
>  	ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
>  			       streams_mask);
> @@ -2216,6 +2219,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
>  			cfg->enabled = true;
>  	}
>  
> +	if (!already_streaming)
> +		v4l2_subdev_enable_privacy_led(sd);
> +
>  done:
>  	v4l2_subdev_unlock_state(state);
>  
> @@ -2340,6 +2346,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
>  	}
>  
>  done:
> +	if (!v4l2_subdev_is_streaming(sd))

Wouldn't it be more efficient to check this while looping over the
stream configs in the loop just above ? Same for
v4l2_subdev_enable_streams().

> +		v4l2_subdev_disable_privacy_led(sd);
> +
>  	v4l2_subdev_unlock_state(state);
>  
>  	return ret;
>
Laurent Pinchart April 19, 2024, 10:22 a.m. UTC | #14
On Tue, Apr 16, 2024 at 01:34:22PM +0300, Tomi Valkeinen wrote:
> On 12/04/2024 21:20, Laurent Pinchart wrote:
> > Hi Tomi,
> > 
> > Thank you for the patch.
> > 
> > On Wed, Apr 10, 2024 at 03:35:54PM +0300, Tomi Valkeinen wrote:
> >> We support camera privacy leds with the .s_stream, in call_s_stream, but
> > 
> > s/the .s_stream/the .s_stream() operation/
> > 
> >> we don't have that support when the subdevice implements
> >> .enable/disable_streams.
> >>
> >> Add the support by enabling the led when the first stream for a
> >> subdevice is enabled, and disabling the led then the last stream is
> >> disabled.
> > 
> > I wonder if that will always be the correct constraint for all devices,
> > but I suppose we can worry about it later.
> > 
> >> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> >> ---
> >>   drivers/media/v4l2-core/v4l2-subdev.c | 9 +++++++++
> >>   1 file changed, 9 insertions(+)
> >>
> >> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
> >> index 20b5a00cbeeb..f44aaa4e1fab 100644
> >> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> >> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> >> @@ -2150,6 +2150,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
> >>   {
> >>   	struct device *dev = sd->entity.graph_obj.mdev->dev;
> >>   	struct v4l2_subdev_state *state;
> >> +	bool already_streaming;
> >>   	u64 found_streams = 0;
> >>   	unsigned int i;
> >>   	int ret;
> >> @@ -2198,6 +2199,8 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
> >>   
> >>   	dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask);
> >>   
> >> +	already_streaming = v4l2_subdev_is_streaming(sd);
> >> +
> >>   	/* Call the .enable_streams() operation. */
> >>   	ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
> >>   			       streams_mask);
> >> @@ -2216,6 +2219,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
> >>   			cfg->enabled = true;
> >>   	}
> >>   
> >> +	if (!already_streaming)
> >> +		v4l2_subdev_enable_privacy_led(sd);
> >> +
> >>   done:
> >>   	v4l2_subdev_unlock_state(state);
> >>   
> >> @@ -2340,6 +2346,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
> >>   	}
> >>   
> >>   done:
> >> +	if (!v4l2_subdev_is_streaming(sd))
> > 
> > Wouldn't it be more efficient to check this while looping over the
> > stream configs in the loop just above ? Same for
> > v4l2_subdev_enable_streams().
> 
> It would, but it would get a lot messier to manage with "media: subdev: 
> Refactor v4l2_subdev_enable/disable_streams()", and we would also need 
> to support the non-routing case.

True.

> This is usually a loop with a couple of iterations, and only called when 
> enabling or enabling a subdevice, so I'm not really worried about the 
> performance. If it's an issue, it would probably be better to also 
> update the sd->enabled_pads when enabling/disabling a stream.

OK, I can live with that for now.