diff options
author | Shuah Khan <shuah@kernel.org> | 2019-04-01 20:40:19 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+samsung@kernel.org> | 2019-04-22 11:18:26 -0400 |
commit | 6e1d824e7a1d324f7fdd276fb7133013109d3764 (patch) | |
tree | dd135d8fd4f43d2375e1716eb0b6cb0dfc055ee7 /drivers/media | |
parent | 33dfeb62e23c31619d2197850f7e8b50e8cc5466 (diff) |
media: Media Device Allocator API
Media Device Allocator API to allows multiple drivers share a media device.
This API solves a very common use-case for media devices where one physical
device (an USB stick) provides both audio and video. When such media device
exposes a standard USB Audio class, a proprietary Video class, two or more
independent drivers will share a single physical USB bridge. In such cases,
it is necessary to coordinate access to the shared resource.
Using this API, drivers can allocate a media device with the shared struct
device as the key. Once the media device is allocated by a driver, other
drivers can get a reference to it. The media device is released when all
the references are released.
Signed-off-by: Shuah Khan <shuah@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/Makefile | 6 | ||||
-rw-r--r-- | drivers/media/media-dev-allocator.c | 135 |
2 files changed, 141 insertions, 0 deletions
diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 985d35ec6b29..4a330d0e5e40 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile | |||
@@ -6,6 +6,12 @@ | |||
6 | media-objs := media-device.o media-devnode.o media-entity.o \ | 6 | media-objs := media-device.o media-devnode.o media-entity.o \ |
7 | media-request.o | 7 | media-request.o |
8 | 8 | ||
9 | ifeq ($(CONFIG_MEDIA_CONTROLLER),y) | ||
10 | ifeq ($(CONFIG_USB),y) | ||
11 | media-objs += media-dev-allocator.o | ||
12 | endif | ||
13 | endif | ||
14 | |||
9 | # | 15 | # |
10 | # I2C drivers should come before other drivers, otherwise they'll fail | 16 | # I2C drivers should come before other drivers, otherwise they'll fail |
11 | # when compiled as builtin drivers | 17 | # when compiled as builtin drivers |
diff --git a/drivers/media/media-dev-allocator.c b/drivers/media/media-dev-allocator.c new file mode 100644 index 000000000000..ae17887dec59 --- /dev/null +++ b/drivers/media/media-dev-allocator.c | |||
@@ -0,0 +1,135 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * media-dev-allocator.c - Media Controller Device Allocator API | ||
4 | * | ||
5 | * Copyright (c) 2019 Shuah Khan <shuah@kernel.org> | ||
6 | * | ||
7 | * Credits: Suggested by Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * This file adds a global refcounted Media Controller Device Instance API. | ||
12 | * A system wide global media device list is managed and each media device | ||
13 | * includes a kref count. The last put on the media device releases the media | ||
14 | * device instance. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/kref.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/usb.h> | ||
22 | |||
23 | #include <media/media-device.h> | ||
24 | #include <media/media-dev-allocator.h> | ||
25 | |||
26 | static LIST_HEAD(media_device_list); | ||
27 | static DEFINE_MUTEX(media_device_lock); | ||
28 | |||
29 | struct media_device_instance { | ||
30 | struct media_device mdev; | ||
31 | struct module *owner; | ||
32 | struct list_head list; | ||
33 | struct kref refcount; | ||
34 | }; | ||
35 | |||
36 | static inline struct media_device_instance * | ||
37 | to_media_device_instance(struct media_device *mdev) | ||
38 | { | ||
39 | return container_of(mdev, struct media_device_instance, mdev); | ||
40 | } | ||
41 | |||
42 | static void media_device_instance_release(struct kref *kref) | ||
43 | { | ||
44 | struct media_device_instance *mdi = | ||
45 | container_of(kref, struct media_device_instance, refcount); | ||
46 | |||
47 | dev_dbg(mdi->mdev.dev, "%s: releasing Media Device\n", __func__); | ||
48 | |||
49 | mutex_lock(&media_device_lock); | ||
50 | |||
51 | media_device_unregister(&mdi->mdev); | ||
52 | media_device_cleanup(&mdi->mdev); | ||
53 | |||
54 | list_del(&mdi->list); | ||
55 | mutex_unlock(&media_device_lock); | ||
56 | |||
57 | kfree(mdi); | ||
58 | } | ||
59 | |||
60 | /* Callers should hold media_device_lock when calling this function */ | ||
61 | static struct media_device *__media_device_get(struct device *dev, | ||
62 | const char *module_name, | ||
63 | struct module *owner) | ||
64 | { | ||
65 | struct media_device_instance *mdi; | ||
66 | |||
67 | list_for_each_entry(mdi, &media_device_list, list) { | ||
68 | if (mdi->mdev.dev != dev) | ||
69 | continue; | ||
70 | |||
71 | kref_get(&mdi->refcount); | ||
72 | |||
73 | /* get module reference for the media_device owner */ | ||
74 | if (owner != mdi->owner && !try_module_get(mdi->owner)) | ||
75 | dev_err(dev, | ||
76 | "%s: module %s get owner reference error\n", | ||
77 | __func__, module_name); | ||
78 | else | ||
79 | dev_dbg(dev, "%s: module %s got owner reference\n", | ||
80 | __func__, module_name); | ||
81 | return &mdi->mdev; | ||
82 | } | ||
83 | |||
84 | mdi = kzalloc(sizeof(*mdi), GFP_KERNEL); | ||
85 | if (!mdi) | ||
86 | return NULL; | ||
87 | |||
88 | mdi->owner = owner; | ||
89 | kref_init(&mdi->refcount); | ||
90 | list_add_tail(&mdi->list, &media_device_list); | ||
91 | |||
92 | dev_dbg(dev, "%s: Allocated media device for owner %s\n", | ||
93 | __func__, module_name); | ||
94 | return &mdi->mdev; | ||
95 | } | ||
96 | |||
97 | struct media_device *media_device_usb_allocate(struct usb_device *udev, | ||
98 | const char *module_name, | ||
99 | struct module *owner) | ||
100 | { | ||
101 | struct media_device *mdev; | ||
102 | |||
103 | mutex_lock(&media_device_lock); | ||
104 | mdev = __media_device_get(&udev->dev, module_name, owner); | ||
105 | if (!mdev) { | ||
106 | mutex_unlock(&media_device_lock); | ||
107 | return ERR_PTR(-ENOMEM); | ||
108 | } | ||
109 | |||
110 | /* check if media device is already initialized */ | ||
111 | if (!mdev->dev) | ||
112 | __media_device_usb_init(mdev, udev, udev->product, | ||
113 | module_name); | ||
114 | mutex_unlock(&media_device_lock); | ||
115 | return mdev; | ||
116 | } | ||
117 | EXPORT_SYMBOL_GPL(media_device_usb_allocate); | ||
118 | |||
119 | void media_device_delete(struct media_device *mdev, const char *module_name, | ||
120 | struct module *owner) | ||
121 | { | ||
122 | struct media_device_instance *mdi = to_media_device_instance(mdev); | ||
123 | |||
124 | mutex_lock(&media_device_lock); | ||
125 | /* put module reference for the media_device owner */ | ||
126 | if (mdi->owner != owner) { | ||
127 | module_put(mdi->owner); | ||
128 | dev_dbg(mdi->mdev.dev, | ||
129 | "%s: module %s put owner module reference\n", | ||
130 | __func__, module_name); | ||
131 | } | ||
132 | mutex_unlock(&media_device_lock); | ||
133 | kref_put(&mdi->refcount, media_device_instance_release); | ||
134 | } | ||
135 | EXPORT_SYMBOL_GPL(media_device_delete); | ||