blob: 60a1a50b9537cca881967d620ae8e24510d7e1b2 [file] [log] [blame]
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -03001/*
2 * V4L2 asynchronous subdevice registration API
3 *
4 * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/device.h>
12#include <linux/err.h>
13#include <linux/i2c.h>
14#include <linux/list.h>
Tomasz Figa758d90e2017-06-19 00:53:43 -030015#include <linux/mm.h>
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030016#include <linux/module.h>
17#include <linux/mutex.h>
Sakari Ailusecdf0cf2016-08-16 06:54:59 -030018#include <linux/of.h>
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030019#include <linux/platform_device.h>
20#include <linux/slab.h>
21#include <linux/types.h>
22
23#include <media/v4l2-async.h>
24#include <media/v4l2-device.h>
25#include <media/v4l2-subdev.h>
26
Sakari Ailus86217652015-06-11 12:18:01 -070027static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030028{
Guennadi Liakhovetskife05e142013-06-24 05:13:51 -030029#if IS_ENABLED(CONFIG_I2C)
Sakari Ailus86217652015-06-11 12:18:01 -070030 struct i2c_client *client = i2c_verify_client(sd->dev);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030031 return client &&
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030032 asd->match.i2c.adapter_id == client->adapter->nr &&
33 asd->match.i2c.address == client->addr;
Guennadi Liakhovetskife05e142013-06-24 05:13:51 -030034#else
35 return false;
36#endif
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030037}
38
Sakari Ailus86217652015-06-11 12:18:01 -070039static bool match_devname(struct v4l2_subdev *sd,
40 struct v4l2_async_subdev *asd)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030041{
Sakari Ailus86217652015-06-11 12:18:01 -070042 return !strcmp(asd->match.device_name.name, dev_name(sd->dev));
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030043}
44
Sakari Ailusecdf0cf2016-08-16 06:54:59 -030045static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
46{
Rob Herring12f92862017-07-20 18:06:22 -040047 return sd->fwnode == asd->match.fwnode.fwnode;
Sakari Ailusecdf0cf2016-08-16 06:54:59 -030048}
49
Sakari Ailus86217652015-06-11 12:18:01 -070050static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
51{
52 if (!asd->match.custom.match)
53 /* Match always */
54 return true;
55
56 return asd->match.custom.match(sd->dev, asd);
Sylwester Nawrockie7359f82013-07-19 12:21:29 -030057}
58
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030059static LIST_HEAD(subdev_list);
60static LIST_HEAD(notifier_list);
61static DEFINE_MUTEX(list_lock);
62
63static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -030064 struct v4l2_subdev *sd)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030065{
Sakari Ailus86217652015-06-11 12:18:01 -070066 bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030067 struct v4l2_async_subdev *asd;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030068
69 list_for_each_entry(asd, &notifier->waiting, list) {
70 /* bus_type has been verified valid before */
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030071 switch (asd->match_type) {
72 case V4L2_ASYNC_MATCH_CUSTOM:
Sakari Ailus86217652015-06-11 12:18:01 -070073 match = match_custom;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030074 break;
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030075 case V4L2_ASYNC_MATCH_DEVNAME:
76 match = match_devname;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030077 break;
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030078 case V4L2_ASYNC_MATCH_I2C:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030079 match = match_i2c;
80 break;
Sakari Ailusecdf0cf2016-08-16 06:54:59 -030081 case V4L2_ASYNC_MATCH_FWNODE:
82 match = match_fwnode;
83 break;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030084 default:
85 /* Cannot happen, unless someone breaks us */
86 WARN_ON(true);
87 return NULL;
88 }
89
90 /* match cannot be NULL here */
Sakari Ailus86217652015-06-11 12:18:01 -070091 if (match(sd, asd))
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030092 return asd;
93 }
94
95 return NULL;
96}
97
98static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -030099 struct v4l2_subdev *sd,
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300100 struct v4l2_async_subdev *asd)
101{
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300102 int ret;
103
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300104 if (notifier->bound) {
105 ret = notifier->bound(notifier, sd, asd);
106 if (ret < 0)
107 return ret;
108 }
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300109
110 ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
111 if (ret < 0) {
112 if (notifier->unbind)
113 notifier->unbind(notifier, sd, asd);
114 return ret;
115 }
116
Tuukka Toivonen47b037a2017-01-27 08:32:56 -0200117 /* Remove from the waiting list */
118 list_del(&asd->list);
119 sd->asd = asd;
120 sd->notifier = notifier;
121
122 /* Move from the global subdevice list to notifier's done */
123 list_move(&sd->async_list, &notifier->done);
124
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300125 if (list_empty(&notifier->waiting) && notifier->complete)
126 return notifier->complete(notifier);
127
128 return 0;
129}
130
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300131static void v4l2_async_cleanup(struct v4l2_subdev *sd)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300132{
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300133 v4l2_device_unregister_subdev(sd);
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300134 /* Subdevice driver will reprobe and put the subdev back onto the list */
135 list_del_init(&sd->async_list);
136 sd->asd = NULL;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300137 sd->dev = NULL;
138}
139
140int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
141 struct v4l2_async_notifier *notifier)
142{
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300143 struct v4l2_subdev *sd, *tmp;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300144 struct v4l2_async_subdev *asd;
145 int i;
146
Niklas Söderlundfbf1e942017-06-13 11:30:35 -0300147 if (!v4l2_dev || !notifier->num_subdevs ||
148 notifier->num_subdevs > V4L2_MAX_SUBDEVS)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300149 return -EINVAL;
150
151 notifier->v4l2_dev = v4l2_dev;
152 INIT_LIST_HEAD(&notifier->waiting);
153 INIT_LIST_HEAD(&notifier->done);
154
155 for (i = 0; i < notifier->num_subdevs; i++) {
Sylwester Nawrockie8419d02013-07-19 12:31:10 -0300156 asd = notifier->subdevs[i];
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300157
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300158 switch (asd->match_type) {
159 case V4L2_ASYNC_MATCH_CUSTOM:
160 case V4L2_ASYNC_MATCH_DEVNAME:
161 case V4L2_ASYNC_MATCH_I2C:
Sakari Ailusecdf0cf2016-08-16 06:54:59 -0300162 case V4L2_ASYNC_MATCH_FWNODE:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300163 break;
164 default:
165 dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300166 "Invalid match type %u on %p\n",
167 asd->match_type, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300168 return -EINVAL;
169 }
170 list_add_tail(&asd->list, &notifier->waiting);
171 }
172
173 mutex_lock(&list_lock);
174
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300175 list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300176 int ret;
177
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300178 asd = v4l2_async_belongs(notifier, sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300179 if (!asd)
180 continue;
181
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300182 ret = v4l2_async_test_notify(notifier, sd, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300183 if (ret < 0) {
184 mutex_unlock(&list_lock);
185 return ret;
186 }
187 }
188
Tuukka Toivonen47b037a2017-01-27 08:32:56 -0200189 /* Keep also completed notifiers on the list */
190 list_add(&notifier->list, &notifier_list);
191
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300192 mutex_unlock(&list_lock);
193
194 return 0;
195}
196EXPORT_SYMBOL(v4l2_async_notifier_register);
197
198void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
199{
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300200 struct v4l2_subdev *sd, *tmp;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300201
Laurent Pinchart8e3fbfe2013-07-03 07:49:06 -0300202 if (!notifier->v4l2_dev)
203 return;
204
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300205 mutex_lock(&list_lock);
206
207 list_del(&notifier->list);
208
Sylwester Nawrockiceedcc42013-07-31 13:10:18 -0300209 list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300210 v4l2_async_cleanup(sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300211
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300212 if (notifier->unbind)
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300213 notifier->unbind(notifier, sd, sd->asd);
Mauro Carvalho Chehab24e9a472013-11-02 06:20:16 -0300214
Sakari Ailusde8dd7b2017-09-05 08:11:59 -0400215 list_move(&sd->async_list, &subdev_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300216 }
217
218 mutex_unlock(&list_lock);
219
Laurent Pinchart8e3fbfe2013-07-03 07:49:06 -0300220 notifier->v4l2_dev = NULL;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300221}
222EXPORT_SYMBOL(v4l2_async_notifier_unregister);
223
224int v4l2_async_register_subdev(struct v4l2_subdev *sd)
225{
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300226 struct v4l2_async_notifier *notifier;
227
Sakari Ailus86217652015-06-11 12:18:01 -0700228 /*
229 * No reference taken. The reference is held by the device
230 * (struct v4l2_subdev.dev), and async sub-device does not
231 * exist independently of the device at any point of time.
232 */
Sakari Ailus859969b2016-08-26 20:17:25 -0300233 if (!sd->fwnode && sd->dev)
234 sd->fwnode = dev_fwnode(sd->dev);
Sakari Ailus86217652015-06-11 12:18:01 -0700235
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300236 mutex_lock(&list_lock);
237
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300238 INIT_LIST_HEAD(&sd->async_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300239
240 list_for_each_entry(notifier, &notifier_list, list) {
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300241 struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300242 if (asd) {
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300243 int ret = v4l2_async_test_notify(notifier, sd, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300244 mutex_unlock(&list_lock);
245 return ret;
246 }
247 }
248
249 /* None matched, wait for hot-plugging */
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300250 list_add(&sd->async_list, &subdev_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300251
252 mutex_unlock(&list_lock);
253
254 return 0;
255}
256EXPORT_SYMBOL(v4l2_async_register_subdev);
257
258void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
259{
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300260 struct v4l2_async_notifier *notifier = sd->notifier;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300261
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300262 if (!sd->asd) {
263 if (!list_empty(&sd->async_list))
264 v4l2_async_cleanup(sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300265 return;
266 }
267
268 mutex_lock(&list_lock);
269
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300270 list_add(&sd->asd->list, &notifier->waiting);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300271
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300272 v4l2_async_cleanup(sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300273
274 if (notifier->unbind)
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300275 notifier->unbind(notifier, sd, sd->asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300276
277 mutex_unlock(&list_lock);
278}
279EXPORT_SYMBOL(v4l2_async_unregister_subdev);