Linux Media Controller development
 help / color / mirror / Atom feed
From: Sakari Ailus <sakari.ailus@linux.intel.com>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: linux-media@vger.kernel.org, bingbu.cao@linux.intel.com,
	stanislaw.gruszka@linux.intel.com, tian.shu.qiu@intel.com,
	tomi.valkeinen@ideasonboard.com
Subject: Re: [PATCH 12/13] media: v4l2-mc: Introduce v4l2_mc_pipeline_enabled()
Date: Mon, 4 Aug 2025 11:32:17 +0000	[thread overview]
Message-ID: <aJCaQdmv6wv6cSwY@kekkonen.localdomain> (raw)
In-Reply-To: <20250626230710.GA31209@pendragon.ideasonboard.com>

Hi Laurent,

Thanks for the review.

On Fri, Jun 27, 2025 at 02:07:10AM +0300, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Thu, Jun 19, 2025 at 11:15:45AM +0300, Sakari Ailus wrote:
> > v4l2_mc_pipeline_enabled() helps solving a problem known for long but
> > lacked any sort of general solution: with multiple streams, when streaming
> > is started on video nodes one by one, when should streaming be started in
> > the source?
> > 
> > v4l2_mc_pipeline_enabled() traverses the pipeline towards the source,
> > queries the streams generated by the source and traces them back to the
> > video nodes.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/v4l2-core/v4l2-mc.c | 243 ++++++++++++++++++++++++++++++
> >  include/media/v4l2-mc.h           |  44 ++++++
> >  2 files changed, 287 insertions(+)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c
> > index 937d358697e1..1731088ad436 100644
> > --- a/drivers/media/v4l2-core/v4l2-mc.c
> > +++ b/drivers/media/v4l2-core/v4l2-mc.c
> > @@ -612,3 +612,246 @@ int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
> >  	return ret;
> >  }
> >  EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
> > +
> > +static int
> > +__v4l2_mc_pipeline_enabled(struct v4l2_subdev_state *state,
> > +			   struct media_pad *src_pad, u64 __src_streams,
> > +			   struct media_pad **__sink_pad, u64 *__sink_streams)
> > +{
> > +	struct v4l2_subdev_route *route;
> > +	u64 src_streams = 0, sink_streams = 0;
> > +	bool has_sink_pad = false;
> > +	unsigned int sink_pad;
> > +
> > +	dev_dbg(state->sd->dev, "%s: source enabled, pad/streams %u/%#llx\n",
> > +		state->sd->entity.name, src_pad->index, __src_streams);
> > +	for_each_active_route(&state->routing, route) {
> > +		dev_dbg(state->sd->dev, "%s: %u/%u -> %u/%u, flags %x\n",
> > +			state->sd->entity.name,
> > +			route->sink_pad, route->sink_stream, route->source_pad,
> > +			route->source_stream, route->flags);
> > +		if (route->source_pad != src_pad->index)
> > +			continue;
> > +
> > +		if (!(BIT_ULL(route->source_stream) & __src_streams))
> > +			continue;
> > +
> > +		if (!has_sink_pad) {
> > +			has_sink_pad = true;
> > +			sink_pad = route->sink_pad;
> > +		}
> > +
> > +		if (route->sink_pad != sink_pad) {
> > +			dev_dbg(state->sd->dev,
> > +				"sink pads (%u vs. %u) differ\n",
> > +				route->sink_pad, sink_pad);
> > +			return -EMLINK;
> > +		}
> > +
> > +		sink_streams |= BIT_ULL(route->sink_stream);
> > +		src_streams |= BIT_ULL(route->source_stream);
> > +	}
> > +
> > +	*__sink_pad = has_sink_pad ? &state->sd->entity.pads[sink_pad] : NULL;
> > +	*__sink_streams = sink_streams;
> > +
> > +	return 0;
> > +}
> > +
> > +static int v4l2_mc_downpath_enabled(struct media_pad *sink_pad,
> > +				    unsigned int sink_stream,
> > +				    bool (*func)(struct video_device *vdev),
> > +				    struct media_pad **__sink_pad,
> > +				    u64 *__sink_streams)
> > +{
> > +	struct v4l2_subdev_state *state;
> > +	struct v4l2_subdev_route *route;
> > +	struct v4l2_subdev *sd;
> > +	struct media_pad *source_pad, *tmp_pad;
> > +	u32 source_stream;
> > +
> > +	if (!is_media_entity_v4l2_subdev(sink_pad->entity))
> > +		return -ENXIO;
> > +
> > +	sd = media_entity_to_v4l2_subdev(sink_pad->entity);
> > +	dev_dbg(sd->dev, "path_enabled: found sub-device %s\n",
> > +		sd->entity.name);
> > +
> > +	state = v4l2_subdev_lock_and_get_active_state(sd);
> > +	route = v4l2_subdev_find_route(&state->routing, sink_pad->index,
> > +				       sink_stream, true, 0);
> > +	if (IS_ERR(route)) {
> > +		v4l2_subdev_unlock_state(state);
> > +		dev_dbg(sd->dev,
> > +			"path_enabled: can't find opposite route for %s:%u/%u",
> > +			sd->entity.name, sink_pad->index, sink_stream);
> > +		return 2;
> > +	}
> > +
> > +	source_pad = &sd->entity.pads[route->source_pad];
> > +	v4l2_subdev_unlock_state(state);
> > +
> > +	tmp_pad = sink_pad;
> > +	sink_pad = media_pad_remote_pad_unique(source_pad);
> > +	if (IS_ERR(sink_pad)) {
> > +		dev_dbg(sd->dev,
> > +			"path_enabled: can't find remote source for %s:%u\n",
> > +			source_pad->entity->name, source_pad->index);
> > +		return PTR_ERR(sink_pad);
> > +	}
> > +
> > +	if (is_media_entity_v4l2_video_device(sink_pad->entity)) {
> > +		struct video_device *vdev;
> > +
> > +		vdev = media_entity_to_video_device(sink_pad->entity);
> > +		if (!vdev)
> > +			return -ENXIO;
> > +
> > +		dev_dbg(vdev->dev_parent,
> > +			"path_enabled: found video device %s\n",
> > +			vdev->name);
> > +
> > +		if (!*__sink_pad) {
> > +			*__sink_pad = tmp_pad;
> > +			dev_dbg(sd->dev, "path_enabled: sink %u/%u\n",
> > +				tmp_pad->index, sink_stream);
> > +		} else if (tmp_pad != *__sink_pad) {
> > +			dev_dbg(sd->dev,
> > +				"path_enabled: pads %s/%u and %s/%u differ\n",
> > +				tmp_pad->entity->name, tmp_pad->index,
> > +				(*__sink_pad)->entity->name,
> > +				(*__sink_pad)->index);
> > +			return -EXDEV;
> > +		}
> > +
> > +		*__sink_streams |= BIT_ULL(sink_stream);
> > +
> > +		return func(vdev);
> > +	}
> > +
> > +	return v4l2_mc_downpath_enabled(sink_pad, source_stream, func,
> > +					__sink_pad, __sink_streams);
> > +}
> > +
> > +static int v4l2_mc_source_get_streams(struct v4l2_subdev *sd, unsigned int pad,
> > +				      u64 *__streams)
> > +{
> > +	struct v4l2_mbus_frame_desc desc;
> > +	u64 streams = 0;
> > +	int ret;
> > +
> > +	if (!__streams)
> > +		return -EINVAL;
> > +
> > +	ret = v4l2_subdev_call(sd, pad, get_frame_desc, pad, &desc);
> > +	if (ret == -ENOIOCTLCMD) {
> > +		*__streams = 1ULL;
> > +		return 0;
> > +	}
> > +	if (ret)
> > +		return ret;
> > +
> > +	for (unsigned int i = 0; i < desc.num_entries; i++) {
> > +		if (streams & BIT_ULL(desc.entry[i].stream))
> > +			return -EINVAL;
> > +
> > +		streams |= BIT_ULL(desc.entry[i].stream);
> > +	}
> > +
> > +	dev_dbg(sd->dev, "found streams %#llx based on streams %#llx\n",
> > +		*__streams, streams);
> > +	if (*__streams & ~streams)
> > +		return -EINVAL;
> > +
> > +	*__streams = streams;
> > +
> > +	return 0;
> > +}
> > +
> > +int v4l2_mc_pipeline_enabled(struct video_device *vdev,
> > +			     bool (*func)(struct video_device *vdev),
> > +			     struct media_pad **__sink_pad, u64 *__sink_streams)
> > +{
> > +	u64 sink_streams = 1U;
> > +	struct media_pad *src_pad;
> > +	u64 src_streams;
> > +	struct v4l2_subdev_state *state;
> > +	struct media_pad *sink_pad = vdev->entity.pads;
> > +	struct v4l2_subdev *sd = NULL;
> > +	bool streaming = true;
> > +	struct media_pad *tmp_pad;
> > +	u64 tmp_streams;
> > +	int ret;
> > +
> > +	if (!__sink_pad)
> > +		__sink_pad = &tmp_pad;
> > +	if (!__sink_streams)
> > +		__sink_streams = &tmp_streams;
> > +	*__sink_pad = NULL;
> > +	*__sink_streams = 0;
> > +
> > +	do {
> > +		src_pad = media_pad_remote_pad_unique(sink_pad);
> > +		if (IS_ERR(src_pad)) {
> > +			dev_dbg(sd ? sd->dev : vdev->dev_parent,
> > +				"no unique remote pad found from %s:%u\n",
> > +				sink_pad->entity->name, sink_pad->index);
> > +			return PTR_ERR(src_pad);
> > +		}
> > +
> > +		sd = media_entity_to_v4l2_subdev(src_pad->entity);
> > +		if (!sd) {
> > +			dev_dbg(sd->dev,
> > +				"media entity %s is not a V4L2 sub-device\n",
> > +				src_pad->entity->name);
> > +			return -ENXIO;
> > +		}
> > +
> > +		/* Source streams match sink. */
> > +		src_streams = sink_streams;
> > +
> > +		state = v4l2_subdev_lock_and_get_active_state(sd);
> > +		ret = __v4l2_mc_pipeline_enabled(state, src_pad,
> > +						 src_streams, &sink_pad,
> > +						 &sink_streams);
> > +		v4l2_subdev_unlock_state(state);
> > +		if (ret)
> > +			return ret;
> > +	} while (sink_pad);
> > +
> > +	ret = v4l2_mc_source_get_streams(sd, src_pad->index, &src_streams);
> > +	if (ret)
> > +		return ret;
> > +
> > +	sd = media_entity_to_v4l2_subdev(src_pad->entity);
> > +
> > +	dev_dbg(sd->dev, "following %s:%u/%#llx\n", sd->entity.name,
> > +		src_pad->index, src_streams);
> > +
> > +	for (unsigned int i = __ffs(src_streams); src_streams;
> > +	     src_streams &= ~BIT_ULL(i), i = __ffs(src_streams)) {
> > +		sink_pad = media_pad_remote_pad_unique(src_pad);
> > +		if (IS_ERR(src_pad)) {
> > +			dev_dbg(sd->dev,
> > +				"no unique remote pad found from %s:%u\n",
> > +				sink_pad->entity->name, sink_pad->index);
> > +			return PTR_ERR(src_pad);
> > +		}
> > +
> > +		ret = v4l2_mc_downpath_enabled(sink_pad, i, func, __sink_pad,
> > +					       __sink_streams);
> > +		if (ret == 2)
> > +			continue;
> > +		if (ret < 0)
> > +			return ret;
> > +		if (!ret)
> > +			streaming = false;
> > +	}
> > +
> > +	dev_dbg(media_entity_to_v4l2_subdev((*__sink_pad)->entity)->dev,
> > +		"sink pad %s:%u/%#llx\n", (*__sink_pad)->entity->name,
> > +		(*__sink_pad)->index, *__sink_streams);
> > +
> > +	return streaming;
> > +}
> > +EXPORT_SYMBOL_GPL(v4l2_mc_pipeline_enabled);
> > diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h
> > index 1837c9fd78cf..e72c0f62fa34 100644
> > --- a/include/media/v4l2-mc.h
> > +++ b/include/media/v4l2-mc.h
> > @@ -193,6 +193,50 @@ void v4l2_pipeline_pm_put(struct media_entity *entity);
> >  int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
> >  			      unsigned int notification);
> >  
> > +/**
> > + * v4l2_mc_pipeline_enabled - Tell when to start streaming
> > + * @vdev: The video device
> > + * @func: Caller-provided function to tell a video device's streaming state
> > + * @__sink_pad: sink pad at the root of the local pipeline
> > + * @__sink_streams: streams to start
> 
> Any reason for the double underscore ?

The function used internally a variable without the underscores for a
different purpose.

> 
> > + *
> > + * Use to tell whether streaming should start on a video node. @func returns
> > + * true if streaming has been started on a given video node. @__sink_pad and
> > + * @__sink_streams are filled with pad and streams on the sub-device closest to
> > + * the video nodes, to be used for calling v4l2_subdev_enable_streams() and
> > + * v4l2_subdev_disable_streams().
> > + *
> > + * Using v4l2_mc_pipeline_enabled() has a few limitations currently (consider it
> > + * a to-do list):
> > + * * only unbranched streams can be supported albeit adding support for
> > + *   downstream branches would be fairly trivial,
> 
> I can't tell from the documentation here what you mean exactly by
> "unbranched streams".

These may have been referred to as "linear" streams elsewhere. I'll address
this in the next version.

> 
> > + * * streams within a single source sub-device are considered to start at the
> > + *   same time, more control could be added in two ways: 1) for sources to
> > + *   determine stream starting, a control could be added to UAPI and 2) sources
> > + *   could tell which streams start at the same time using a sub-device
> > + *   operation,
> > + * * CSI-2 VC framing is ignored currently, but VC-based stream starting could
> > + *   be implemented by letting the caller to provide a function to determine
> > + *   which streams are of interest and
> > + * * routes leading to nowhere are ignored, on some hardware this is a problem,
> > + *   but this can also be rather trivially addressed.
> 
> I'm afraid this function looks like a hack, to solve a problem that is
> not even explicitly described. You don't explain the issue in the cover
> letter or in Documentation/, the cover letter merely states that this is
> a "partial solution". The documentation of the function doesn't explain
> what criteria the decision is based on. We need a proper explanation of
> the problem in Documentation/, with a description of the behaviour (or
> behaviours) drivers are expected to implement.

I believe this is discussed in the cover letter, not in detail though. I'm
fine with adding more documentation, there isn't much as I also wanted to
get feedback on the approach itself. No alternatives have been proposed so
far either.

> 
> Furthermore, on the implementation side, things are fairly inefficient.
> We already traverse the whole pipeline in media_pipeline_start(), based
> on links and routes, and populate the media_pipeline structure. We
> shouldn't do the same here, but instead inspect media_pipeline to
> extract the information we need. If you're missing information there,
> let's add it.

The media pipeline is created and traversed, yes, but the media pipeline
does not include streams which are strictly a V4L2 concept. I agree there
is some overlap between the two but as long as MC remains separate from
V4L2, we can't reasonably access streams from the pipeline traversal.

> 
> > + *
> > + * Return:
> > + * * 0: Success, but don't start streaming yet
> > + * * 1: Success, now it's time to start streaming
> > + * * -ENXIO: Route traversal encountered a non-video device/sub-device entity
> > + * * -ENOTUNIQ: No unique remote pad
> > + * * -ENOLINK: No remote pad found
> > + * * -ENOENT: Enabled upstream route not found
> > + * * -EMLINK: No unique downstream route found
> > + * * -EINVAL: Stream could not be followed to source or was not produced by
> > + *            the source
> > + */
> > +int v4l2_mc_pipeline_enabled(struct video_device *vdev,
> > +			     bool (*func)(struct video_device *vdev),
> > +			     struct media_pad **__sink_pad,
> > +			     u64 *__sink_streams);
> > +
> >  #else /* CONFIG_MEDIA_CONTROLLER */
> >  
> >  static inline int v4l2_mc_create_media_graph(struct media_device *mdev)

-- 
Kind regards,

Sakari Ailus

  reply	other threads:[~2025-08-04 11:32 UTC|newest]

Thread overview: 66+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-19  8:15 [PATCH 00/13] Streaming control for MC with metadata or streams otherwise Sakari Ailus
2025-06-19  8:15 ` [PATCH 01/13] media: ipu6: Use correct pads for xlate_streams() Sakari Ailus
2025-06-19 13:27   ` Laurent Pinchart
2025-06-19 13:55     ` Sakari Ailus
2025-06-19 14:15       ` Laurent Pinchart
2025-06-19 14:28         ` Sakari Ailus
2025-06-19 15:08           ` Laurent Pinchart
2025-06-19  8:15 ` [PATCH 02/13] media: ipu6: Set minimum height to 1 Sakari Ailus
2025-06-19 13:27   ` Laurent Pinchart
2025-06-19  8:15 ` [PATCH 03/13] media: ipu6: Enable and disable each stream at CSI-2 subdev source pad Sakari Ailus
2025-06-19 12:23   ` kernel test robot
2025-06-19 12:48   ` Laurent Pinchart
2025-06-19 13:10     ` Sakari Ailus
2025-06-19 13:19       ` Laurent Pinchart
2025-06-19 13:52         ` Sakari Ailus
2025-06-19  8:15 ` [PATCH 04/13] media: v4l2-subdev: Add a helper to figure out the pad streaming state Sakari Ailus
2025-06-19 13:37   ` Laurent Pinchart
2025-06-19  8:15 ` [PATCH 05/13] media: v4l: Make media_entity_to_video_device() NULL-safe Sakari Ailus
2025-06-19 15:20   ` Laurent Pinchart
2025-06-19 16:14     ` Sakari Ailus
2025-07-08 11:56       ` Laurent Pinchart
2025-07-08 12:02         ` Sakari Ailus
2025-07-08 16:17           ` Laurent Pinchart
2025-07-09 20:03             ` Sakari Ailus
2025-07-09 20:54               ` Laurent Pinchart
2025-07-10  6:57                 ` Sakari Ailus
2025-06-19  8:15 ` [PATCH 06/13] media: v4l2-subdev: Mark both streams of a route enabled Sakari Ailus
2025-06-19 16:56   ` Laurent Pinchart
2025-06-19 18:34     ` Sakari Ailus
2025-06-19 22:18       ` Laurent Pinchart
2025-06-25 16:10         ` Sakari Ailus
2025-06-26 15:22       ` Tomi Valkeinen
2025-06-26 19:13         ` Laurent Pinchart
2025-06-26 15:17   ` Tomi Valkeinen
2025-06-27  6:09     ` Sakari Ailus
2025-06-30  0:47       ` Laurent Pinchart
2025-06-19  8:15 ` [PATCH 07/13] media: ipu6: Set up CSI-2 receiver at correct moment Sakari Ailus
2025-06-19 17:00   ` Laurent Pinchart
2025-06-19 17:20     ` Sakari Ailus
2025-06-19  8:15 ` [PATCH 08/13] media: v4l2-subdev: Print early in v4l2_subdev_{enable,disable}_streams() Sakari Ailus
2025-06-19 17:03   ` Laurent Pinchart
2025-06-25 16:12     ` Sakari Ailus
2025-06-19  8:15 ` [PATCH 09/13] media: v4l2-subdev: Collect streams on source pads only Sakari Ailus
2025-06-19 17:07   ` Laurent Pinchart
2025-06-25 16:14     ` Sakari Ailus
2025-06-19  8:15 ` [PATCH 10/13] media: v4l2-subdev: Add debug prints to v4l2_subdev_collect_streams() Sakari Ailus
2025-06-19 22:23   ` Laurent Pinchart
2025-06-25 16:28     ` Sakari Ailus
2025-06-19  8:15 ` [PATCH 11/13] media: v4l2-subdev: Introduce v4l2_subdev_find_route() Sakari Ailus
2025-06-20  8:14   ` Jacopo Mondi
2025-06-25 16:53     ` Sakari Ailus
2025-06-26 22:20       ` Laurent Pinchart
2025-07-15 14:09         ` Sakari Ailus
2025-06-19  8:15 ` [PATCH 12/13] media: v4l2-mc: Introduce v4l2_mc_pipeline_enabled() Sakari Ailus
2025-06-19 11:42   ` kernel test robot
2025-06-20  3:58   ` Dan Carpenter
2025-06-20  8:53   ` Jacopo Mondi
2025-06-21  8:10     ` Sakari Ailus
2025-07-15 10:49     ` Sakari Ailus
2025-07-15 11:25       ` Laurent Pinchart
2025-07-15 11:32         ` Sakari Ailus
2025-07-15 18:18           ` Laurent Pinchart
2025-06-26 23:07   ` Laurent Pinchart
2025-08-04 11:32     ` Sakari Ailus [this message]
2025-08-04 11:46       ` Laurent Pinchart
2025-06-19  8:15 ` [PATCH 13/13] media: ipu6: isys: Rework stream starting and stopping Sakari Ailus

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aJCaQdmv6wv6cSwY@kekkonen.localdomain \
    --to=sakari.ailus@linux.intel.com \
    --cc=bingbu.cao@linux.intel.com \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-media@vger.kernel.org \
    --cc=stanislaw.gruszka@linux.intel.com \
    --cc=tian.shu.qiu@intel.com \
    --cc=tomi.valkeinen@ideasonboard.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox