On 26/09/17 00:25, Sakari Ailus wrote: > 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 root notifier's complete callback is only called when all sub-device > notifiers are completed. > > Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> Acked-by: Hans Verkuil <hans.verkuil@xxxxxxxxx> > --- > drivers/media/v4l2-core/v4l2-async.c | 218 +++++++++++++++++++++++++++++------ > include/media/v4l2-async.h | 16 ++- > 2 files changed, 199 insertions(+), 35 deletions(-) > > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c > index 1d4132305243..735f72f81740 100644 > --- a/drivers/media/v4l2-core/v4l2-async.c > +++ b/drivers/media/v4l2-core/v4l2-async.c > @@ -124,11 +124,109 @@ 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, ¬ifier_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(¬ifier->waiting)) > + return false; > + > + list_for_each_entry(sd, ¬ifier->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 all notifiers. Call on the root notifier. */ > +static int v4l2_async_notifier_complete( > + struct v4l2_async_notifier *notifier) > +{ > + struct v4l2_subdev *sd; > + > + list_for_each_entry(sd, ¬ifier->done, async_list) { > + struct v4l2_async_notifier *subdev_notifier = > + v4l2_async_find_subdev_notifier(sd); > + int ret; > + > + if (!subdev_notifier) > + continue; > + > + ret = v4l2_async_notifier_complete(subdev_notifier); > + if (ret) > + return ret; > + } > + > + return v4l2_async_notifier_call_complete(notifier); > +} > + > +/* > + * Complete notifiers 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(¬ifier->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_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,8 +247,17 @@ 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, ¬ifier->done); > > - if (list_empty(¬ifier->waiting)) > - return v4l2_async_notifier_call_complete(notifier); > + /* > + * See if the sub-device has a notifier. If it does, proceed > + * with checking for its async sub-devices. > + */ > + subdev_notifier = v4l2_async_find_subdev_notifier(sd); > + if (subdev_notifier && !subdev_notifier->parent) { > + subdev_notifier->parent = notifier; > + ret = v4l2_async_notifier_try_all_subdevs(subdev_notifier); > + if (ret) > + return ret; > + } > > return 0; > } > @@ -159,10 +266,15 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, > 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; > > - list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { > + if (!v4l2_dev) > + return 0; > + > +again: > + list_for_each_entry(sd, &subdev_list, async_list) { > struct v4l2_async_subdev *asd; > int ret; > > @@ -171,10 +283,16 @@ static int v4l2_async_notifier_try_all_subdevs( > continue; > > ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); > - if (ret < 0) { > - mutex_unlock(&list_lock); > + 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; > @@ -201,15 +319,6 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) > INIT_LIST_HEAD(¬ifier->waiting); > INIT_LIST_HEAD(¬ifier->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]; > > @@ -236,18 +345,20 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) > return ret; > } > > + ret = v4l2_async_notifier_try_complete(notifier); > + > /* Keep also completed notifiers on the list */ > list_add(¬ifier->list, ¬ifier_list); > > mutex_unlock(&list_lock); > > - return 0; > + return ret; > } > > int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, > struct v4l2_async_notifier *notifier) > { > - if (WARN_ON(!v4l2_dev)) > + if (WARN_ON(!v4l2_dev || notifier->sd)) > return -EINVAL; > > notifier->v4l2_dev = v4l2_dev; > @@ -256,18 +367,31 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, > } > EXPORT_SYMBOL(v4l2_async_notifier_register); > > -void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) > +int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, > + struct v4l2_async_notifier *notifier) > { > - struct v4l2_subdev *sd, *tmp; > + if (WARN_ON(!sd || notifier->v4l2_dev)) > + return -EINVAL; > > - if (!notifier->v4l2_dev) > - return; > + notifier->sd = sd; > > - mutex_lock(&list_lock); > + return __v4l2_async_notifier_register(notifier); > +} > +EXPORT_SYMBOL(v4l2_async_subdev_notifier_register); > > - list_del(¬ifier->list); > +/* 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, ¬ifier->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_cleanup(sd); > > v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); > @@ -275,9 +399,24 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) > list_move(&sd->async_list, &subdev_list); > } > > - mutex_unlock(&list_lock); > + notifier->parent = NULL; > +} > > +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) > +{ > + if (!notifier->v4l2_dev && !notifier->sd) > + return; > + > + mutex_lock(&list_lock); > + > + v4l2_async_notifier_unbind_all_subdevs(notifier); > + > + notifier->sd = NULL; > notifier->v4l2_dev = NULL; > + > + list_del(¬ifier->list); > + > + mutex_unlock(&list_lock); > } > EXPORT_SYMBOL(v4l2_async_notifier_unregister); > > @@ -327,14 +466,25 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) > INIT_LIST_HEAD(&sd->async_list); > > list_for_each_entry(notifier, ¬ifier_list, list) { > - struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier, > - sd); > - if (asd) { > - int ret = v4l2_async_match_notify( > - notifier, notifier->v4l2_dev, sd, asd); > - mutex_unlock(&list_lock); > - return ret; > - } > + 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, v4l2_dev, sd, asd); > + > + if (!ret) > + ret = v4l2_async_notifier_try_complete(notifier); > + > + mutex_unlock(&list_lock); > + return ret; > } > > /* None matched, wait for hot-plugging */ > diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h > index 7d56c355138b..0b30a631ad19 100644 > --- a/include/media/v4l2-async.h > +++ b/include/media/v4l2-async.h > @@ -102,7 +102,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 +115,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; > @@ -128,6 +132,16 @@ 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 > * > * @notifier: pointer to &struct v4l2_async_notifier > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html