From 5bc3cb743bbab408792c1b4ef31adf6268aa4b7e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 14 Jun 2012 16:35:52 -0300 Subject: [media] v4l: move v4l2 core into a separate directory Currently, the v4l2 core is mixed together with other non-core drivers. Move them into a separate directory. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-device.c | 280 ++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 drivers/media/v4l2-core/v4l2-device.c (limited to 'drivers/media/v4l2-core/v4l2-device.c') diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c new file mode 100644 index 00000000000..1f203b85a63 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -0,0 +1,280 @@ +/* + V4L2 device support. + + Copyright (C) 2008 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#if defined(CONFIG_SPI) +#include +#endif +#include +#include +#include + +int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) +{ + if (v4l2_dev == NULL) + return -EINVAL; + + INIT_LIST_HEAD(&v4l2_dev->subdevs); + spin_lock_init(&v4l2_dev->lock); + mutex_init(&v4l2_dev->ioctl_lock); + v4l2_prio_init(&v4l2_dev->prio); + kref_init(&v4l2_dev->ref); + get_device(dev); + v4l2_dev->dev = dev; + if (dev == NULL) { + /* If dev == NULL, then name must be filled in by the caller */ + WARN_ON(!v4l2_dev->name[0]); + return 0; + } + + /* Set name to driver name + device name if it is empty. */ + if (!v4l2_dev->name[0]) + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + dev->driver->name, dev_name(dev)); + if (!dev_get_drvdata(dev)) + dev_set_drvdata(dev, v4l2_dev); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register); + +static void v4l2_device_release(struct kref *ref) +{ + struct v4l2_device *v4l2_dev = + container_of(ref, struct v4l2_device, ref); + + if (v4l2_dev->release) + v4l2_dev->release(v4l2_dev); +} + +int v4l2_device_put(struct v4l2_device *v4l2_dev) +{ + return kref_put(&v4l2_dev->ref, v4l2_device_release); +} +EXPORT_SYMBOL_GPL(v4l2_device_put); + +int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, + atomic_t *instance) +{ + int num = atomic_inc_return(instance) - 1; + int len = strlen(basename); + + if (basename[len - 1] >= '0' && basename[len - 1] <= '9') + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), + "%s-%d", basename, num); + else + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), + "%s%d", basename, num); + return num; +} +EXPORT_SYMBOL_GPL(v4l2_device_set_name); + +void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) +{ + if (v4l2_dev->dev == NULL) + return; + + if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) + dev_set_drvdata(v4l2_dev->dev, NULL); + put_device(v4l2_dev->dev); + v4l2_dev->dev = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_device_disconnect); + +void v4l2_device_unregister(struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd, *next; + + if (v4l2_dev == NULL) + return; + v4l2_device_disconnect(v4l2_dev); + + /* Unregister subdevs */ + list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { + v4l2_device_unregister_subdev(sd); +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) + if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* We need to unregister the i2c client explicitly. + We cannot rely on i2c_del_adapter to always + unregister clients for us, since if the i2c bus + is a platform bus, then it is never deleted. */ + if (client) + i2c_unregister_device(client); + continue; + } +#endif +#if defined(CONFIG_SPI) + if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { + struct spi_device *spi = v4l2_get_subdevdata(sd); + + if (spi) + spi_unregister_device(spi); + continue; + } +#endif + } +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister); + +int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, + struct v4l2_subdev *sd) +{ +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_entity *entity = &sd->entity; +#endif + int err; + + /* Check for valid input */ + if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) + return -EINVAL; + + /* Warn if we apparently re-register a subdev */ + WARN_ON(sd->v4l2_dev != NULL); + + if (!try_module_get(sd->owner)) + return -ENODEV; + + sd->v4l2_dev = v4l2_dev; + if (sd->internal_ops && sd->internal_ops->registered) { + err = sd->internal_ops->registered(sd); + if (err) { + module_put(sd->owner); + return err; + } + } + + /* This just returns 0 if either of the two args is NULL */ + err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); + if (err) { + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); + module_put(sd->owner); + return err; + } + +#if defined(CONFIG_MEDIA_CONTROLLER) + /* Register the entity. */ + if (v4l2_dev->mdev) { + err = media_device_register_entity(v4l2_dev->mdev, entity); + if (err < 0) { + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); + module_put(sd->owner); + return err; + } + } +#endif + + spin_lock(&v4l2_dev->lock); + list_add_tail(&sd->list, &v4l2_dev->subdevs); + spin_unlock(&v4l2_dev->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); + +static void v4l2_device_release_subdev_node(struct video_device *vdev) +{ + struct v4l2_subdev *sd = video_get_drvdata(vdev); + sd->devnode = NULL; + kfree(vdev); +} + +int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) +{ + struct video_device *vdev; + struct v4l2_subdev *sd; + int err; + + /* Register a device node for every subdev marked with the + * V4L2_SUBDEV_FL_HAS_DEVNODE flag. + */ + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) + continue; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + err = -ENOMEM; + goto clean_up; + } + + video_set_drvdata(vdev, sd); + strlcpy(vdev->name, sd->name, sizeof(vdev->name)); + vdev->v4l2_dev = v4l2_dev; + vdev->fops = &v4l2_subdev_fops; + vdev->release = v4l2_device_release_subdev_node; + vdev->ctrl_handler = sd->ctrl_handler; + err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, + sd->owner); + if (err < 0) { + kfree(vdev); + goto clean_up; + } +#if defined(CONFIG_MEDIA_CONTROLLER) + sd->entity.info.v4l.major = VIDEO_MAJOR; + sd->entity.info.v4l.minor = vdev->minor; +#endif + sd->devnode = vdev; + } + return 0; + +clean_up: + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + if (!sd->devnode) + break; + video_unregister_device(sd->devnode); + } + + return err; +} +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); + +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) +{ + struct v4l2_device *v4l2_dev; + + /* return if it isn't registered */ + if (sd == NULL || sd->v4l2_dev == NULL) + return; + + v4l2_dev = sd->v4l2_dev; + + spin_lock(&v4l2_dev->lock); + list_del(&sd->list); + spin_unlock(&v4l2_dev->lock); + + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); + sd->v4l2_dev = NULL; + +#if defined(CONFIG_MEDIA_CONTROLLER) + if (v4l2_dev->mdev) + media_device_unregister_entity(&sd->entity); +#endif + video_unregister_device(sd->devnode); + module_put(sd->owner); +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); -- cgit v1.2.3-70-g09d2 From 34a6b7d093d8fe738ada191b36648d00bc18b7eb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 14 Sep 2012 07:15:03 -0300 Subject: [media] v4l2-ctrls: add a filter function to v4l2_ctrl_add_handler With a filter function you can control more precisely which controls are added. This is useful in particular for radio device nodes for combined TV/Radio cards where you want to show just the radio-specific controls and not controls like brightness. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-controls.txt | 6 +++++- drivers/media/pci/cx88/cx88-blackbird.c | 2 +- drivers/media/pci/cx88/cx88-video.c | 2 +- drivers/media/platform/s5p-fimc/fimc-capture.c | 2 +- drivers/media/platform/soc_camera/soc_camera.c | 2 +- drivers/media/v4l2-core/v4l2-ctrls.c | 25 ++++++++++++++++++++++++- drivers/media/v4l2-core/v4l2-device.c | 2 +- include/media/v4l2-ctrls.h | 18 ++++++++++++++++-- 8 files changed, 50 insertions(+), 9 deletions(-) (limited to 'drivers/media/v4l2-core/v4l2-device.c') diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 43da22b8972..54270df99d5 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -594,7 +594,11 @@ handler and finally add the first handler to the second. For example: v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...); v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...); v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...); - v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler); + v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler, NULL); + +The last argument to v4l2_ctrl_add_handler() is a filter function that allows +you to filter which controls will be added. Set it to NULL if you want to add +all controls. Or you can add specific controls to a handler: diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index 843ffd9e533..def363fb71c 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -1236,7 +1236,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) err = cx2341x_handler_init(&dev->cxhdl, 36); if (err) goto fail_core; - v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl); + v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL); /* blackbird stuff */ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index f6fcc7e763a..a146d50d779 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1795,7 +1795,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, if (vc->id == V4L2_CID_CHROMA_AGC) core->chroma_agc = vc; } - v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl); + v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL); /* load and configure helper modules */ diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 40923885977..ac2ca36039e 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -472,7 +472,7 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc) return ret; return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler, - fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler); + fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler, NULL); } static int fimc_capture_set_default_format(struct fimc_dev *fimc); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index f6b1c1f8776..3be92944f8e 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1184,7 +1184,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) sd->grp_id = soc_camera_grp_id(icd); v4l2_set_subdev_hostdata(sd, icd); - if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler)) + if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL)) goto ectrl; /* At this point client .probe() should have run already */ diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f40003550b6..631cdc0e0bd 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1687,7 +1687,8 @@ EXPORT_SYMBOL(v4l2_ctrl_add_ctrl); /* Add the controls from another handler to our own. */ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, - struct v4l2_ctrl_handler *add) + struct v4l2_ctrl_handler *add, + bool (*filter)(const struct v4l2_ctrl *ctrl)) { struct v4l2_ctrl_ref *ref; int ret = 0; @@ -1707,6 +1708,9 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, /* And control classes */ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) continue; + /* Filter any unwanted controls */ + if (filter && !filter(ctrl)) + continue; ret = handler_new_ref(hdl, ctrl); if (ret) break; @@ -1716,6 +1720,25 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, } EXPORT_SYMBOL(v4l2_ctrl_add_handler); +bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl) +{ + if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_FM_TX) + return true; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return true; + default: + break; + } + return false; +} +EXPORT_SYMBOL(v4l2_ctrl_radio_filter); + /* Cluster controls */ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls) { diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 1f203b85a63..513969fa695 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -166,7 +166,7 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, } /* This just returns 0 if either of the two args is NULL */ - err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); + err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); if (err) { if (sd->internal_ops && sd->internal_ops->unregistered) sd->internal_ops->unregistered(sd); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 6890f5e11ad..801adb466bd 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -384,14 +384,28 @@ struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl, * @hdl: The control handler. * @add: The control handler whose controls you want to add to * the @hdl control handler. + * @filter: This function will filter which controls should be added. * - * Does nothing if either of the two is a NULL pointer. + * Does nothing if either of the two handlers is a NULL pointer. + * If @filter is NULL, then all controls are added. Otherwise only those + * controls for which @filter returns true will be added. * In case of an error @hdl->error will be set to the error code (if it * wasn't set already). */ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, - struct v4l2_ctrl_handler *add); + struct v4l2_ctrl_handler *add, + bool (*filter)(const struct v4l2_ctrl *ctrl)); +/** v4l2_ctrl_radio_filter() - Standard filter for radio controls. + * @ctrl: The control that is filtered. + * + * This will return true for any controls that are valid for radio device + * nodes. Those are all of the V4L2_CID_AUDIO_* user controls and all FM + * transmitter class controls. + * + * This function is to be used with v4l2_ctrl_add_handler(). + */ +bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl); /** v4l2_ctrl_cluster() - Mark all controls in the cluster as belonging to that cluster. * @ncontrols: The number of controls in this cluster. -- cgit v1.2.3-70-g09d2