As the struct media_device embeds struct media_devnode, the lifetime of that object must be that same than that of the media_device. References are obtained by media_device_get() and released by media_device_put(). In order to use refcounting, the driver must set the release callback before calling media_device_init() on the media device. Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> Acked-by: Hans Verkuil <hverkuil-cisco@xxxxxxxxx> --- drivers/media/mc/mc-device.c | 35 +++++++++++++++++++++++++++++------ drivers/media/mc/mc-devnode.c | 2 +- include/media/media-device.h | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c index f1d89d940fe1..bbc233e726d2 100644 --- a/drivers/media/mc/mc-device.c +++ b/drivers/media/mc/mc-device.c @@ -700,6 +700,31 @@ void media_device_unregister_entity_notify(struct media_device *mdev, } EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify); +static void __media_device_release(struct media_device *mdev) +{ + dev_dbg(mdev->dev, "Media device released\n"); + + ida_destroy(&mdev->entity_internal_idx); + mdev->entity_internal_idx_max = 0; + media_graph_walk_cleanup(&mdev->pm_count_walk); + mutex_destroy(&mdev->graph_mutex); + mutex_destroy(&mdev->req_queue_mutex); +} + +static void media_device_release(struct media_devnode *devnode) +{ + struct media_device *mdev = to_media_device(devnode); + + if (mdev->ops && mdev->ops->release) { + /* + * If release op isn't set, __media_device_release() is called + * via media_device_cleanup(). + */ + __media_device_release(mdev); + mdev->ops->release(mdev); + } +} + void media_device_init(struct media_device *mdev) { INIT_LIST_HEAD(&mdev->entities); @@ -713,6 +738,7 @@ void media_device_init(struct media_device *mdev) ida_init(&mdev->entity_internal_idx); atomic_set(&mdev->request_id, 0); + mdev->devnode.release = media_device_release; media_devnode_init(&mdev->devnode); if (!*mdev->bus_info) @@ -725,12 +751,9 @@ EXPORT_SYMBOL_GPL(media_device_init); void media_device_cleanup(struct media_device *mdev) { - ida_destroy(&mdev->entity_internal_idx); - mdev->entity_internal_idx_max = 0; - media_graph_walk_cleanup(&mdev->pm_count_walk); - mutex_destroy(&mdev->graph_mutex); - mutex_destroy(&mdev->req_queue_mutex); - put_device(&mdev->devnode.dev); + WARN_ON(mdev->ops && mdev->ops->release); + __media_device_release(mdev); + media_device_put(mdev); } EXPORT_SYMBOL_GPL(media_device_cleanup); diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c index 214b9b142d90..f27d5d288a38 100644 --- a/drivers/media/mc/mc-devnode.c +++ b/drivers/media/mc/mc-devnode.c @@ -200,6 +200,7 @@ static const struct file_operations media_devnode_fops = { void media_devnode_init(struct media_devnode *devnode) { device_initialize(&devnode->dev); + devnode->dev.release = media_devnode_release; } int __must_check media_devnode_register(struct media_devnode *devnode, @@ -229,7 +230,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode, devnode->dev.bus = &media_bus_type; devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); - devnode->dev.release = media_devnode_release; if (devnode->parent) devnode->dev.parent = devnode->parent; dev_set_name(&devnode->dev, "media%d", devnode->minor); diff --git a/include/media/media-device.h b/include/media/media-device.h index c791f3d5ad77..f1afbfc4dca2 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -62,6 +62,7 @@ struct media_entity_notify { * request (and thus the buffer) must be available to the driver. * And once a buffer is queued, then the driver can complete * or delete objects from the request before req_queue exits. + * @release: Release the resources of the media device. */ struct media_device_ops { int (*link_notify)(struct media_link *link, u32 flags, @@ -70,6 +71,7 @@ struct media_device_ops { void (*req_free)(struct media_request *req); int (*req_validate)(struct media_request *req); void (*req_queue)(struct media_request *req); + void (*release)(struct media_device *mdev); }; /** @@ -219,6 +221,32 @@ struct usb_device; */ void media_device_init(struct media_device *mdev); +/** + * media_device_get() - atomically increment the reference count for the media + * device + * + * Returns: the media device + * + * @mdev: media device + */ +static inline struct media_device *media_device_get(struct media_device *mdev) +{ + get_device(&mdev->devnode.dev); + + return mdev; +} + +/** + * media_device_put() - atomically decrement the reference count for the media + * device + * + * @mdev: media device + */ +static inline void media_device_put(struct media_device *mdev) +{ + put_device(&mdev->devnode.dev); +} + /** * media_device_cleanup() - Cleanups a media device element * @@ -435,6 +463,13 @@ void __media_device_usb_init(struct media_device *mdev, static inline void media_device_init(struct media_device *mdev) { } +static inline struct media_device *media_device_get(struct media_device *mdev) +{ + return NULL; +} +static inline void media_device_put(struct media_device *mdev) +{ +} static inline int media_device_register(struct media_device *mdev) { return 0; -- 2.39.2