diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index e0a33d372c500ccd297d1cece8758d67a988dbb9..38eaa246e4c0a7b58f411ebf0308e5f9627a1038 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -20,12 +20,174 @@ #include #include #include +#include #include #include #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 */ @@ -249,12 +411,18 @@ static int rvin_digital_graph_init(struct rvin_dev *vin) 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); } /* ----------------------------------------------------------------------------- @@ -374,7 +542,9 @@ static int rcar_vin_remove(struct platform_device *pdev) 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); diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 5102ad254bff515b4b8a72c55162bf6e3f582b9b..cf5c467d45e1084774dd558463a484b94eba3a7e 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -17,6 +17,8 @@ #ifndef __RCAR_VIN__ #define __RCAR_VIN__ +#include + #include #include #include @@ -29,6 +31,11 @@ /* 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, @@ -99,6 +106,8 @@ struct rvin_info { * @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 @@ -133,6 +142,8 @@ struct rvin_dev { struct v4l2_async_notifier notifier; struct rvin_graph_entity *digital; + struct rvin_group *group; + unsigned int id; struct media_pad pad; struct mutex lock; @@ -164,6 +175,26 @@ struct rvin_dev { #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);