Hi Helen, Thanks for your work. On 2020-04-03 18:33:10 -0300, Helen Koike wrote: > Add v4l2_pipeline_stream_{enable,disable} helper functions to iterate > through the subdevices in a given stream (i.e following links from sink > to source) and call .s_stream() callback. > > Add stream_count on the subdevice object for simultaneous streaming from > different video devices which shares subdevices. > > Prevent calling .s_stream(true) if it was already called previously by > another stream. > > Prevent calling .s_stream(false) from one stream when there are still > others active. > > If .s_stream(true) fails, call .s_stream(false) in the reverse order. > > Signed-off-by: Helen Koike <helen.koike@xxxxxxxxxxxxx> > > --- > > Changes in v2: > - re-write helpers to not use media walkers > > drivers/media/v4l2-core/v4l2-common.c | 117 ++++++++++++++++++++++++++ > include/media/v4l2-common.h | 28 ++++++ > include/media/v4l2-subdev.h | 2 + > 3 files changed, 147 insertions(+) > > diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c > index d0e5ebc736f9f..379d4bf4f8128 100644 > --- a/drivers/media/v4l2-core/v4l2-common.c > +++ b/drivers/media/v4l2-core/v4l2-common.c > @@ -441,3 +441,120 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, > return 0; > } > EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); > + > +/* > + * v4l2_pipeline_subdevs_get - Assemble a list of subdevices in a pipeline > + * @subdevs: the array to be filled > + * @size: the array size Should this be documented as the maximum number of elements that can fit in the subdevs array? > + * > + * Walk from a video node, following link from sink to source and fill the > + * array with subdevices in the path. > + * > + * Note: this function follows the first enabled link in a sink pad found in a > + * given entity. Thus it won't work if there are entities with multiple enabled > + * links to its sink pads in the topology. I wonder if it would be more useful to make this function return all subdevs in the pipeline that have an enabled link going from sink to source while walking from the video device? When reading the commit messages I thought this could be useful for the rcar-vin driver. By not finding all subdevices in the pipeline this would not be possible as there on some platforms are a CSI-2 bus where the CSI-2 transmitter have 4 sink pads and one source pads so the whole pipeline would not be started. > + * > + * Return the number of subdevices filled in the array. > + */ > +static unsigned int v4l2_pipeline_subdevs_get(struct video_device *vdev, > + struct v4l2_subdev **subdevs, > + unsigned int size) > +{ > + struct media_entity *entity = &vdev->entity; > + unsigned int idx = 0; > + > + while (1) { > + struct media_pad *src_pad = NULL; > + unsigned int i; > + > + /* Find remote source pad */ > + for (i = 0; i < entity->num_pads; i++) { > + struct media_pad *sink_pad = &entity->pads[i]; > + > + if (!(sink_pad->flags & MEDIA_PAD_FL_SINK)) > + continue; > + > + src_pad = media_entity_remote_pad(sink_pad); > + if (src_pad && > + is_media_entity_v4l2_subdev(src_pad->entity)) > + break; > + } > + if (i == entity->num_pads) > + break; > + > + if (idx >= size) { > + WARN_ON(1); > + return 0; Would it make sens to have this function return int and a negative error code here? Is this now how other areas of V4L2 deal with when a provided array is too small ? I'm thinking about if this function could be exported for use by drivers in the future. > + } > + > + entity = src_pad->entity; > + subdevs[idx++] = media_entity_to_v4l2_subdev(entity); > + } > + > + return idx; > +} > + > +__must_check int v4l2_pipeline_stream_enable(struct video_device *vdev) > +{ > + struct media_device *mdev = vdev->entity.graph_obj.mdev; > + struct v4l2_subdev *subdevs[MEDIA_ENTITY_ENUM_MAX_DEPTH]; > + struct v4l2_subdev *sd; > + unsigned int i, size; > + int ret; > + > + mutex_lock(&mdev->graph_mutex); > + > + size = v4l2_pipeline_subdevs_get(vdev, subdevs, ARRAY_SIZE(subdevs)); > + > + for (i = 0; i < size; i++) { > + sd = subdevs[i]; > + if (sd->stream_count++) > + continue; > + dev_dbg(mdev->dev, > + "enabling stream for '%s'\n", sd->entity.name); Small nit, would it make sens to print the sd->stream_count also in this debug statement (and the similar ones bellow) ? > + ret = v4l2_subdev_call(sd, video, s_stream, true); > + if (ret && ret != -ENOIOCTLCMD) > + goto err_stream_disable; > + } > + > + mutex_unlock(&mdev->graph_mutex); > + return 0; > + > +err_stream_disable: > + do { > + sd = subdevs[i]; > + if (--sd->stream_count) > + continue; > + dev_dbg(mdev->dev, > + "disabling stream for '%s'\n", sd->entity.name); > + v4l2_subdev_call(sd, video, s_stream, false); > + } while (i--); > + > + mutex_unlock(&mdev->graph_mutex); > + return ret; > +} > +EXPORT_SYMBOL_GPL(v4l2_pipeline_stream_enable); > + > +void v4l2_pipeline_stream_disable(struct video_device *vdev) > +{ > + struct media_device *mdev = vdev->entity.graph_obj.mdev; > + struct v4l2_subdev *subdevs[MEDIA_ENTITY_ENUM_MAX_DEPTH]; > + unsigned int size; > + > + mutex_lock(&mdev->graph_mutex); > + > + size = v4l2_pipeline_subdevs_get(vdev, subdevs, ARRAY_SIZE(subdevs)); > + > + while (size--) { > + struct v4l2_subdev *sd = subdevs[size]; > + > + if (--sd->stream_count) > + continue; > + dev_dbg(mdev->dev, > + "disabling stream for '%s'\n", sd->entity.name); > + v4l2_subdev_call(sd, video, s_stream, false); > + } > + > + mutex_unlock(&mdev->graph_mutex); > +} > +EXPORT_SYMBOL_GPL(v4l2_pipeline_stream_disable); > diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h > index 150ee16ebd811..e833610b0f66d 100644 > --- a/include/media/v4l2-common.h > +++ b/include/media/v4l2-common.h > @@ -519,6 +519,34 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, > int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat, > u32 width, u32 height); > > +/** > + * v4l2_pipeline_stream_enable - Call .s_stream(true) callbacks in the stream > + * @vdev: Starting video device > + * > + * Call .s_stream(true) callback in all the subdevices participating in the > + * stream, i.e. following links from sink to source. > + * > + * Calls to this function can be nested, in which case the same number of > + * v4l2_pipeline_stream_disable() calls will be required to stop streaming. > + * The pipeline pointer must be identical for all nested calls to > + * v4l2_pipeline_stream_enable(). > + */ > +__must_check int v4l2_pipeline_stream_enable(struct video_device *vdev); > + > +/** > + * v4l2_pipeline_stream_disable - Call .s_stream(false) callbacks in the stream > + * @vdev: Starting video device > + * > + * Call .s_stream(true) callback in all the subdevices participating in the > + * stream, i.e. following links from sink to source. > + * > + * Calls to this function can be nested, in which case the same number of > + * v4l2_pipeline_stream_disable() calls will be required to stop streaming. > + * The pipeline pointer must be identical for all nested calls to > + * v4l2_pipeline_stream_enable(). > + */ > +void v4l2_pipeline_stream_disable(struct video_device *vdev); > + > static inline u64 v4l2_buffer_get_timestamp(const struct v4l2_buffer *buf) > { > /* > diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h > index a4848de598521..20f913a9f70c5 100644 > --- a/include/media/v4l2-subdev.h > +++ b/include/media/v4l2-subdev.h > @@ -838,6 +838,7 @@ struct v4l2_subdev_platform_data { > * @subdev_notifier: A sub-device notifier implicitly registered for the sub- > * device using v4l2_device_register_sensor_subdev(). > * @pdata: common part of subdevice platform data > + * @stream_count: Stream count for the subdevice. > * > * Each instance of a subdev driver should create this struct, either > * stand-alone or embedded in a larger struct. > @@ -869,6 +870,7 @@ struct v4l2_subdev { > struct v4l2_async_notifier *notifier; > struct v4l2_async_notifier *subdev_notifier; > struct v4l2_subdev_platform_data *pdata; > + unsigned int stream_count; > }; > > > -- > 2.26.0 > -- Regards, Niklas Söderlund