11590Srgrimes// SPDX-License-Identifier: GPL-2.0
21590Srgrimes/*
31590Srgrimes * media-dev-allocator.c - Media Controller Device Allocator API
41590Srgrimes *
51590Srgrimes * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>
61590Srgrimes *
71590Srgrimes * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com>
81590Srgrimes */
91590Srgrimes
101590Srgrimes/*
111590Srgrimes * This file adds a global refcounted Media Controller Device Instance API.
121590Srgrimes * A system wide global media device list is managed and each media device
131590Srgrimes * includes a kref count. The last put on the media device releases the media
141590Srgrimes * device instance.
151590Srgrimes *
161590Srgrimes */
171590Srgrimes
181590Srgrimes#include <linux/kref.h>
191590Srgrimes#include <linux/module.h>
201590Srgrimes#include <linux/slab.h>
211590Srgrimes#include <linux/usb.h>
221590Srgrimes
231590Srgrimes#include <media/media-device.h>
241590Srgrimes#include <media/media-dev-allocator.h>
251590Srgrimes
261590Srgrimesstatic LIST_HEAD(media_device_list);
271590Srgrimesstatic DEFINE_MUTEX(media_device_lock);
281590Srgrimes
291590Srgrimesstruct media_device_instance {
301590Srgrimes	struct media_device mdev;
311590Srgrimes	struct module *owner;
321590Srgrimes	struct list_head list;
331590Srgrimes	struct kref refcount;
341590Srgrimes};
351590Srgrimes
361590Srgrimesstatic inline struct media_device_instance *
371590Srgrimesto_media_device_instance(struct media_device *mdev)
381590Srgrimes{
3928066Scharnier	return container_of(mdev, struct media_device_instance, mdev);
401590Srgrimes}
411590Srgrimes
421590Srgrimesstatic void media_device_instance_release(struct kref *kref)
431590Srgrimes{
441590Srgrimes	struct media_device_instance *mdi =
4528066Scharnier		container_of(kref, struct media_device_instance, refcount);
461590Srgrimes
4728066Scharnier	dev_dbg(mdi->mdev.dev, "%s: releasing Media Device\n", __func__);
4828066Scharnier
4950477Speter	mutex_lock(&media_device_lock);
501590Srgrimes
511590Srgrimes	media_device_unregister(&mdi->mdev);
521590Srgrimes	media_device_cleanup(&mdi->mdev);
531590Srgrimes
5428066Scharnier	list_del(&mdi->list);
551590Srgrimes	mutex_unlock(&media_device_lock);
561590Srgrimes
5717522Sache	kfree(mdi);
581590Srgrimes}
591590Srgrimes
601590Srgrimes/* Callers should hold media_device_lock when calling this function */
611590Srgrimesstatic struct media_device *__media_device_get(struct device *dev,
621590Srgrimes						const char *module_name,
631590Srgrimes						struct module *owner)
641590Srgrimes{
651590Srgrimes	struct media_device_instance *mdi;
661590Srgrimes
671590Srgrimes	list_for_each_entry(mdi, &media_device_list, list) {
681590Srgrimes		if (mdi->mdev.dev != dev)
691590Srgrimes			continue;
701590Srgrimes
711590Srgrimes		kref_get(&mdi->refcount);
721590Srgrimes
731590Srgrimes		/* get module reference for the media_device owner */
741590Srgrimes		if (owner != mdi->owner && !try_module_get(mdi->owner))
751590Srgrimes			dev_err(dev,
761590Srgrimes				"%s: module %s get owner reference error\n",
771590Srgrimes					__func__, module_name);
781590Srgrimes		else
791590Srgrimes			dev_dbg(dev, "%s: module %s got owner reference\n",
801590Srgrimes					__func__, module_name);
811590Srgrimes		return &mdi->mdev;
821590Srgrimes	}
831590Srgrimes
841590Srgrimes	mdi = kzalloc(sizeof(*mdi), GFP_KERNEL);
851590Srgrimes	if (!mdi)
861590Srgrimes		return NULL;
871590Srgrimes
881590Srgrimes	mdi->owner = owner;
891590Srgrimes	kref_init(&mdi->refcount);
901590Srgrimes	list_add_tail(&mdi->list, &media_device_list);
911590Srgrimes
921590Srgrimes	dev_dbg(dev, "%s: Allocated media device for owner %s\n",
931590Srgrimes			__func__, module_name);
941590Srgrimes	return &mdi->mdev;
951590Srgrimes}
961590Srgrimes
971590Srgrimesstruct media_device *media_device_usb_allocate(struct usb_device *udev,
9858309Sgreen					       const char *module_name,
991590Srgrimes					       struct module *owner)
1001590Srgrimes{
1011590Srgrimes	struct media_device *mdev;
1021590Srgrimes
1031590Srgrimes	mutex_lock(&media_device_lock);
1041590Srgrimes	mdev = __media_device_get(&udev->dev, module_name, owner);
1051590Srgrimes	if (!mdev) {
1061590Srgrimes		mutex_unlock(&media_device_lock);
1071590Srgrimes		return ERR_PTR(-ENOMEM);
1081590Srgrimes	}
1091590Srgrimes
11028066Scharnier	/* check if media device is already initialized */
1111590Srgrimes	if (!mdev->dev)
1121590Srgrimes		__media_device_usb_init(mdev, udev, udev->product,
1131590Srgrimes					module_name);
1141590Srgrimes	mutex_unlock(&media_device_lock);
1151590Srgrimes	return mdev;
1161590Srgrimes}
1171590SrgrimesEXPORT_SYMBOL_GPL(media_device_usb_allocate);
11860394Snsayer
1191590Srgrimesvoid media_device_delete(struct media_device *mdev, const char *module_name,
12017522Sache			 struct module *owner)
12117522Sache{
1221590Srgrimes	struct media_device_instance *mdi = to_media_device_instance(mdev);
12358309Sgreen
1241590Srgrimes	mutex_lock(&media_device_lock);
12558309Sgreen	/* put module reference for the media_device owner */
12658309Sgreen	if (mdi->owner != owner) {
12758309Sgreen		module_put(mdi->owner);
1281590Srgrimes		dev_dbg(mdi->mdev.dev,
1291590Srgrimes			"%s: module %s put owner module reference\n",
1301590Srgrimes			__func__, module_name);
1311590Srgrimes	}
1321590Srgrimes	mutex_unlock(&media_device_lock);
13360394Snsayer	kref_put(&mdi->refcount, media_device_instance_release);
13460394Snsayer}
13560394SnsayerEXPORT_SYMBOL_GPL(media_device_delete);
13660394Snsayer