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
next prev parent 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