blob: ca281438a0ae5519b07e8660aac3958698b936ab [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}
138
139int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
140 struct v4l2_async_notifier *notifier)
141{
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300142 struct v4l2_subdev *sd, *tmp;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300143 struct v4l2_async_subdev *asd;
144 int i;
145
Niklas Söderlundfbf1e942017-06-13 11:30:35 -0300146 if (!v4l2_dev || !notifier->num_subdevs ||
147 notifier->num_subdevs > V4L2_MAX_SUBDEVS)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300148 return -EINVAL;
149
150 notifier->v4l2_dev = v4l2_dev;
151 INIT_LIST_HEAD(&notifier->waiting);
152 INIT_LIST_HEAD(&notifier->done);
153
154 for (i = 0; i < notifier->num_subdevs; i++) {
Sylwester Nawrockie8419d02013-07-19 12:31:10 -0300155 asd = notifier->subdevs[i];
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300156
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300157 switch (asd->match_type) {
158 case V4L2_ASYNC_MATCH_CUSTOM:
159 case V4L2_ASYNC_MATCH_DEVNAME:
160 case V4L2_ASYNC_MATCH_I2C:
Sakari Ailusecdf0cf2016-08-16 06:54:59 -0300161 case V4L2_ASYNC_MATCH_FWNODE:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300162 break;
163 default:
164 dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300165 "Invalid match type %u on %p\n",
166 asd->match_type, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300167 return -EINVAL;
168 }
169 list_add_tail(&asd->list, &notifier->waiting);
170 }
171
172 mutex_lock(&list_lock);
173
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300174 list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300175 int ret;
176
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300177 asd = v4l2_async_belongs(notifier, sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300178 if (!asd)
179 continue;
180
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300181 ret = v4l2_async_test_notify(notifier, sd, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300182 if (ret < 0) {
183 mutex_unlock(&list_lock);
184 return ret;
185 }
186 }
187
Tuukka Toivonen47b037a2017-01-27 08:32:56 -0200188 /* Keep also completed notifiers on the list */
189 list_add(&notifier->list, &notifier_list);
190
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300191 mutex_unlock(&list_lock);
192
193 return 0;
194}
195EXPORT_SYMBOL(v4l2_async_notifier_register);
196
197void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
198{
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300199 struct v4l2_subdev *sd, *tmp;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300200
Laurent Pinchart8e3fbfe2013-07-03 07:49:06 -0300201 if (!notifier->v4l2_dev)
202 return;
203
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300204 mutex_lock(&list_lock);
205
206 list_del(&notifier->list);
207
Sylwester Nawrockiceedcc42013-07-31 13:10:18 -0300208 list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300209 if (notifier->unbind)
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300210 notifier->unbind(notifier, sd, sd->asd);
Mauro Carvalho Chehab24e9a472013-11-02 06:20:16 -0300211
Niklas Söderlund633d1852017-10-02 16:16:52 -0400212 v4l2_async_cleanup(sd);
213
Sakari Ailusde8dd7b2017-09-05 08:11:59 -0400214 list_move(&sd->async_list, &subdev_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300215 }
216
217 mutex_unlock(&list_lock);
218
Laurent Pinchart8e3fbfe2013-07-03 07:49:06 -0300219 notifier->v4l2_dev = NULL;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300220}
221EXPORT_SYMBOL(v4l2_async_notifier_unregister);
222
223int v4l2_async_register_subdev(struct v4l2_subdev *sd)
224{
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300225 struct v4l2_async_notifier *notifier;
226
Sakari Ailus86217652015-06-11 12:18:01 -0700227 /*
228 * No reference taken. The reference is held by the device
229 * (struct v4l2_subdev.dev), and async sub-device does not
230 * exist independently of the device at any point of time.
231 */
Sakari Ailus859969b2016-08-26 20:17:25 -0300232 if (!sd->fwnode && sd->dev)
233 sd->fwnode = dev_fwnode(sd->dev);
Sakari Ailus86217652015-06-11 12:18:01 -0700234
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300235 mutex_lock(&list_lock);
236
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300237 INIT_LIST_HEAD(&sd->async_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300238
239 list_for_each_entry(notifier, &notifier_list, list) {
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300240 struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300241 if (asd) {
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300242 int ret = v4l2_async_test_notify(notifier, sd, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300243 mutex_unlock(&list_lock);
244 return ret;
245 }
246 }
247
248 /* None matched, wait for hot-plugging */
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300249 list_add(&sd->async_list, &subdev_list);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300250
251 mutex_unlock(&list_lock);
252
253 return 0;
254}
255EXPORT_SYMBOL(v4l2_async_register_subdev);
256
257void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
258{
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300259 struct v4l2_async_notifier *notifier = sd->notifier;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300260
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300261 if (!sd->asd) {
262 if (!list_empty(&sd->async_list))
263 v4l2_async_cleanup(sd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300264 return;
265 }
266
267 mutex_lock(&list_lock);
268
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300269 list_add(&sd->asd->list, &notifier->waiting);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300270
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300271 if (notifier->unbind)
Sylwester Nawrockib426b3a2013-07-22 08:01:33 -0300272 notifier->unbind(notifier, sd, sd->asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300273
Niklas Söderlund633d1852017-10-02 16:16:52 -0400274 v4l2_async_cleanup(sd);
275
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300276 mutex_unlock(&list_lock);
277}
278EXPORT_SYMBOL(v4l2_async_unregister_subdev);