Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cab00bb authored by Sakari Ailus's avatar Sakari Ailus Committed by Mauro Carvalho Chehab
Browse files

media: v4l: async: Allow binding notifiers to sub-devices



Registering a notifier has required the knowledge of struct v4l2_device
for the reason that sub-devices generally are registered to the
v4l2_device (as well as the media device, also available through
v4l2_device).

This information is not available for sub-device drivers at probe time.

What this patch does is that it allows registering notifiers without
having v4l2_device around. Instead the sub-device pointer is stored in the
notifier. Once the sub-device of the driver that registered the notifier
is registered, the notifier will gain the knowledge of the v4l2_device,
and the binding of async sub-devices from the sub-device driver's notifier
may proceed.

The complete callback of the root notifier will be called only when the
v4l2_device is available and no notifier has pending sub-devices to bind.
No complete callbacks are supported for sub-device notifiers.

Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Acked-by: default avatarNiklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Reviewed-by: default avatarSebastian Reichel <sebastian.reichel@collabora.co.uk>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent a3620cb4
Loading
Loading
Loading
Loading
+172 −40
Original line number Diff line number Diff line
@@ -124,11 +124,87 @@ static struct v4l2_async_subdev *v4l2_async_find_match(
	return NULL;
}

/* Find the sub-device notifier registered by a sub-device driver. */
static struct v4l2_async_notifier *v4l2_async_find_subdev_notifier(
	struct v4l2_subdev *sd)
{
	struct v4l2_async_notifier *n;

	list_for_each_entry(n, &notifier_list, list)
		if (n->sd == sd)
			return n;

	return NULL;
}

/* Get v4l2_device related to the notifier if one can be found. */
static struct v4l2_device *v4l2_async_notifier_find_v4l2_dev(
	struct v4l2_async_notifier *notifier)
{
	while (notifier->parent)
		notifier = notifier->parent;

	return notifier->v4l2_dev;
}

/*
 * Return true if all child sub-device notifiers are complete, false otherwise.
 */
static bool v4l2_async_notifier_can_complete(
	struct v4l2_async_notifier *notifier)
{
	struct v4l2_subdev *sd;

	if (!list_empty(&notifier->waiting))
		return false;

	list_for_each_entry(sd, &notifier->done, async_list) {
		struct v4l2_async_notifier *subdev_notifier =
			v4l2_async_find_subdev_notifier(sd);

		if (subdev_notifier &&
		    !v4l2_async_notifier_can_complete(subdev_notifier))
			return false;
	}

	return true;
}

/*
 * Complete the master notifier if possible. This is done when all async
 * sub-devices have been bound; v4l2_device is also available then.
 */
static int v4l2_async_notifier_try_complete(
	struct v4l2_async_notifier *notifier)
{
	/* Quick check whether there are still more sub-devices here. */
	if (!list_empty(&notifier->waiting))
		return 0;

	/* Check the entire notifier tree; find the root notifier first. */
	while (notifier->parent)
		notifier = notifier->parent;

	/* This is root if it has v4l2_dev. */
	if (!notifier->v4l2_dev)
		return 0;

	/* Is everything ready? */
	if (!v4l2_async_notifier_can_complete(notifier))
		return 0;

	return v4l2_async_notifier_call_complete(notifier);
}

static int v4l2_async_notifier_try_all_subdevs(
	struct v4l2_async_notifier *notifier);

static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
				   struct v4l2_device *v4l2_dev,
				   struct v4l2_subdev *sd,
				   struct v4l2_async_subdev *asd)
{
	struct v4l2_async_notifier *subdev_notifier;
	int ret;

	ret = v4l2_device_register_subdev(v4l2_dev, sd);
@@ -149,17 +225,36 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
	/* Move from the global subdevice list to notifier's done */
	list_move(&sd->async_list, &notifier->done);

	/*
	 * See if the sub-device has a notifier. If not, return here.
	 */
	subdev_notifier = v4l2_async_find_subdev_notifier(sd);
	if (!subdev_notifier || subdev_notifier->parent)
		return 0;

	/*
	 * Proceed with checking for the sub-device notifier's async
	 * sub-devices, and return the result. The error will be handled by the
	 * caller.
	 */
	subdev_notifier->parent = notifier;

	return v4l2_async_notifier_try_all_subdevs(subdev_notifier);
}

/* Test all async sub-devices in a notifier for a match. */
static int v4l2_async_notifier_try_all_subdevs(
	struct v4l2_async_notifier *notifier)
{
	struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
	struct v4l2_subdev *sd, *tmp;
	struct v4l2_device *v4l2_dev =
		v4l2_async_notifier_find_v4l2_dev(notifier);
	struct v4l2_subdev *sd;

	if (!v4l2_dev)
		return 0;

	list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
again:
	list_for_each_entry(sd, &subdev_list, async_list) {
		struct v4l2_async_subdev *asd;
		int ret;

@@ -170,6 +265,14 @@ static int v4l2_async_notifier_try_all_subdevs(
		ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
		if (ret < 0)
			return ret;

		/*
		 * v4l2_async_match_notify() may lead to registering a
		 * new notifier and thus changing the async subdevs
		 * list. In order to proceed safely from here, restart
		 * parsing the list from the beginning.
		 */
		goto again;
	}

	return 0;
@@ -183,17 +286,26 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd)
	sd->asd = NULL;
}

/* Unbind all sub-devices in the notifier tree. */
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) {
		struct v4l2_async_notifier *subdev_notifier =
			v4l2_async_find_subdev_notifier(sd);

		if (subdev_notifier)
			v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);

		v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
		v4l2_async_cleanup(sd);

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

	notifier->parent = NULL;
}

static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
@@ -208,15 +320,6 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
	INIT_LIST_HEAD(&notifier->waiting);
	INIT_LIST_HEAD(&notifier->done);

	if (!notifier->num_subdevs) {
		int ret;

		ret = v4l2_async_notifier_call_complete(notifier);
		notifier->v4l2_dev = NULL;

		return ret;
	}

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

@@ -238,16 +341,12 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
	mutex_lock(&list_lock);

	ret = v4l2_async_notifier_try_all_subdevs(notifier);
	if (ret) {
		mutex_unlock(&list_lock);
		return ret;
	}
	if (ret)
		goto err_unbind;

	if (list_empty(&notifier->waiting)) {
		ret = v4l2_async_notifier_call_complete(notifier);
	ret = v4l2_async_notifier_try_complete(notifier);
	if (ret)
			goto err_complete;
	}
		goto err_unbind;

	/* Keep also completed notifiers on the list */
	list_add(&notifier->list, &notifier_list);
@@ -256,7 +355,10 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)

	return 0;

err_complete:
err_unbind:
	/*
	 * On failure, unbind all sub-devices registered through this notifier.
	 */
	v4l2_async_notifier_unbind_all_subdevs(notifier);

	mutex_unlock(&list_lock);
@@ -269,7 +371,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
{
	int ret;

	if (WARN_ON(!v4l2_dev))
	if (WARN_ON(!v4l2_dev || notifier->sd))
		return -EINVAL;

	notifier->v4l2_dev = v4l2_dev;
@@ -282,20 +384,39 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
}
EXPORT_SYMBOL(v4l2_async_notifier_register);

int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
					struct v4l2_async_notifier *notifier)
{
	int ret;

	if (WARN_ON(!sd || notifier->v4l2_dev))
		return -EINVAL;

	notifier->sd = sd;

	ret = __v4l2_async_notifier_register(notifier);
	if (ret)
		notifier->sd = NULL;

	return ret;
}
EXPORT_SYMBOL(v4l2_async_subdev_notifier_register);

void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
{
	if (!notifier->v4l2_dev)
	if (!notifier->v4l2_dev && !notifier->sd)
		return;

	mutex_lock(&list_lock);

	list_del(&notifier->list);

	v4l2_async_notifier_unbind_all_subdevs(notifier);

	mutex_unlock(&list_lock);

	notifier->sd = NULL;
	notifier->v4l2_dev = NULL;

	list_del(&notifier->list);

	mutex_unlock(&list_lock);
}
EXPORT_SYMBOL(v4l2_async_notifier_unregister);

@@ -331,6 +452,7 @@ EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup);

int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
	struct v4l2_async_notifier *subdev_notifier;
	struct v4l2_async_notifier *notifier;
	int ret;

@@ -347,24 +469,26 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
	INIT_LIST_HEAD(&sd->async_list);

	list_for_each_entry(notifier, &notifier_list, list) {
		struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier,
								      sd);
		struct v4l2_device *v4l2_dev =
			v4l2_async_notifier_find_v4l2_dev(notifier);
		struct v4l2_async_subdev *asd;
		int ret;

		if (!v4l2_dev)
			continue;

		asd = v4l2_async_find_match(notifier, sd);
		if (!asd)
			continue;

		ret = v4l2_async_match_notify(notifier, notifier->v4l2_dev, sd,
					      asd);
		if (ret)
			goto err_unlock;
			goto err_unbind;

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

		ret = v4l2_async_notifier_call_complete(notifier);
		ret = v4l2_async_notifier_try_complete(notifier);
		if (ret)
			goto err_cleanup;
			goto err_unbind;

		goto out_unlock;
	}
@@ -377,11 +501,19 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)

	return 0;

err_cleanup:
err_unbind:
	/*
	 * Complete failed. Unbind the sub-devices bound through registering
	 * this async sub-device.
	 */
	subdev_notifier = v4l2_async_find_subdev_notifier(sd);
	if (subdev_notifier)
		v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);

	if (sd->asd)
		v4l2_async_notifier_call_unbind(notifier, sd, sd->asd);
	v4l2_async_cleanup(sd);

err_unlock:
	mutex_unlock(&list_lock);

	return ret;
+17 −2
Original line number Diff line number Diff line
@@ -82,7 +82,8 @@ struct v4l2_async_subdev {
/**
 * struct v4l2_async_notifier_operations - Asynchronous V4L2 notifier operations
 * @bound:	a subdevice driver has successfully probed one of the subdevices
 * @complete:	all subdevices have been probed successfully
 * @complete:	All subdevices have been probed successfully. The complete
 *		callback is only executed for the root notifier.
 * @unbind:	a subdevice is leaving
 */
struct v4l2_async_notifier_operations {
@@ -102,7 +103,9 @@ struct v4l2_async_notifier_operations {
 * @num_subdevs: number of subdevices used in the subdevs array
 * @max_subdevs: number of subdevices allocated in the subdevs array
 * @subdevs:	array of pointers to subdevice descriptors
 * @v4l2_dev:	pointer to struct v4l2_device
 * @v4l2_dev:	v4l2_device of the root notifier, NULL otherwise
 * @sd:		sub-device that registered the notifier, NULL otherwise
 * @parent:	parent notifier
 * @waiting:	list of struct v4l2_async_subdev, waiting for their drivers
 * @done:	list of struct v4l2_subdev, already probed
 * @list:	member in a global list of notifiers
@@ -113,6 +116,8 @@ struct v4l2_async_notifier {
	unsigned int max_subdevs;
	struct v4l2_async_subdev **subdevs;
	struct v4l2_device *v4l2_dev;
	struct v4l2_subdev *sd;
	struct v4l2_async_notifier *parent;
	struct list_head waiting;
	struct list_head done;
	struct list_head list;
@@ -127,6 +132,16 @@ struct v4l2_async_notifier {
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
				 struct v4l2_async_notifier *notifier);

/**
 * v4l2_async_subdev_notifier_register - registers a subdevice asynchronous
 *					 notifier for a sub-device
 *
 * @sd: pointer to &struct v4l2_subdev
 * @notifier: pointer to &struct v4l2_async_notifier
 */
int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
					struct v4l2_async_notifier *notifier);

/**
 * v4l2_async_notifier_unregister - unregisters a subdevice asynchronous notifier
 *