On Fri Jun 13 17:34:29 2025 +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
> one of the notifiers having to handle different edge cases depending on
> 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+rene...@ragnatech.se>
> Tested-by: Tomi Valkeinen <tomi.valkeinen+rene...@ideasonboard.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart+rene...@ideasonboard.com>
> Link: 
> https://lore.kernel.org/r/20250613153434.2001800-9-niklas.soderlund+rene...@ragnatech.se
> Signed-off-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
> Signed-off-by: Hans Verkuil <hverk...@xs4all.nl>

Patch committed.

Thanks,
Hans Verkuil

 .../media/platform/renesas/rcar-vin/rcar-core.c    | 256 +++++++++------------
 drivers/media/platform/renesas/rcar-vin/rcar-vin.h |   2 -
 2 files changed, 106 insertions(+), 152 deletions(-)

---

diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c 
b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
index 28070ada7f60..b3558c3c44d1 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);
+
 /* 
-----------------------------------------------------------------------------
  * Gen3 Group Allocator
  */
@@ -233,7 +236,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,
@@ -241,20 +247,32 @@ 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++) {
+               if (group->vin[i])
+                       rvin_v4l2_unregister(group->vin[i]);
+       }
 
        mutex_lock(&vin->group->lock);
 
-       for (i = 0; i < ARRAY_SIZE(vin->group->remotes); i++) {
-               if (vin->group->remotes[i].asc != asc)
+       for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+               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 (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); 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;
        }
 
        mutex_unlock(&vin->group->lock);
@@ -267,21 +285,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 (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]);
 
-       for (i = 0; i < ARRAY_SIZE(vin->group->remotes); i++) {
+               vin_dbg(group->vin[i], "Bound subdev %s\n", subdev->name);
+
+               return 0;
+       }
+
+       for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); 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;
-       }
 
-       mutex_unlock(&vin->group->lock);
+               return 0;
+       }
 
-       return 0;
+       return -ENODEV;
 }
 
 static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
@@ -371,7 +406,7 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin)
        }
 
        fwnode = fwnode_graph_get_remote_endpoint(ep);
-       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))
                return PTR_ERR(asc);
@@ -417,6 +452,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;
+
+               /* Parse shared subdevices. */
                for (id = 0; id < max_id; id++) {
                        if (vin->group->remotes[id].asc)
                                continue;
@@ -596,124 +637,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
  */
@@ -888,11 +811,52 @@ 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;
+
+       guard(mutex)(&group->lock);
+
+       /* If the group also has links don't enable the link. */
+       for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) {
+               if (group->remotes[i].subdev) {
+                       flags = 0;
+                       break;
+               }
+       }
+
+       /* Create 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;
+               int ret;
+
+               /* Nothing to do if there is no VIN or parallel subdev. */
+               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)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int rvin_csi2_setup_links(struct rvin_group *group)
 {
        const struct rvin_group_route *route;
        unsigned int id;
-       int ret = -EINVAL;
+       int ret;
+
+       ret = rvin_parallel_setup_links(group);
+       if (ret)
+               return ret;
 
        /* Create all media device links between VINs and CSI-2's. */
        mutex_lock(&group->lock);
@@ -923,9 +887,7 @@ out:
 
 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);
 }
 
@@ -946,18 +908,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:
@@ -1018,7 +973,6 @@ static int rvin_isp_setup_links(struct rvin_group *group)
 static void rvin_isp_cleanup(struct rvin_dev *vin)
 {
        rvin_group_notifier_cleanup(vin);
-       rvin_group_put(vin);
        rvin_free_controls(vin);
 }
 
@@ -1432,7 +1386,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;
@@ -1466,8 +1422,8 @@ 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_group_put(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 cb8e8fa54f96..38ae2bd20b72 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;
 

Reply via email to