v4l2-async.c 7.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * V4L2 asynchronous subdevice registration API
 *
 * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/list.h>
15
#include <linux/mm.h>
16 17
#include <linux/module.h>
#include <linux/mutex.h>
18
#include <linux/of.h>
19 20 21 22 23 24
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>

#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
25
#include <media/v4l2-fwnode.h>
26 27
#include <media/v4l2-subdev.h>

28
static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
29
{
30
#if IS_ENABLED(CONFIG_I2C)
31
	struct i2c_client *client = i2c_verify_client(sd->dev);
32 33 34
	return client &&
		asd->match.i2c.adapter_id == client->adapter->nr &&
		asd->match.i2c.address == client->addr;
35 36 37
#else
	return false;
#endif
38 39
}

40 41
static bool match_devname(struct v4l2_subdev *sd,
			  struct v4l2_async_subdev *asd)
42
{
43
	return !strcmp(asd->match.device_name.name, dev_name(sd->dev));
44 45
}

46 47
static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
48
	return sd->fwnode == asd->match.fwnode.fwnode;
49 50
}

51 52 53 54 55 56 57
static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
	if (!asd->match.custom.match)
		/* Match always */
		return true;

	return asd->match.custom.match(sd->dev, asd);
58 59
}

60 61 62 63
static LIST_HEAD(subdev_list);
static LIST_HEAD(notifier_list);
static DEFINE_MUTEX(list_lock);

64 65
static struct v4l2_async_subdev *v4l2_async_find_match(
	struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd)
66
{
67
	bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
68 69 70 71
	struct v4l2_async_subdev *asd;

	list_for_each_entry(asd, &notifier->waiting, list) {
		/* bus_type has been verified valid before */
72 73
		switch (asd->match_type) {
		case V4L2_ASYNC_MATCH_CUSTOM:
74
			match = match_custom;
75
			break;
76 77
		case V4L2_ASYNC_MATCH_DEVNAME:
			match = match_devname;
78
			break;
79
		case V4L2_ASYNC_MATCH_I2C:
80 81
			match = match_i2c;
			break;
82 83 84
		case V4L2_ASYNC_MATCH_FWNODE:
			match = match_fwnode;
			break;
85 86 87 88 89 90 91
		default:
			/* Cannot happen, unless someone breaks us */
			WARN_ON(true);
			return NULL;
		}

		/* match cannot be NULL here */
92
		if (match(sd, asd))
93 94 95 96 97 98
			return asd;
	}

	return NULL;
}

99 100 101
static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
				   struct v4l2_subdev *sd,
				   struct v4l2_async_subdev *asd)
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
{
	int ret;

	if (notifier->bound) {
		ret = notifier->bound(notifier, sd, asd);
		if (ret < 0)
			return ret;
	}

	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
	if (ret < 0) {
		if (notifier->unbind)
			notifier->unbind(notifier, sd, asd);
		return ret;
	}

118 119 120 121 122 123 124 125
	/* Remove from the waiting list */
	list_del(&asd->list);
	sd->asd = asd;
	sd->notifier = notifier;

	/* Move from the global subdevice list to notifier's done */
	list_move(&sd->async_list, &notifier->done);

126 127 128
	return 0;
}

129
static void v4l2_async_cleanup(struct v4l2_subdev *sd)
130 131
{
	v4l2_device_unregister_subdev(sd);
132 133 134
	/* Subdevice driver will reprobe and put the subdev back onto the list */
	list_del_init(&sd->async_list);
	sd->asd = NULL;
135 136
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
static void v4l2_async_notifier_unbind_all_subdevs(
	struct v4l2_async_notifier *notifier)
{
	struct v4l2_subdev *sd, *tmp;

	list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
		if (notifier->unbind)
			notifier->unbind(notifier, sd, sd->asd);

		v4l2_async_cleanup(sd);

		list_move(&sd->async_list, &subdev_list);
	}
}

152 153 154
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
				 struct v4l2_async_notifier *notifier)
{
155
	struct v4l2_subdev *sd, *tmp;
156
	struct v4l2_async_subdev *asd;
157
	int ret;
158 159
	int i;

160 161
	if (!v4l2_dev || !notifier->num_subdevs ||
	    notifier->num_subdevs > V4L2_MAX_SUBDEVS)
162 163 164 165 166 167 168
		return -EINVAL;

	notifier->v4l2_dev = v4l2_dev;
	INIT_LIST_HEAD(&notifier->waiting);
	INIT_LIST_HEAD(&notifier->done);

	for (i = 0; i < notifier->num_subdevs; i++) {
169
		asd = notifier->subdevs[i];
170

171 172 173 174
		switch (asd->match_type) {
		case V4L2_ASYNC_MATCH_CUSTOM:
		case V4L2_ASYNC_MATCH_DEVNAME:
		case V4L2_ASYNC_MATCH_I2C:
175
		case V4L2_ASYNC_MATCH_FWNODE:
176 177 178
			break;
		default:
			dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
179 180
				"Invalid match type %u on %p\n",
				asd->match_type, asd);
181 182 183 184 185 186 187
			return -EINVAL;
		}
		list_add_tail(&asd->list, &notifier->waiting);
	}

	mutex_lock(&list_lock);

188
	list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
189 190
		int ret;

191
		asd = v4l2_async_find_match(notifier, sd);
192 193 194
		if (!asd)
			continue;

195
		ret = v4l2_async_match_notify(notifier, sd, asd);
196 197 198 199 200 201
		if (ret < 0) {
			mutex_unlock(&list_lock);
			return ret;
		}
	}

202 203 204 205 206 207
	if (list_empty(&notifier->waiting) && notifier->complete) {
		ret = notifier->complete(notifier);
		if (ret)
			goto err_complete;
	}

208 209 210
	/* Keep also completed notifiers on the list */
	list_add(&notifier->list, &notifier_list);

211 212 213
	mutex_unlock(&list_lock);

	return 0;
214 215 216 217 218 219 220

err_complete:
	v4l2_async_notifier_unbind_all_subdevs(notifier);

	mutex_unlock(&list_lock);

	return ret;
221 222 223 224 225
}
EXPORT_SYMBOL(v4l2_async_notifier_register);

void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
{
226 227 228
	if (!notifier->v4l2_dev)
		return;

229 230 231 232
	mutex_lock(&list_lock);

	list_del(&notifier->list);

233
	v4l2_async_notifier_unbind_all_subdevs(notifier);
234 235 236

	mutex_unlock(&list_lock);

237
	notifier->v4l2_dev = NULL;
238 239 240
}
EXPORT_SYMBOL(v4l2_async_notifier_unregister);

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier)
{
	unsigned int i;

	if (!notifier->max_subdevs)
		return;

	for (i = 0; i < notifier->num_subdevs; i++) {
		struct v4l2_async_subdev *asd = notifier->subdevs[i];

		switch (asd->match_type) {
		case V4L2_ASYNC_MATCH_FWNODE:
			fwnode_handle_put(asd->match.fwnode.fwnode);
			break;
		default:
			WARN_ON_ONCE(true);
			break;
		}

		kfree(asd);
	}

	notifier->max_subdevs = 0;
	notifier->num_subdevs = 0;

	kvfree(notifier->subdevs);
	notifier->subdevs = NULL;
}
EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup);

271 272 273
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
	struct v4l2_async_notifier *notifier;
274
	int ret;
275

276 277 278 279 280
	/*
	 * No reference taken. The reference is held by the device
	 * (struct v4l2_subdev.dev), and async sub-device does not
	 * exist independently of the device at any point of time.
	 */
281 282
	if (!sd->fwnode && sd->dev)
		sd->fwnode = dev_fwnode(sd->dev);
283

284 285
	mutex_lock(&list_lock);

286
	INIT_LIST_HEAD(&sd->async_list);
287 288

	list_for_each_entry(notifier, &notifier_list, list) {
289 290
		struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier,
								      sd);
291 292 293 294 295
		int ret;

		if (!asd)
			continue;

296
		ret = v4l2_async_match_notify(notifier, sd, asd);
297 298 299 300 301 302 303 304 305 306 307
		if (ret)
			goto err_unlock;

		if (!list_empty(&notifier->waiting) || !notifier->complete)
			goto out_unlock;

		ret = notifier->complete(notifier);
		if (ret)
			goto err_cleanup;

		goto out_unlock;
308 309 310
	}

	/* None matched, wait for hot-plugging */
311
	list_add(&sd->async_list, &subdev_list);
312

313
out_unlock:
314 315 316
	mutex_unlock(&list_lock);

	return 0;
317 318 319 320 321 322 323 324 325 326 327

err_cleanup:
	if (notifier->unbind)
		notifier->unbind(notifier, sd, sd->asd);

	v4l2_async_cleanup(sd);

err_unlock:
	mutex_unlock(&list_lock);

	return ret;
328 329 330 331 332 333 334
}
EXPORT_SYMBOL(v4l2_async_register_subdev);

void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
{
	mutex_lock(&list_lock);

335 336
	if (sd->asd) {
		struct v4l2_async_notifier *notifier = sd->notifier;
337

338 339 340 341 342
		list_add(&sd->asd->list, &notifier->waiting);

		if (notifier->unbind)
			notifier->unbind(notifier, sd, sd->asd);
	}
343

344 345
	v4l2_async_cleanup(sd);

346 347 348
	mutex_unlock(&list_lock);
}
EXPORT_SYMBOL(v4l2_async_unregister_subdev);