#include <linux/of_graph.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <media/v4l2-async.h>
 #include <media/v4l2-fwnode.h>
 
 #include "rcar-vin.h"
 
+/* -----------------------------------------------------------------------------
+ * Gen3 CSI2 Group Allocator
+ */
+
+/* FIXME:  This should if we find a system that supports more
+ * than one group for the whole system be replaced with a linked
+ * list of groups. And eventually all of this should be replaced
+ * with a global device allocator API.
+ *
+ * But for now this works as on all supported systems there will
+ * be only one group for all instances.
+ */
+
+static DEFINE_MUTEX(rvin_group_lock);
+static struct rvin_group *rvin_group_data;
+
+static void rvin_group_cleanup(struct rvin_group *group)
+{
+       media_device_unregister(&group->mdev);
+       media_device_cleanup(&group->mdev);
+       mutex_destroy(&group->lock);
+}
+
+static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin)
+{
+       struct media_device *mdev = &group->mdev;
+       const struct of_device_id *match;
+       struct device_node *np;
+       int ret;
+
+       mutex_init(&group->lock);
+
+       /* Count number of VINs in the system */
+       group->count = 0;
+       for_each_matching_node(np, vin->dev->driver->of_match_table)
+               if (of_device_is_available(np))
+                       group->count++;
+
+       vin_dbg(vin, "found %u enabled VIN's in DT", group->count);
+
+       mdev->dev = vin->dev;
+
+       match = of_match_node(vin->dev->driver->of_match_table,
+                             vin->dev->of_node);
+
+       strlcpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name));
+       strlcpy(mdev->model, match->compatible, sizeof(mdev->model));
+       snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
+                dev_name(mdev->dev));
+
+       media_device_init(mdev);
+
+       ret = media_device_register(&group->mdev);
+       if (ret)
+               rvin_group_cleanup(group);
+
+       return ret;
+}
+
+static void rvin_group_release(struct kref *kref)
+{
+       struct rvin_group *group =
+               container_of(kref, struct rvin_group, refcount);
+
+       mutex_lock(&rvin_group_lock);
+
+       rvin_group_data = NULL;
+
+       rvin_group_cleanup(group);
+
+       kfree(group);
+
+       mutex_unlock(&rvin_group_lock);
+}
+
+static int rvin_group_get(struct rvin_dev *vin)
+{
+       struct rvin_group *group;
+       u32 id;
+       int ret;
+
+       /* Make sure VIN id is present and sane */
+       ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id);
+       if (ret) {
+               vin_err(vin, "%pOF: No renesas,id property found\n",
+                       vin->dev->of_node);
+               return -EINVAL;
+       }
+
+       if (id >= RCAR_VIN_NUM) {
+               vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
+                       vin->dev->of_node, id);
+               return -EINVAL;
+       }
+
+       /* Join or create a VIN group */
+       mutex_lock(&rvin_group_lock);
+       if (rvin_group_data) {
+               group = rvin_group_data;
+               kref_get(&group->refcount);
+       } else {
+               group = kzalloc(sizeof(*group), GFP_KERNEL);
+               if (!group) {
+                       ret = -ENOMEM;
+                       goto err_group;
+               }
+
+               ret = rvin_group_init(group, vin);
+               if (ret) {
+                       kfree(group);
+                       vin_err(vin, "Failed to initialize group\n");
+                       goto err_group;
+               }
+
+               kref_init(&group->refcount);
+
+               rvin_group_data = group;
+       }
+       mutex_unlock(&rvin_group_lock);
+
+       /* Add VIN to group */
+       mutex_lock(&group->lock);
+
+       if (group->vin[id]) {
+               vin_err(vin, "Duplicate renesas,id property value %u\n", id);
+               mutex_unlock(&group->lock);
+               kref_put(&group->refcount, rvin_group_release);
+               return -EINVAL;
+       }
+
+       group->vin[id] = vin;
+
+       vin->id = id;
+       vin->group = group;
+       vin->v4l2_dev.mdev = &group->mdev;
+
+       mutex_unlock(&group->lock);
+
+       return 0;
+err_group:
+       mutex_unlock(&rvin_group_lock);
+       return ret;
+}
+
+static void rvin_group_put(struct rvin_dev *vin)
+{
+       mutex_lock(&vin->group->lock);
+
+       vin->group = NULL;
+       vin->v4l2_dev.mdev = NULL;
+
+       if (WARN_ON(vin->group->vin[vin->id] != vin))
+               goto out;
+
+       vin->group->vin[vin->id] = NULL;
+out:
+       mutex_unlock(&vin->group->lock);
+
+       kref_put(&vin->group->refcount, rvin_group_release);
+}
+
 /* -----------------------------------------------------------------------------
  * Async notifier
  */
 
 static int rvin_mc_init(struct rvin_dev *vin)
 {
+       int ret;
+
        /* All our sources are CSI-2 */
        vin->mbus_cfg.type = V4L2_MBUS_CSI2;
        vin->mbus_cfg.flags = 0;
 
        vin->pad.flags = MEDIA_PAD_FL_SINK;
-       return media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
+       ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
+       if (ret)
+               return ret;
+
+       return rvin_group_get(vin);
 }
 
 /* -----------------------------------------------------------------------------
        v4l2_async_notifier_unregister(&vin->notifier);
        v4l2_async_notifier_cleanup(&vin->notifier);
 
-       if (!vin->info->use_mc)
+       if (vin->info->use_mc)
+               rvin_group_put(vin);
+       else
                v4l2_ctrl_handler_free(&vin->ctrl_handler);
 
        rvin_dma_unregister(vin);
 
 #ifndef __RCAR_VIN__
 #define __RCAR_VIN__
 
+#include <linux/kref.h>
+
 #include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-dev.h>
 /* Address alignment mask for HW buffers */
 #define HW_BUFFER_MASK 0x7f
 
+/* Max number on VIN instances that can be in a system */
+#define RCAR_VIN_NUM 8
+
+struct rvin_group;
+
 enum model_id {
        RCAR_H1,
        RCAR_M1,
  * @notifier:          V4L2 asynchronous subdevs notifier
  * @digital:           entity in the DT for local digital subdevice
  *
+ * @group:             Gen3 CSI group
+ * @id:                        Gen3 group id for this VIN
  * @pad:               media pad for the video device entity
  *
  * @lock:              protects @queue
        struct v4l2_async_notifier notifier;
        struct rvin_graph_entity *digital;
 
+       struct rvin_group *group;
+       unsigned int id;
        struct media_pad pad;
 
        struct mutex lock;
 #define vin_warn(d, fmt, arg...)       dev_warn(d->dev, fmt, ##arg)
 #define vin_err(d, fmt, arg...)                dev_err(d->dev, fmt, ##arg)
 
+/**
+ * struct rvin_group - VIN CSI2 group information
+ * @refcount:          number of VIN instances using the group
+ *
+ * @mdev:              media device which represents the group
+ *
+ * @lock:              protects the count and vin members
+ * @count:             number of enabled VIN instances found in DT
+ * @vin:               VIN instances which are part of the group
+ */
+struct rvin_group {
+       struct kref refcount;
+
+       struct media_device mdev;
+
+       struct mutex lock;
+       unsigned int count;
+       struct rvin_dev *vin[RCAR_VIN_NUM];
+};
+
 int rvin_dma_register(struct rvin_dev *vin, int irq);
 void rvin_dma_unregister(struct rvin_dev *vin);