Add support for binding and unbinding digital subdevices to rcar-vin.
On 'complete' also create direct links between the VIN instance and the
digital subdevice.

Signed-off-by: Jacopo Mondi <jacopo+rene...@jmondi.org>
---
 drivers/media/platform/rcar-vin/rcar-core.c | 133 +++++++++++++++++++++++-----
 1 file changed, 110 insertions(+), 23 deletions(-)

diff --git a/drivers/media/platform/rcar-vin/rcar-core.c 
b/drivers/media/platform/rcar-vin/rcar-core.c
index 105b6b6..93c37b0 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -168,10 +168,37 @@ static int rvin_group_link_notify(struct media_link 
*link, u32 flags,
        }
 
        /* Add the new link to the existing mask and check if it works. */
-       csi_id = rvin_group_entity_to_csi_id(group, link->source->entity);
        channel = rvin_group_csi_pad_to_channel(link->source->index);
-       mask_new = mask & rvin_group_get_mask(vin, csi_id, channel);
+       csi_id = rvin_group_entity_to_csi_id(group, link->source->entity);
+       if (csi_id == -ENODEV) {
+               struct v4l2_subdev *sd;
+               unsigned int i;
+
+               /*
+                * Make sure the source entity subdevice is registered as
+                * a digital input of one of the enabled VINs if it is not
+                * one of the CSI-2 subdevices.
+                *
+                * No hardware configuration required for digital inputs,
+                * we can return here.
+                */
+               sd = media_entity_to_v4l2_subdev(link->source->entity);
+
+               for (i = 0; i < RCAR_VIN_NUM; i++) {
+                       if (group->vin[i] && group->vin[i]->digital &&
+                           group->vin[i]->digital->subdev == sd) {
+                               ret = 0;
+                               goto out;
+                       }
+               }
 
+               vin_err(vin, "Subdevice %s not registered to any VIN\n",
+                       link->source->entity->name);
+               ret = -ENODEV;
+               goto out;
+       }
+
+       mask_new = mask & rvin_group_get_mask(vin, csi_id, channel);
        vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new);
 
        if (!mask_new) {
@@ -583,50 +610,70 @@ static int rvin_digital_graph_init(struct rvin_dev *vin)
 
 static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
 {
-       struct rvin_dev *vin = notifier_to_vin(notifier);
+       struct rvin_dev *gvin = notifier_to_vin(notifier);
        const struct rvin_group_route *route;
+       struct media_entity *source;
+       struct media_entity *sink;
        unsigned int i;
        int ret;
 
-       ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
+       ret = v4l2_device_register_subdev_nodes(&gvin->v4l2_dev);
        if (ret) {
-               vin_err(vin, "Failed to register subdev nodes\n");
+               vin_err(gvin, "Failed to register subdev nodes\n");
                return ret;
        }
 
-       /* Register all video nodes for the group. */
        for (i = 0; i < RCAR_VIN_NUM; i++) {
-               if (vin->group->vin[i]) {
-                       ret = rvin_v4l2_register(vin->group->vin[i]);
-                       if (ret)
-                               return ret;
+               struct rvin_dev *ivin;
+
+               if (!gvin->group->vin[i])
+                       continue;
+
+               /* Register all video nodes for the group. */
+               ivin = gvin->group->vin[i];
+               ret = rvin_v4l2_register(ivin);
+               if (ret)
+                       return ret;
+
+               /* Link the digital input, if any. */
+               if (!ivin->digital || !ivin->digital->subdev)
+                       continue;
+
+               source = &ivin->digital->subdev->entity;
+               sink = &ivin->vdev.entity;
+
+               ret = media_create_pad_link(source, ivin->digital->source_pad,
+                                           sink, ivin->digital->sink_pad, 0);
+               if (ret) {
+                       vin_err(gvin, "Error adding link from %s to %s\n",
+                               source->name, sink->name);
+                       return ret;
                }
        }
 
        /* Create all media device links between VINs and CSI-2's. */
-       mutex_lock(&vin->group->lock);
-       for (route = vin->info->routes; route->mask; route++) {
+       mutex_lock(&gvin->group->lock);
+       for (route = gvin->info->routes; route->mask; route++) {
                struct media_pad *source_pad, *sink_pad;
-               struct media_entity *source, *sink;
                unsigned int source_idx;
 
                /* Check that VIN is part of the group. */
-               if (!vin->group->vin[route->vin])
+               if (!gvin->group->vin[route->vin])
                        continue;
 
                /* Check that VIN' master is part of the group. */
-               if (!vin->group->vin[rvin_group_id_to_master(route->vin)])
+               if (!gvin->group->vin[rvin_group_id_to_master(route->vin)])
                        continue;
 
                /* Check that CSI-2 is part of the group. */
-               if (!vin->group->csi[route->csi].subdev)
+               if (!gvin->group->csi[route->csi].subdev)
                        continue;
 
-               source = &vin->group->csi[route->csi].subdev->entity;
+               source = &gvin->group->csi[route->csi].subdev->entity;
                source_idx = rvin_group_csi_channel_to_pad(route->channel);
                source_pad = &source->pads[source_idx];
 
-               sink = &vin->group->vin[route->vin]->vdev.entity;
+               sink = &gvin->group->vin[route->vin]->vdev.entity;
                sink_pad = &sink->pads[0];
 
                /* Skip if link already exists. */
@@ -635,12 +682,12 @@ static int rvin_group_notify_complete(struct 
v4l2_async_notifier *notifier)
 
                ret = media_create_pad_link(source, source_idx, sink, 0, 0);
                if (ret) {
-                       vin_err(vin, "Error adding link from %s to %s\n",
+                       vin_err(gvin, "Error adding link from %s to %s\n",
                                source->name, sink->name);
                        break;
                }
        }
-       mutex_unlock(&vin->group->lock);
+       mutex_unlock(&gvin->group->lock);
 
        return ret;
 }
@@ -650,6 +697,7 @@ static void rvin_group_notify_unbind(struct 
v4l2_async_notifier *notifier,
                                     struct v4l2_async_subdev *asd)
 {
        struct rvin_dev *vin = notifier_to_vin(notifier);
+       struct rvin_group *group = vin->group;
        unsigned int i;
 
        for (i = 0; i < RCAR_VIN_NUM; i++)
@@ -658,6 +706,23 @@ static void rvin_group_notify_unbind(struct 
v4l2_async_notifier *notifier,
 
        mutex_lock(&vin->group->lock);
 
+       /* Check if this is a digital subdevice first, then try with CSI-2. */
+       for (i = 0; i < RCAR_VIN_NUM; i++)
+               if (group->vin[i] && group->vin[i]->digital &&
+                   group->vin[i]->digital->asd.match.fwnode ==
+                   asd->match.fwnode)
+                       break;
+
+       if (i < RCAR_VIN_NUM) {
+               group->vin[i]->digital->subdev = NULL;
+               vin_dbg(vin, "Unbind digital subdevice %s from VIN %u\n",
+                       subdev->name, i);
+
+               mutex_unlock(&vin->group->lock);
+
+               return;
+       }
+
        for (i = 0; i < RVIN_CSI_MAX; i++) {
                if (vin->group->csi[i].fwnode != asd->match.fwnode)
                        continue;
@@ -674,14 +739,36 @@ static int rvin_group_notify_bound(struct 
v4l2_async_notifier *notifier,
                                   struct v4l2_async_subdev *asd)
 {
        struct rvin_dev *vin = notifier_to_vin(notifier);
+       struct rvin_group *group = vin->group;
        unsigned int i;
 
-       mutex_lock(&vin->group->lock);
+       mutex_lock(&group->lock);
+
+       /* Check if this is a digital subdevice first, then try with CSI-2. */
+       for (i = 0; i < RCAR_VIN_NUM; i++)
+               if (group->vin[i] && group->vin[i]->digital &&
+                   group->vin[i]->digital->asd.match.fwnode ==
+                   asd->match.fwnode)
+                       break;
+
+       if (i < RCAR_VIN_NUM) {
+               group->vin[i]->digital->subdev = subdev;
+               group->vin[i]->digital->sink_pad = RVIN_PORT_DIGITAL;
+               group->vin[i]->digital->source_pad = rvin_find_pad(subdev,
+                                                          MEDIA_PAD_FL_SOURCE);
+
+               vin_dbg(vin, "Bound digital subdevice %s to VIN %u\n",
+                       subdev->name, i);
+
+               mutex_unlock(&vin->group->lock);
+
+               return 0;
+       }
 
        for (i = 0; i < RVIN_CSI_MAX; i++) {
-               if (vin->group->csi[i].fwnode != asd->match.fwnode)
+               if (group->csi[i].fwnode != asd->match.fwnode)
                        continue;
-               vin->group->csi[i].subdev = subdev;
+               group->csi[i].subdev = subdev;
                vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i);
                break;
        }
-- 
2.7.4

Reply via email to