blob: cde2cf2ab4b0da4129962e20040c96dffc57b632 [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 return 0;
126}
127
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300128static void v4l2_async_cleanup(struct v4l2_subdev *sd)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300129{
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300130 v4l2_device_unregister_subdev(sd);
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300131 /* Subdevice driver will reprobe and put the subdev back onto the list */
132 list_del_init(&sd->async_list);
133 sd->asd = NULL;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300134}
135
Sakari Ailusfb45f432017-10-02 06:24:54 -0400136static void v4l2_async_notifier_unbind_all_subdevs(
137 struct v4l2_async_notifier *notifier)
138{
139 struct v4l2_subdev *sd, *tmp;
140
141 list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
142 if (notifier->unbind)
143 notifier->unbind(notifier, sd, sd->asd);
144
145 v4l2_async_cleanup(sd);
146
147 list_move(&sd->async_list, &subdev_list);
148 }
149}
150
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300151int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
152 struct v4l2_async_notifier *notifier)
153{
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300154 struct v4l2_subdev *sd, *tmp;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300155 struct v4l2_async_subdev *asd;
Sakari Ailusfb45f432017-10-02 06:24:54 -0400156 int ret;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300157 int i;
158
Niklas Söderlundfbf1e942017-06-13 11:30:35 -0300159 if (!v4l2_dev || !notifier->num_subdevs ||
160 notifier->num_subdevs > V4L2_MAX_SUBDEVS)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300161 return -EINVAL;
162
163 notifier->v4l2_dev = v4l2_dev;
164 INIT_LIST_HEAD(&notifier->waiting);
165 INIT_LIST_HEAD(&notifier->done);
166
167 for (i = 0; i < notifier->num_subdevs; i++) {
Sylwester Nawrockie8419d02013-07-19 12:31:10 -0300168 asd = notifier->subdevs[i];
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300169
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300170 switch (asd->match_type) {
171 case V4L2_ASYNC_MATCH_CUSTOM:
172 case V4L2_ASYNC_MATCH_DEVNAME:
173 case V4L2_ASYNC_MATCH_I2C:
Sakari Ailusecdf0cf2016-08-16 06:54:59 -0300174 case V4L2_ASYNC_MATCH_FWNODE:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300175 break;
176 default:
177 dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300178 "Invalid match type %u on %p\n",
179 asd->match_type, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300180 return -EINVAL;
181 }
182 list_add_tail(&asd->list, &notifier->waiting);
183 }
184
185 mutex_lock(&list_lock);
186
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300187 list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300188 int ret;
189
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300190 asd = v4l2_async_belongs(notifier, sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300191 if (!asd)
192 continue;
193
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300194 ret = v4l2_async_test_notify(notifier, sd, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300195 if (ret < 0) {
196 mutex_unlock(&list_lock);
197 return ret;
198 }
199 }
200
Sakari Ailusfb45f432017-10-02 06:24:54 -0400201 if (list_empty(&notifier->waiting) && notifier->complete) {
202 ret = notifier->complete(notifier);
203 if (ret)
204 goto err_complete;
205 }
206
Tuukka Toivonen47b037a2017-01-27 08:32:56 -0200207 /* Keep also completed notifiers on the list */
208 list_add(&notifier->list, &notifier_list);
209
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300210 mutex_unlock(&list_lock);
211
212 return 0;
Sakari Ailusfb45f432017-10-02 06:24:54 -0400213
214err_complete:
215 v4l2_async_notifier_unbind_all_subdevs(notifier);
216
217 mutex_unlock(&list_lock);
218
219 return ret;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300220}
221EXPORT_SYMBOL(v4l2_async_notifier_register);
222
223void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
224{
Laurent Pinchart8e3fbfe2013-07-03 07:49:06 -0300225 if (!notifier->v4l2_dev)
226 return;
227
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300228 mutex_lock(&list_lock);
229
230 list_del(&notifier->list);
231
Sakari Ailusfb45f432017-10-02 06:24:54 -0400232 v4l2_async_notifier_unbind_all_subdevs(notifier);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300233
234 mutex_unlock(&list_lock);
235
Laurent Pinchart8e3fbfe2013-07-03 07:49:06 -0300236 notifier->v4l2_dev = NULL;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300237}
238EXPORT_SYMBOL(v4l2_async_notifier_unregister);
239
240int v4l2_async_register_subdev(struct v4l2_subdev *sd)
241{
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300242 struct v4l2_async_notifier *notifier;
Sakari Ailusfb45f432017-10-02 06:24:54 -0400243 int ret;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300244
Sakari Ailus86217652015-06-11 12:18:01 -0700245 /*
246 * No reference taken. The reference is held by the device
247 * (struct v4l2_subdev.dev), and async sub-device does not
248 * exist independently of the device at any point of time.
249 */
Sakari Ailus859969b2016-08-26 20:17:25 -0300250 if (!sd->fwnode && sd->dev)
251 sd->fwnode = dev_fwnode(sd->dev);
Sakari Ailus86217652015-06-11 12:18:01 -0700252
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300253 mutex_lock(&list_lock);
254
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300255 INIT_LIST_HEAD(&sd->async_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300256
257 list_for_each_entry(notifier, &notifier_list, list) {
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300258 struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
Sakari Ailusfb45f432017-10-02 06:24:54 -0400259 int ret;
260
261 if (!asd)
262 continue;
263
264 ret = v4l2_async_test_notify(notifier, sd, asd);
265 if (ret)
266 goto err_unlock;
267
268 if (!list_empty(&notifier->waiting) || !notifier->complete)
269 goto out_unlock;
270
271 ret = notifier->complete(notifier);
272 if (ret)
273 goto err_cleanup;
274
275 goto out_unlock;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300276 }
277
278 /* None matched, wait for hot-plugging */
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300279 list_add(&sd->async_list, &subdev_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300280
Sakari Ailusfb45f432017-10-02 06:24:54 -0400281out_unlock:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300282 mutex_unlock(&list_lock);
283
284 return 0;
Sakari Ailusfb45f432017-10-02 06:24:54 -0400285
286err_cleanup:
287 if (notifier->unbind)
288 notifier->unbind(notifier, sd, sd->asd);
289
290 v4l2_async_cleanup(sd);
291
292err_unlock:
293 mutex_unlock(&list_lock);
294
295 return ret;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300296}
297EXPORT_SYMBOL(v4l2_async_register_subdev);
298
299void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
300{
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300301 mutex_lock(&list_lock);
302
Sakari Ailus7fc4fdb2017-10-03 02:26:32 -0400303 if (sd->asd) {
304 struct v4l2_async_notifier *notifier = sd->notifier;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300305
Sakari Ailus7fc4fdb2017-10-03 02:26:32 -0400306 list_add(&sd->asd->list, &notifier->waiting);
307
308 if (notifier->unbind)
309 notifier->unbind(notifier, sd, sd->asd);
310 }
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300311
Niklas Söderlund633d1852017-10-02 16:16:52 -0400312 v4l2_async_cleanup(sd);
313
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300314 mutex_unlock(&list_lock);
315}
316EXPORT_SYMBOL(v4l2_async_unregister_subdev);