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

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
...@@ -124,11 +124,87 @@ static struct v4l2_async_subdev *v4l2_async_find_match( ...@@ -124,11 +124,87 @@ static struct v4l2_async_subdev *v4l2_async_find_match(
return NULL; 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, static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
struct v4l2_device *v4l2_dev, struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd, struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd) struct v4l2_async_subdev *asd)
{ {
struct v4l2_async_notifier *subdev_notifier;
int ret; int ret;
ret = v4l2_device_register_subdev(v4l2_dev, sd); ret = v4l2_device_register_subdev(v4l2_dev, sd);
...@@ -149,17 +225,36 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, ...@@ -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 */ /* Move from the global subdevice list to notifier's done */
list_move(&sd->async_list, &notifier->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; 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. */ /* Test all async sub-devices in a notifier for a match. */
static int v4l2_async_notifier_try_all_subdevs( static int v4l2_async_notifier_try_all_subdevs(
struct v4l2_async_notifier *notifier) struct v4l2_async_notifier *notifier)
{ {
struct v4l2_device *v4l2_dev = notifier->v4l2_dev; struct v4l2_device *v4l2_dev =
struct v4l2_subdev *sd, *tmp; 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; struct v4l2_async_subdev *asd;
int ret; int ret;
...@@ -170,6 +265,14 @@ static int v4l2_async_notifier_try_all_subdevs( ...@@ -170,6 +265,14 @@ static int v4l2_async_notifier_try_all_subdevs(
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
if (ret < 0) if (ret < 0)
return ret; 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; return 0;
...@@ -183,17 +286,26 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd) ...@@ -183,17 +286,26 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd)
sd->asd = NULL; sd->asd = NULL;
} }
/* Unbind all sub-devices in the notifier tree. */
static void v4l2_async_notifier_unbind_all_subdevs( static void v4l2_async_notifier_unbind_all_subdevs(
struct v4l2_async_notifier *notifier) struct v4l2_async_notifier *notifier)
{ {
struct v4l2_subdev *sd, *tmp; struct v4l2_subdev *sd, *tmp;
list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) { 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_notifier_call_unbind(notifier, sd, sd->asd);
v4l2_async_cleanup(sd); v4l2_async_cleanup(sd);
list_move(&sd->async_list, &subdev_list); list_move(&sd->async_list, &subdev_list);
} }
notifier->parent = NULL;
} }
static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) 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) ...@@ -208,15 +320,6 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
INIT_LIST_HEAD(&notifier->waiting); INIT_LIST_HEAD(&notifier->waiting);
INIT_LIST_HEAD(&notifier->done); 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++) { for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i]; asd = notifier->subdevs[i];
...@@ -238,16 +341,12 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) ...@@ -238,16 +341,12 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
mutex_lock(&list_lock); mutex_lock(&list_lock);
ret = v4l2_async_notifier_try_all_subdevs(notifier); ret = v4l2_async_notifier_try_all_subdevs(notifier);
if (ret) { if (ret)
mutex_unlock(&list_lock); goto err_unbind;
return ret;
}
if (list_empty(&notifier->waiting)) { ret = v4l2_async_notifier_try_complete(notifier);
ret = v4l2_async_notifier_call_complete(notifier);
if (ret) if (ret)
goto err_complete; goto err_unbind;
}
/* Keep also completed notifiers on the list */ /* Keep also completed notifiers on the list */
list_add(&notifier->list, &notifier_list); list_add(&notifier->list, &notifier_list);
...@@ -256,7 +355,10 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) ...@@ -256,7 +355,10 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
return 0; return 0;
err_complete: err_unbind:
/*
* On failure, unbind all sub-devices registered through this notifier.
*/
v4l2_async_notifier_unbind_all_subdevs(notifier); v4l2_async_notifier_unbind_all_subdevs(notifier);
mutex_unlock(&list_lock); mutex_unlock(&list_lock);
...@@ -269,7 +371,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, ...@@ -269,7 +371,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
{ {
int ret; int ret;
if (WARN_ON(!v4l2_dev)) if (WARN_ON(!v4l2_dev || notifier->sd))
return -EINVAL; return -EINVAL;
notifier->v4l2_dev = v4l2_dev; notifier->v4l2_dev = v4l2_dev;
...@@ -282,20 +384,39 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, ...@@ -282,20 +384,39 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
} }
EXPORT_SYMBOL(v4l2_async_notifier_register); 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) void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
{ {
if (!notifier->v4l2_dev) if (!notifier->v4l2_dev && !notifier->sd)
return; return;
mutex_lock(&list_lock); mutex_lock(&list_lock);
list_del(&notifier->list);
v4l2_async_notifier_unbind_all_subdevs(notifier); v4l2_async_notifier_unbind_all_subdevs(notifier);
mutex_unlock(&list_lock); notifier->sd = NULL;
notifier->v4l2_dev = NULL; notifier->v4l2_dev = NULL;
list_del(&notifier->list);
mutex_unlock(&list_lock);
} }
EXPORT_SYMBOL(v4l2_async_notifier_unregister); EXPORT_SYMBOL(v4l2_async_notifier_unregister);
...@@ -331,6 +452,7 @@ EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); ...@@ -331,6 +452,7 @@ EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup);
int v4l2_async_register_subdev(struct v4l2_subdev *sd) int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{ {
struct v4l2_async_notifier *subdev_notifier;
struct v4l2_async_notifier *notifier; struct v4l2_async_notifier *notifier;
int ret; int ret;
...@@ -347,24 +469,26 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) ...@@ -347,24 +469,26 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
INIT_LIST_HEAD(&sd->async_list); INIT_LIST_HEAD(&sd->async_list);
list_for_each_entry(notifier, &notifier_list, list) { list_for_each_entry(notifier, &notifier_list, list) {
struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier, struct v4l2_device *v4l2_dev =
sd); v4l2_async_notifier_find_v4l2_dev(notifier);
struct v4l2_async_subdev *asd;
int ret; int ret;
if (!v4l2_dev)
continue;
asd = v4l2_async_find_match(notifier, sd);
if (!asd) if (!asd)
continue; continue;
ret = v4l2_async_match_notify(notifier, notifier->v4l2_dev, sd, ret = v4l2_async_match_notify(notifier, notifier->v4l2_dev, sd,
asd); asd);
if (ret) if (ret)
goto err_unlock; goto err_unbind;
if (!list_empty(&notifier->waiting)) ret = v4l2_async_notifier_try_complete(notifier);
goto out_unlock;
ret = v4l2_async_notifier_call_complete(notifier);
if (ret) if (ret)
goto err_cleanup; goto err_unbind;
goto out_unlock; goto out_unlock;
} }
...@@ -377,11 +501,19 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) ...@@ -377,11 +501,19 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
return 0; 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_notifier_call_unbind(notifier, sd, sd->asd);
v4l2_async_cleanup(sd); v4l2_async_cleanup(sd);
err_unlock:
mutex_unlock(&list_lock); mutex_unlock(&list_lock);
return ret; return ret;
......
...@@ -82,7 +82,8 @@ struct v4l2_async_subdev { ...@@ -82,7 +82,8 @@ struct v4l2_async_subdev {
/** /**
* struct v4l2_async_notifier_operations - Asynchronous V4L2 notifier operations * struct v4l2_async_notifier_operations - Asynchronous V4L2 notifier operations
* @bound: a subdevice driver has successfully probed one of the subdevices * @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 * @unbind: a subdevice is leaving
*/ */
struct v4l2_async_notifier_operations { struct v4l2_async_notifier_operations {
...@@ -102,7 +103,9 @@ struct v4l2_async_notifier_operations { ...@@ -102,7 +103,9 @@ struct v4l2_async_notifier_operations {
* @num_subdevs: number of subdevices used in the subdevs array * @num_subdevs: number of subdevices used in the subdevs array
* @max_subdevs: number of subdevices allocated in the subdevs array * @max_subdevs: number of subdevices allocated in the subdevs array
* @subdevs: array of pointers to subdevice descriptors * @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 * @waiting: list of struct v4l2_async_subdev, waiting for their drivers
* @done: list of struct v4l2_subdev, already probed * @done: list of struct v4l2_subdev, already probed
* @list: member in a global list of notifiers * @list: member in a global list of notifiers
...@@ -113,6 +116,8 @@ struct v4l2_async_notifier { ...@@ -113,6 +116,8 @@ struct v4l2_async_notifier {
unsigned int max_subdevs; unsigned int max_subdevs;
struct v4l2_async_subdev **subdevs; struct v4l2_async_subdev **subdevs;
struct v4l2_device *v4l2_dev; struct v4l2_device *v4l2_dev;
struct v4l2_subdev *sd;
struct v4l2_async_notifier *parent;
struct list_head waiting; struct list_head waiting;
struct list_head done; struct list_head done;
struct list_head list; struct list_head list;
...@@ -127,6 +132,16 @@ struct v4l2_async_notifier { ...@@ -127,6 +132,16 @@ struct v4l2_async_notifier {
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier); 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 * v4l2_async_notifier_unregister - unregisters a subdevice asynchronous notifier
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment