Re: [PATCH 2/2] media: V4L2: support asynchronous subdevice registration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon October 22 2012 13:08:12 Guennadi Liakhovetski wrote:
> Hi Hans
> 
> Thanks for reviewing the patch.
> 
> On Mon, 22 Oct 2012, Hans Verkuil wrote:
> 
> > Hi Guennadi,
> > 
> > I've reviewed this patch and I have a few questions:
> > 
> > On Sat October 20 2012 00:20:24 Guennadi Liakhovetski wrote:
> > > Currently bridge device drivers register devices for all subdevices
> > > synchronously, tupically, during their probing. E.g. if an I2C CMOS sensor
> > > is attached to a video bridge device, the bridge driver will create an I2C
> > > device and wait for the respective I2C driver to probe. This makes linking
> > > of devices straight forward, but this approach cannot be used with
> > > intrinsically asynchronous and unordered device registration systems like
> > > the Flattened Device Tree. To support such systems this patch adds an
> > > asynchronous subdevice registration framework to V4L2. To use it respective
> > > (e.g. I2C) subdevice drivers must request deferred probing as long as their
> > > bridge driver hasn't probed. The bridge driver during its probing submits a
> > > an arbitrary number of subdevice descriptor groups to the framework to
> > > manage. After that it can add callbacks to each of those groups to be
> > > called at various stages during subdevice probing, e.g. after completion.
> > > Then the bridge driver can request single groups to be probed, finish its
> > > own probing and continue its video subsystem configuration from its
> > > callbacks.
> > 
> > What is the purpose of allowing multiple groups?
> 
> To support, e.g. multiple sensors connected to a single bridge.

So, isn't that one group with two sensor subdevs?

A bridge driver has a list of subdevs. There is no concept of 'groups'. Perhaps
I misunderstand?

> > I can't think of any reason
> > why you would want to have more than one group. If you have just one group
> > you can simplify this code quite a bit: most of the v4l2_async_group fields
> > can just become part of struct v4l2_device, you don't need the 'list' and
> > 'v4l2_dev' fields anymore and the 'bind' and 'complete' callbacks can be
> > implemented using the v4l2_device notify callback which we already have.
> > 
> > > 
> > > Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@xxxxxx>
> > > ---
> > > 
> > > One more thing to note about this patch. Subdevice drivers, supporting 
> > > asynchronous probing, and using this framework, need a unified way to 
> > > detect, whether their probing should succeed or they should request 
> > > deferred probing. I implement this using device platform data. This means, 
> > > that all subdevice drivers, wishing to use this API will have to use the 
> > > same platform data struct. I don't think this is a major inconvenience, 
> > > but if we decide against this, we'll have to add a V4L2 function to verify 
> > > "are you ready for me or not." The latter would be inconvenient, because 
> > > then we would have to look through all registered subdevice descriptor 
> > > groups for this specific subdevice.
> > 
> > I have to admit that I don't quite follow this. I guess I would need to see
> > this being used in an actual driver.
> 
> The issue is simple: subdevice drivers have to recognise, when it's still 
> too early to probe and return -EPROBE_DEFER. If you have a sensor, whose 
> master clock is supplied from an external oscillator. You load its driver, 
> it will happily get a clock reference and find no reason to fail probe(). 
> It will initialise its subdevice and return from probing. Then, when your 
> bridge driver probes, it will have no way to find that subdevice.

This problem is specific to platform subdev drivers, right? Since for i2c
subdevs you can use i2c_get_clientdata().

I wonder whether it isn't a better idea to have platform_data embed a standard
struct containing a v4l2_subdev pointer. Subdevs can use container_of to get
from the address of that struct to the full platform_data, and you don't have
that extra dereference (i.e. a pointer to the new struct which has a pointer
to the actual platform_data). The impact of that change is much smaller for
existing subdevs.

And if it isn't necessary for i2c subdev drivers, then I think we should do
this only for platform drivers.

> 
> > >  drivers/media/v4l2-core/Makefile      |    3 +-
> > >  drivers/media/v4l2-core/v4l2-async.c  |  249 +++++++++++++++++++++++++++++++++
> > >  drivers/media/v4l2-core/v4l2-device.c |    2 +
> > >  include/media/v4l2-async.h            |   88 ++++++++++++
> > >  include/media/v4l2-device.h           |    6 +
> > >  include/media/v4l2-subdev.h           |   16 ++
> > >  6 files changed, 363 insertions(+), 1 deletions(-)
> > >  create mode 100644 drivers/media/v4l2-core/v4l2-async.c
> > >  create mode 100644 include/media/v4l2-async.h
> > > 
> > > diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> > > index cb5fede..074e01c 100644
> > > --- a/drivers/media/v4l2-core/Makefile
> > > +++ b/drivers/media/v4l2-core/Makefile
> > > @@ -5,7 +5,8 @@
> > >  tuner-objs	:=	tuner-core.o
> > >  
> > >  videodev-objs	:=	v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
> > > -			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o
> > > +			v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o \
> > > +			v4l2-async.o
> > >  ifeq ($(CONFIG_COMPAT),y)
> > >    videodev-objs += v4l2-compat-ioctl32.o
> > >  endif
> > > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > > new file mode 100644
> > > index 0000000..871f116
> > > --- /dev/null
> > > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > > @@ -0,0 +1,249 @@
> > > +/*
> > > + * V4L2 asynchronous subdevice registration API
> > > + *
> > > + * Copyright (C) 2012, Guennadi Liakhovetski <g.liakhovetski@xxxxxx>
> > > + *
> > > + * 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>
> > > +#include <linux/module.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/notifier.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/types.h>
> > > +
> > > +#include <media/v4l2-async.h>
> > > +#include <media/v4l2-device.h>
> > > +#include <media/v4l2-subdev.h>
> > > +
> > > +static bool match_i2c(struct device *dev, struct v4l2_async_hw_device *hw_dev)
> > > +{
> > > +	struct i2c_client *client = to_i2c_client(dev);
> > > +	return hw_dev->bus_type == V4L2_ASYNC_BUS_I2C &&
> > > +		hw_dev->match.i2c.adapter_id == client->adapter->nr &&
> > > +		hw_dev->match.i2c.address == client->addr;
> > > +}
> > > +
> > > +static bool match_platform(struct device *dev, struct v4l2_async_hw_device *hw_dev)
> > > +{
> > > +	return hw_dev->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
> > > +		!strcmp(hw_dev->match.platform.name, dev_name(dev));
> > > +}
> > > +
> > > +/*
> > > + * I think, notifiers on different busses can run concurrently, so, we have to
> > > + * protect common data, e.g. sub-device lists.
> > > + */
> > > +static int async_notifier_cb(struct v4l2_async_group *group,
> > > +		unsigned long action, struct device *dev,
> > > +		bool (*match)(struct device *, struct v4l2_async_hw_device *))
> > > +{
> > > +	struct v4l2_device *v4l2_dev = group->v4l2_dev;
> > > +	struct v4l2_async_subdev *asd;
> > > +	bool done;
> > > +	int ret;
> > > +
> > > +	if (action != BUS_NOTIFY_BOUND_DRIVER &&
> > > +	    action != BUS_NOTIFY_BIND_DRIVER)
> > > +		return NOTIFY_DONE;
> > > +
> > > +	/* Asynchronous: have to lock */
> > > +	mutex_lock(&v4l2_dev->group_lock);
> > > +
> > > +	list_for_each_entry(asd, &group->group, list) {
> > > +		if (match(dev, &asd->hw))
> > > +			break;
> > > +	}
> > > +
> > > +	if (&asd->list == &group->group) {
> > > +		/* Not our device */
> > > +		mutex_unlock(&v4l2_dev->group_lock);
> > > +		return NOTIFY_DONE;
> > > +	}
> > > +
> > > +	asd->dev = dev;
> > > +
> > > +	if (action == BUS_NOTIFY_BIND_DRIVER) {
> > > +		/*
> > > +		 * Provide platform data to the driver: it can complete probing
> > > +		 * now.
> > > +		 */
> > > +		dev->platform_data = &asd->sdpd;
> > > +		mutex_unlock(&v4l2_dev->group_lock);
> > > +		if (group->bind_cb)
> > > +			group->bind_cb(group, asd);
> > > +		return NOTIFY_OK;
> > > +	}
> > > +
> > > +	/* BUS_NOTIFY_BOUND_DRIVER */
> > > +	if (asd->hw.bus_type == V4L2_ASYNC_BUS_I2C)
> > > +		asd->sdpd.subdev = i2c_get_clientdata(to_i2c_client(dev));
> > > +	/*
> > > +	 * Non-I2C subdevice drivers should take care to assign their subdevice
> > > +	 * pointers
> > 
> > Can they? Isn't it the bridge driver that instantiates the subdev structs and
> > calls v4l2_device_register_subdev() in the case of platform subdev drivers?
> > I don't think a subdev platform driver knows its subdev pointer.
> 
> Not in case of sh_mobile_csi2. There I've used the same notification 
> procedure, that we want to use now, to get to the subdevice, initialised 
> by the csi2 driver itself.

Ah, OK.
 
> > For that matter, is async probing needed at all for platform subdev drivers? It
> > is for i2c subdevs, no doubt about that, but does it make sense for platform
> > subdev drivers as well?
> 
> I think it does. E.g. on sh-mobile CSI2 is a separate hardware unit with 
> an own subdevice driver. It will get an own DT node, so, it will probe 
> asynchronously.
> 
> > > +	 */
> > > +	ret = v4l2_device_register_subdev(v4l2_dev,
> > > +					  asd->sdpd.subdev);
> > > +	if (ret < 0) {
> > > +		mutex_unlock(&v4l2_dev->group_lock);
> > > +		/* FIXME: error, clean up world? */
> > > +		dev_err(dev, "Failed registering a subdev: %d\n", ret);
> > > +		return NOTIFY_OK;
> > > +	}
> > > +	list_move(&asd->list, &group->done);
> > > +
> > > +	/* Client probed & all subdev drivers collected */
> > > +	done = list_empty(&group->group);
> > > +
> > > +	mutex_unlock(&v4l2_dev->group_lock);
> > > +
> > > +	if (group->bound_cb)
> > > +		group->bound_cb(group, asd);
> > > +
> > > +	if (done && group->complete_cb)
> > > +		group->complete_cb(group);
> > > +
> > > +	return NOTIFY_OK;
> > > +}
> > > +
> > > +static int platform_cb(struct notifier_block *nb,
> > > +		       unsigned long action, void *data)
> > > +{
> > > +	struct device *dev = data;
> > > +	struct v4l2_async_group *group = container_of(nb, struct v4l2_async_group,
> > > +						     platform_notifier);
> > > +
> > > +	return async_notifier_cb(group, action, dev, match_platform);
> > > +}
> > > +
> > > +static int i2c_cb(struct notifier_block *nb,
> > > +		  unsigned long action, void *data)
> > > +{
> > > +	struct device *dev = data;
> > > +	struct v4l2_async_group *group = container_of(nb, struct v4l2_async_group,
> > > +						     i2c_notifier);
> > > +
> > > +	return async_notifier_cb(group, action, dev, match_i2c);
> > > +}
> > > +
> > > +/*
> > > + * Typically this function will be called during bridge driver probing. It
> > > + * installs bus notifiers to handle asynchronously probing subdevice drivers.
> > > + * Once the bridge driver probing completes, subdevice drivers, waiting in
> > > + * EPROBE_DEFER state are re-probed, at which point they get their platform
> > > + * data, which allows them to complete probing.
> > > + */
> > > +int v4l2_async_group_probe(struct v4l2_async_group *group)
> > > +{
> > > +	struct v4l2_async_subdev *asd, *tmp;
> > > +	bool i2c_used = false, platform_used = false;
> > > +	int ret;
> > > +
> > > +	/* This group is inactive so far - no notifiers yet */
> > > +	list_for_each_entry_safe(asd, tmp, &group->group, list) {
> > > +		if (asd->sdpd.subdev) {
> > > +			/* Simulate a BIND event */
> > > +			if (group->bind_cb)
> > > +				group->bind_cb(group, asd);
> > > +
> > > +			/* Already probed, don't wait for it */
> > > +			ret = v4l2_device_register_subdev(group->v4l2_dev,
> > > +							  asd->sdpd.subdev);
> > > +
> > > +			if (ret < 0)
> > > +				return ret;
> > > +
> > > +			list_move(&asd->list, &group->done);
> > > +			continue;
> > > +		}
> > > +
> > > +		switch (asd->hw.bus_type) {
> > > +		case V4L2_ASYNC_BUS_PLATFORM:
> > > +			platform_used = true;
> > > +			break;
> > > +		case V4L2_ASYNC_BUS_I2C:
> > > +			i2c_used = true;
> > 
> > Add 'break;'
> > 
> > > +		}
> > > +	}
> > > +
> > > +	if (list_empty(&group->group)) {
> > > +		if (group->complete_cb)
> > > +			group->complete_cb(group);
> > > +		return 0;
> > > +	}
> > > +
> > > +	/* TODO: so far bus_register_notifier() never fails */
> > > +	if (platform_used) {
> > > +		group->platform_notifier.notifier_call = platform_cb;
> > > +		bus_register_notifier(&platform_bus_type,
> > > +				      &group->platform_notifier);
> > > +	}
> > > +
> > > +	if (i2c_used) {
> > > +		group->i2c_notifier.notifier_call = i2c_cb;
> > > +		bus_register_notifier(&i2c_bus_type,
> > > +				      &group->i2c_notifier);
> > > +	}
> > > +
> > 
> > Hmm. I would expect that this probe function would sleep here and wait until
> > all subdev drivers are probed. Or is that not possible?
> 
> Why would it be async probing then? :-) The whole concept is built around 
> deferred probing. A typical sequence will look like this:
> 
> sensor_probe() {
> 	return -EPROBE_DEFER;
> }
> 
> bridge_probe() {
> 	v4l2_async_group_init();
> 	v4l2_async_group_probe();
> 	return 0;
> }
> 
> /* The bridge driver completed its probing, _THIS_ triggers re-probing of 
> all drivers in deferred-probe state */	
> 
> sensor_probe() {
> 	v4l2_subdev_init();
> 	return 0;
> }
> 
> async_notifier_cb() {
> 	v4l2_device_register_subdev();
> 	group->complete_cb();
> }
> 
> bridge_complete_cb() {
> 	video_register_device();
> }
> 
> So, the bridge driver has to complete its probe() for sensor's probe() to 
> be called again.

True. Sorry, it can be confusing :-)

That said, how does this new framework take care of timeouts if one of the
subdevs is never bound? You don't want to have this wait indefinitely, I think.

> > If this function could just sleep (with a timeout, probably), then you don't
> > need the 'complete_cb' and bridge drivers don't have to set up their own
> > completion mechanism.
> > 
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL(v4l2_async_group_probe);
> > > +
> > > +int v4l2_async_group_init(struct v4l2_device *v4l2_dev,
> > > +			  struct v4l2_async_group *group,
> > > +			  struct v4l2_async_subdev *asd, int cnt)
> > > +{
> > > +	int i;
> > > +
> > > +	if (!group)
> > > +		return -EINVAL;
> > > +
> > > +	INIT_LIST_HEAD(&group->group);
> > > +	INIT_LIST_HEAD(&group->done);
> > > +	group->v4l2_dev = v4l2_dev;
> > > +
> > > +	for (i = 0; i < cnt; i++)
> > > +		list_add_tail(&asd[i].list, &group->group);
> > > +
> > > +	/* Protect the global V4L2 device group list */
> > > +	mutex_lock(&v4l2_dev->group_lock);
> > > +	list_add_tail(&group->list, &v4l2_dev->group_head);
> > > +	mutex_unlock(&v4l2_dev->group_lock);
> > > +
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL(v4l2_async_group_init);
> > > +
> > > +void v4l2_async_group_release(struct v4l2_async_group *group)
> > > +{
> > > +	struct v4l2_async_subdev *asd, *tmp;
> > > +
> > > +	/* Also no problem, if notifiers haven't been registered */
> > > +	bus_unregister_notifier(&platform_bus_type,
> > > +				&group->platform_notifier);
> > > +	bus_unregister_notifier(&i2c_bus_type,
> > > +				&group->i2c_notifier);
> > > +
> > > +	mutex_lock(&group->v4l2_dev->group_lock);
> > > +	list_del(&group->list);
> > > +	mutex_unlock(&group->v4l2_dev->group_lock);
> > > +
> > > +	list_for_each_entry_safe(asd, tmp, &group->done, list) {
> > > +		v4l2_device_unregister_subdev(asd->sdpd.subdev);
> > > +		/* If we handled USB devices, we'd have to lock the parent too */
> > > +		device_release_driver(asd->dev);
> > > +		asd->dev->platform_data = NULL;
> > > +		if (device_attach(asd->dev) <= 0)
> > > +			dev_dbg(asd->dev, "Failed to re-probe to %s\n", asd->dev->driver ?
> > > +				asd->dev->driver->name : "(none)");
> > > +		list_del(&asd->list);
> > > +	}
> > > +}
> > > +EXPORT_SYMBOL(v4l2_async_group_release);
> > > diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
> > > index 513969f..52faf2f 100644
> > > --- a/drivers/media/v4l2-core/v4l2-device.c
> > > +++ b/drivers/media/v4l2-core/v4l2-device.c
> > > @@ -40,6 +40,8 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
> > >  	mutex_init(&v4l2_dev->ioctl_lock);
> > >  	v4l2_prio_init(&v4l2_dev->prio);
> > >  	kref_init(&v4l2_dev->ref);
> > > +	INIT_LIST_HEAD(&v4l2_dev->group_head);
> > > +	mutex_init(&v4l2_dev->group_lock);
> > >  	get_device(dev);
> > >  	v4l2_dev->dev = dev;
> > >  	if (dev == NULL) {
> > > diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> > > new file mode 100644
> > > index 0000000..8f7bc06
> > > --- /dev/null
> > > +++ b/include/media/v4l2-async.h
> > > @@ -0,0 +1,88 @@
> > > +/*
> > > + * V4L2 asynchronous subdevice registration API
> > > + *
> > > + * Copyright (C) 2012, Guennadi Liakhovetski <g.liakhovetski@xxxxxx>
> > > + *
> > > + * 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.
> > > + */
> > > +
> > > +#ifndef V4L2_ASYNC_H
> > > +#define V4L2_ASYNC_H
> > > +
> > > +#include <linux/list.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/notifier.h>
> > > +
> > > +#include <media/v4l2-subdev.h>
> > > +
> > > +struct device;
> > > +struct v4l2_device;
> > > +
> > > +enum v4l2_async_bus_type {
> > > +	V4L2_ASYNC_BUS_PLATFORM,
> > > +	V4L2_ASYNC_BUS_I2C,
> > > +};
> > > +
> > > +struct v4l2_async_hw_device {
> > > +	enum v4l2_async_bus_type bus_type;
> > > +	union {
> > > +		struct {
> > > +			const char *name;
> > > +		} platform;
> > > +		struct {
> > > +			int adapter_id;
> > > +			unsigned short address;
> > > +		} i2c;
> > > +	} match;
> > > +};
> > > +
> > > +/**
> > > + * struct v4l2_async_subdev - device descriptor
> > > + * @hw:		this device descriptor
> > > + * @list:	member in the group
> > > + * @dev:	corresponding hardware device (I2C, platform,...)
> > > + * @sdpd:	embedded subdevice platform data
> > > + * @role:	this subdevice role in the video pipeline
> > > + */
> > > +struct v4l2_async_subdev {
> > > +	struct v4l2_async_hw_device hw;
> > > +	struct list_head list;
> > > +	struct device *dev;
> > > +	struct v4l2_subdev_platform_data sdpd;
> > > +	enum v4l2_subdev_role role;
> > 
> > What's the purpose of 'role'? I don't see what this has to do with async
> > subdev registration.
> 
> Right, looks like it's not used by the generic code, I'll try to make it 
> soc-camera local in the next version

OK.

Regards,

	Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux