Hi Laurent, Thanks for your feedback! On 2025-05-27 13:45:13 +0200, Laurent Pinchart wrote: > Hi Niklas, > > Thank you for the patch. > > On Wed, May 21, 2025 at 03:20:36PM +0200, Niklas Söderlund wrote: > > The VIN usage of v4l-async is complex and stems from organic growth of > > the driver of supporting both private local subdevices (Gen2, Gen3) and > > subdevices shared between all VIN instances (Gen3 and Gen4). > > > > The driver used a separate notifier for each VIN for the private local > > ones, and a shared group notifier for the shared ones. This was complex > > and lead to subtle bugs when unbinding and later rebinding subdevices in > > on of the notifiers having to handle different edge cases depending on > > s/on of/one of/ (I think) > > > if it also had subdevices in the other notifiers etc. > > > > To simplify this have the Gen2 devices allocate and form a VIN group > > too. This way all subdevices on all models can be collect in a > > single group notifier. Then there is only a single complete callback for > > all where the video devices and subdevice nodes can be registered etc. > > > > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@xxxxxxxxxxxx> > > --- > > .../platform/renesas/rcar-vin/rcar-core.c | 263 ++++++++---------- > > .../platform/renesas/rcar-vin/rcar-vin.h | 2 - > > 2 files changed, 114 insertions(+), 151 deletions(-) > > > > diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c > > index 60ec57d73a12..b0727e98dac6 100644 > > --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c > > +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c > > @@ -43,6 +43,9 @@ > > > > #define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev) > > > > +static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, > > + struct v4l2_subdev *subdev); > > Any chance you could move the function instead of forward-declaring it ? I could move it in a separate commit prior to this patch. But the last patch in the series removes the function all together so I thought that to be more churn then needed. > > > + > > /* ----------------------------------------------------------------------------- > > * Gen3 Group Allocator > > */ > > @@ -232,7 +235,10 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) > > } > > } > > > > - return vin->group->link_setup(vin->group); > > + if (vin->group->link_setup) > > + return vin->group->link_setup(vin->group); > > + > > + return 0; > > } > > > > static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, > > @@ -240,22 +246,31 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, > > struct v4l2_async_connection *asc) > > { > > struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); > > - unsigned int i; > > + struct rvin_group *group = vin->group; > > > > - for (i = 0; i < RCAR_VIN_NUM; i++) > > - if (vin->group->vin[i]) > > - rvin_v4l2_unregister(vin->group->vin[i]); > > + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) > > While at it, you can use ARRAY_SIZE(). The use of RCAR_VIN_NUM is everywhere in the driver. I do however agree using ARRAY_SIZE() is better. I will convert the whole driver to ARRAY_SIZE() in a follow up patch outside this series. I think it's important that all loops look the same and I'm worried this series is growing too large with all nice, but unrelated, cleanups unearth by it. I hope this is OK. I will however keep the conversion to ARRAY_SIZE() for the RVIN_REMOTES_MAX define as this was already something this series needed to address in patch 1 and it's only used in one location that was not already touched by this series. > > > + if (group->vin[i]) > > + rvin_v4l2_unregister(group->vin[i]); > > And please use curly braces for the for statement. > > > > > mutex_lock(&vin->group->lock); > > Add a blank line. > > > + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { > > ARRAY_SIZE() here too. Same below where applicable. > > > + if (!group->vin[i] || group->vin[i]->parallel.asc != asc) > > + continue; > > + > > + group->vin[i]->parallel.subdev = NULL; > > + > > + vin_dbg(group->vin[i], "Unbind parallel subdev %s\n", > > + subdev->name); > > + } > > > > - for (i = 0; i < RVIN_REMOTES_MAX; i++) { > > - if (vin->group->remotes[i].asc != asc) > > + for (unsigned int i = 0; i < RVIN_REMOTES_MAX; i++) { > > + if (group->remotes[i].asc != asc) > > continue; > > - vin->group->remotes[i].subdev = NULL; > > + > > + group->remotes[i].subdev = NULL; > > + > > vin_dbg(vin, "Unbind %s from slot %u\n", subdev->name, i); > > - break; > > } > > - > > You can keep this blank line :-) > > > mutex_unlock(&vin->group->lock); > > > > media_device_unregister(&vin->group->mdev); > > @@ -266,21 +281,38 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, > > struct v4l2_async_connection *asc) > > { > > struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); > > - unsigned int i; > > + struct rvin_group *group = vin->group; > > > > - mutex_lock(&vin->group->lock); > > + guard(mutex)(&group->lock); > > > > - for (i = 0; i < RVIN_REMOTES_MAX; i++) { > > + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { > > + int ret; > > + > > + if (!group->vin[i] || group->vin[i]->parallel.asc != asc) > > + continue; > > + > > + ret = rvin_parallel_subdevice_attach(group->vin[i], subdev); > > + if (ret) > > + return ret; > > + > > + v4l2_set_subdev_hostdata(subdev, group->vin[i]); > > + > > + vin_dbg(group->vin[i], "Bound subdev %s\n", subdev->name); > > + > > + return 0; > > + } > > + > > + for (unsigned int i = 0; i < RVIN_REMOTES_MAX; i++) { > > if (vin->group->remotes[i].asc != asc) > > continue; > > + > > vin->group->remotes[i].subdev = subdev; > > vin_dbg(vin, "Bound %s to slot %u\n", subdev->name, i); > > - break; > > + > > + return 0; > > } > > > > - mutex_unlock(&vin->group->lock); > > - > > - return 0; > > + return -ENODEV; > > } > > > > static const struct v4l2_async_notifier_operations rvin_group_notify_ops = { > > @@ -374,7 +406,7 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin) > > goto out; > > } > > > > - asc = v4l2_async_nf_add_fwnode(&vin->notifier, fwnode, > > + asc = v4l2_async_nf_add_fwnode(&vin->group->notifier, fwnode, > > struct v4l2_async_connection); > > if (IS_ERR(asc)) { > > ret = PTR_ERR(asc); > > @@ -424,6 +456,12 @@ static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port, > > if (!(vin_mask & BIT(i))) > > continue; > > > > + /* Parse local subdevice. */ > > + ret = rvin_parallel_parse_of(vin->group->vin[i]); > > + if (ret) > > + return ret; > > + > > + /* Prase shared subdevices. */ > > for (id = 0; id < max_id; id++) { > > if (vin->group->remotes[id].asc) > > continue; > > @@ -603,124 +641,6 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, > > return 0; > > } > > > > -static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) > > -{ > > - rvin_v4l2_unregister(vin); > > - vin->parallel.subdev = NULL; > > - > > - if (!vin->info->use_mc) > > - rvin_free_controls(vin); > > -} > > - > > -static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) > > -{ > > - struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); > > - struct media_entity *source; > > - struct media_entity *sink; > > - int ret; > > - > > - ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); > > - if (ret < 0) { > > - vin_err(vin, "Failed to register subdev nodes\n"); > > - return ret; > > - } > > - > > - if (!video_is_registered(&vin->vdev)) { > > - ret = rvin_v4l2_register(vin); > > - if (ret < 0) > > - return ret; > > - } > > - > > - if (!vin->info->use_mc) > > - return 0; > > - > > - /* If we're running with media-controller, link the subdevs. */ > > - source = &vin->parallel.subdev->entity; > > - sink = &vin->vdev.entity; > > - > > - ret = media_create_pad_link(source, vin->parallel.source_pad, > > - sink, vin->parallel.sink_pad, 0); > > - if (ret) > > - vin_err(vin, "Error adding link from %s to %s: %d\n", > > - source->name, sink->name, ret); > > - > > - return ret; > > -} > > - > > -static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, > > - struct v4l2_subdev *subdev, > > - struct v4l2_async_connection *asc) > > -{ > > - struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); > > - > > - vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); > > - > > - mutex_lock(&vin->lock); > > - rvin_parallel_subdevice_detach(vin); > > - mutex_unlock(&vin->lock); > > -} > > - > > -static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, > > - struct v4l2_subdev *subdev, > > - struct v4l2_async_connection *asc) > > -{ > > - struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); > > - int ret; > > - > > - mutex_lock(&vin->lock); > > - ret = rvin_parallel_subdevice_attach(vin, subdev); > > - mutex_unlock(&vin->lock); > > - if (ret) > > - return ret; > > - > > - v4l2_set_subdev_hostdata(subdev, vin); > > - > > - vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", > > - subdev->name, vin->parallel.source_pad, > > - vin->parallel.sink_pad); > > - > > - return 0; > > -} > > - > > -static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { > > - .bound = rvin_parallel_notify_bound, > > - .unbind = rvin_parallel_notify_unbind, > > - .complete = rvin_parallel_notify_complete, > > -}; > > - > > -static void rvin_parallel_cleanup(struct rvin_dev *vin) > > -{ > > - v4l2_async_nf_unregister(&vin->notifier); > > - v4l2_async_nf_cleanup(&vin->notifier); > > -} > > - > > -static int rvin_parallel_init(struct rvin_dev *vin) > > -{ > > - int ret; > > - > > - v4l2_async_nf_init(&vin->notifier, &vin->v4l2_dev); > > - > > - ret = rvin_parallel_parse_of(vin); > > - if (ret) > > - return ret; > > - > > - if (!vin->parallel.asc) > > - return -ENODEV; > > - > > - vin_dbg(vin, "Found parallel subdevice %pOF\n", > > - to_of_node(vin->parallel.asc->match.fwnode)); > > - > > - vin->notifier.ops = &rvin_parallel_notify_ops; > > - ret = v4l2_async_nf_register(&vin->notifier); > > - if (ret < 0) { > > - vin_err(vin, "Notifier registration failed\n"); > > - v4l2_async_nf_cleanup(&vin->notifier); > > - return ret; > > - } > > - > > - return 0; > > -} > > - > > /* ----------------------------------------------------------------------------- > > * CSI-2 > > */ > > @@ -895,11 +815,63 @@ static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id, > > return 0; > > } > > > > +static int rvin_parallel_setup_links(struct rvin_group *group) > > +{ > > + u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; > > + int ret = 0; > > + > > + mutex_lock(&group->lock); > > Use a guard. > > > + /* If the group also have links don't enable the link. */ > > s/have/has/ > > > + for (unsigned int i = 0; i < RVIN_REMOTES_MAX; i++) { > > + if (group->remotes[i].subdev) { > > + flags = 0; > > + break; > > + } > > + } > > + > > + /* Create links */ > > s/links/links./ > > > + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { > > + struct rvin_dev *vin = group->vin[i]; > > + struct media_entity *source; > > + struct media_entity *sink; > > + > > + /* Noting to do if their is no VIN or parallel subdev. */ > > s/Noting/Nothing/ > s/their/there/ > > > + if (!vin || !vin->parallel.subdev) > > + continue; > > + > > + source = &vin->parallel.subdev->entity; > > + sink = &vin->vdev.entity; > > + > > + ret = media_create_pad_link(source, vin->parallel.source_pad, > > + sink, 0, flags); > > + if (ret) > > + break; > > return ret; > > (thanks to the guard above) > > > + } > > + mutex_unlock(&group->lock); > > + > > + return ret; > > return 0; > > > +} > > + > > static int rvin_csi2_setup_links(struct rvin_group *group) > > { > > const struct rvin_group_route *routes, *route; > > unsigned int id; > > - int ret = -EINVAL; > > + int ret; > > + > > + /* Find any VIN in group to get route info. */ > > + routes = NULL; > > + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { > > + if (group->vin[i]) { > > + routes = group->vin[i]->info->routes; > > + break; > > + } > > + } > > + if (!routes) > > + return -ENODEV; > > Storing the info pointer in the group as proposed in the review of a > previous patch from the same series would help here too. I agree. I have added a patch that introduce this change separatly and all new code uses the info from the group structure. I have added to my ever growing todo file to remove the info struct all together from the private VIN data structure in a follow up patch. This change would depend on this series as Gen2 currently do not participate in the group concept. > > > + > > + ret = rvin_parallel_setup_links(group); > > + if (ret) > > + return ret; > > > > /* Find any VIN in group to get route info. */ > > routes = NULL; > > @@ -914,6 +886,7 @@ static int rvin_csi2_setup_links(struct rvin_group *group) > > > > /* Create all media device links between VINs and CSI-2's. */ > > mutex_lock(&group->lock); > > + ret = -EINVAL; > > for (route = routes; route->chsel; route++) { > > /* Check that VIN' master is part of the group. */ > > if (!group->vin[route->master]) > > @@ -941,7 +914,6 @@ static int rvin_csi2_setup_links(struct rvin_group *group) > > > > static void rvin_csi2_cleanup(struct rvin_dev *vin) > > { > > - rvin_parallel_cleanup(vin); > > rvin_group_notifier_cleanup(vin); > > rvin_group_put(vin); > > rvin_free_controls(vin); > > @@ -964,18 +936,11 @@ static int rvin_csi2_init(struct rvin_dev *vin) > > if (ret) > > goto err_controls; > > > > - /* It's OK to not have a parallel subdevice. */ > > - ret = rvin_parallel_init(vin); > > - if (ret && ret != -ENODEV) > > - goto err_group; > > - > > ret = rvin_group_notifier_init(vin, 1, RVIN_CSI_MAX); > > if (ret) > > - goto err_parallel; > > + goto err_group; > > > > return 0; > > -err_parallel: > > - rvin_parallel_cleanup(vin); > > err_group: > > rvin_group_put(vin); > > err_controls: > > @@ -1448,7 +1413,9 @@ static int rcar_vin_probe(struct platform_device *pdev) > > rvin_group_id_to_master(vin->id) == vin->id) > > vin->scaler = vin->info->scaler; > > } else { > > - ret = rvin_parallel_init(vin); > > + ret = rvin_group_get(vin, NULL, NULL); > > + if (!ret) > > + ret = rvin_group_notifier_init(vin, 0, 0); > > > > if (vin->info->scaler) > > vin->scaler = vin->info->scaler; > > @@ -1478,8 +1445,6 @@ static void rcar_vin_remove(struct platform_device *pdev) > > rvin_isp_cleanup(vin); > > else if (vin->info->use_mc) > > rvin_csi2_cleanup(vin); > > - else > > - rvin_parallel_cleanup(vin); > > > > rvin_id_put(vin); > > > > diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h > > index 7d4fce248976..a577f4fe4a6c 100644 > > --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h > > +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h > > @@ -149,7 +149,6 @@ struct rvin_info { > > * @vdev: V4L2 video device associated with VIN > > * @v4l2_dev: V4L2 device > > * @ctrl_handler: V4L2 control handler > > - * @notifier: V4L2 asynchronous subdevs notifier > > * > > * @parallel: parallel input subdevice descriptor > > * > > @@ -189,7 +188,6 @@ struct rvin_dev { > > struct video_device vdev; > > struct v4l2_device v4l2_dev; > > struct v4l2_ctrl_handler ctrl_handler; > > - struct v4l2_async_notifier notifier; > > > > struct rvin_parallel_entity parallel; > > > > -- > Regards, > > Laurent Pinchart -- Kind Regards, Niklas Söderlund