summaryrefslogtreecommitdiff
path: root/drivers/media/v4l2-core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/v4l2-core')
-rw-r--r--drivers/media/v4l2-core/Makefile3
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c284
-rw-r--r--drivers/media/v4l2-core/v4l2-clk.c242
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c60
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c1
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c40
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c13
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c83
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-contig.c19
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c10
-rw-r--r--drivers/media/v4l2-core/videobuf-vmalloc.c10
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c4
12 files changed, 594 insertions, 175 deletions
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index aa50c46314b7..4c33b8d6520c 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-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 000000000000..aae241730caa
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -0,0 +1,284 @@
+/*
+ * V4L2 asynchronous subdevice registration API
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/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_subdev *asd)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ struct i2c_client *client = i2c_verify_client(dev);
+ return client &&
+ asd->bus_type == V4L2_ASYNC_BUS_I2C &&
+ asd->match.i2c.adapter_id == client->adapter->nr &&
+ asd->match.i2c.address == client->addr;
+#else
+ return false;
+#endif
+}
+
+static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd)
+{
+ return asd->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
+ !strcmp(asd->match.platform.name, dev_name(dev));
+}
+
+static LIST_HEAD(subdev_list);
+static LIST_HEAD(notifier_list);
+static DEFINE_MUTEX(list_lock);
+
+static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_subdev_list *asdl)
+{
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+ struct v4l2_async_subdev *asd;
+ bool (*match)(struct device *,
+ struct v4l2_async_subdev *);
+
+ list_for_each_entry(asd, &notifier->waiting, list) {
+ /* bus_type has been verified valid before */
+ switch (asd->bus_type) {
+ case V4L2_ASYNC_BUS_CUSTOM:
+ match = asd->match.custom.match;
+ if (!match)
+ /* Match always */
+ return asd;
+ break;
+ case V4L2_ASYNC_BUS_PLATFORM:
+ match = match_platform;
+ break;
+ case V4L2_ASYNC_BUS_I2C:
+ match = match_i2c;
+ break;
+ default:
+ /* Cannot happen, unless someone breaks us */
+ WARN_ON(true);
+ return NULL;
+ }
+
+ /* match cannot be NULL here */
+ if (match(sd->dev, asd))
+ return asd;
+ }
+
+ return NULL;
+}
+
+static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_subdev_list *asdl,
+ struct v4l2_async_subdev *asd)
+{
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+ int ret;
+
+ /* Remove from the waiting list */
+ list_del(&asd->list);
+ asdl->asd = asd;
+ asdl->notifier = notifier;
+
+ if (notifier->bound) {
+ ret = notifier->bound(notifier, sd, asd);
+ if (ret < 0)
+ return ret;
+ }
+ /* Move from the global subdevice list to notifier's done */
+ list_move(&asdl->list, &notifier->done);
+
+ ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
+ if (ret < 0) {
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, asd);
+ return ret;
+ }
+
+ if (list_empty(&notifier->waiting) && notifier->complete)
+ return notifier->complete(notifier);
+
+ return 0;
+}
+
+static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
+{
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+ v4l2_device_unregister_subdev(sd);
+ /* Subdevice driver will reprobe and put asdl back onto the list */
+ list_del_init(&asdl->list);
+ asdl->asd = NULL;
+ sd->dev = NULL;
+}
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+ struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_async_subdev_list *asdl, *tmp;
+ struct v4l2_async_subdev *asd;
+ int i;
+
+ if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
+ return -EINVAL;
+
+ notifier->v4l2_dev = v4l2_dev;
+ INIT_LIST_HEAD(&notifier->waiting);
+ INIT_LIST_HEAD(&notifier->done);
+
+ for (i = 0; i < notifier->num_subdevs; i++) {
+ asd = notifier->subdev[i];
+
+ switch (asd->bus_type) {
+ case V4L2_ASYNC_BUS_CUSTOM:
+ case V4L2_ASYNC_BUS_PLATFORM:
+ case V4L2_ASYNC_BUS_I2C:
+ break;
+ default:
+ dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
+ "Invalid bus-type %u on %p\n",
+ asd->bus_type, asd);
+ return -EINVAL;
+ }
+ list_add_tail(&asd->list, &notifier->waiting);
+ }
+
+ mutex_lock(&list_lock);
+
+ /* Keep also completed notifiers on the list */
+ list_add(&notifier->list, &notifier_list);
+
+ list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
+ int ret;
+
+ asd = v4l2_async_belongs(notifier, asdl);
+ if (!asd)
+ continue;
+
+ ret = v4l2_async_test_notify(notifier, asdl, asd);
+ if (ret < 0) {
+ mutex_unlock(&list_lock);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_async_notifier_register);
+
+void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_async_subdev_list *asdl, *tmp;
+ unsigned int notif_n_subdev = notifier->num_subdevs;
+ unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
+ struct device *dev[n_subdev];
+ int i = 0;
+
+ mutex_lock(&list_lock);
+
+ list_del(&notifier->list);
+
+ list_for_each_entry_safe(asdl, tmp, &notifier->done, list) {
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+ dev[i] = get_device(sd->dev);
+
+ v4l2_async_cleanup(asdl);
+
+ /* If we handled USB devices, we'd have to lock the parent too */
+ device_release_driver(dev[i++]);
+
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, sd->asdl.asd);
+ }
+
+ mutex_unlock(&list_lock);
+
+ while (i--) {
+ struct device *d = dev[i];
+
+ if (d && device_attach(d) < 0) {
+ const char *name = "(none)";
+ int lock = device_trylock(d);
+
+ if (lock && d->driver)
+ name = d->driver->name;
+ dev_err(d, "Failed to re-probe to %s\n", name);
+ if (lock)
+ device_unlock(d);
+ }
+ put_device(d);
+ }
+ /*
+ * Don't care about the waiting list, it is initialised and populated
+ * upon notifier registration.
+ */
+}
+EXPORT_SYMBOL(v4l2_async_notifier_unregister);
+
+int v4l2_async_register_subdev(struct v4l2_subdev *sd)
+{
+ struct v4l2_async_subdev_list *asdl = &sd->asdl;
+ struct v4l2_async_notifier *notifier;
+
+ mutex_lock(&list_lock);
+
+ INIT_LIST_HEAD(&asdl->list);
+
+ list_for_each_entry(notifier, &notifier_list, list) {
+ struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
+ if (asd) {
+ int ret = v4l2_async_test_notify(notifier, asdl, asd);
+ mutex_unlock(&list_lock);
+ return ret;
+ }
+ }
+
+ /* None matched, wait for hot-plugging */
+ list_add(&asdl->list, &subdev_list);
+
+ mutex_unlock(&list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_async_register_subdev);
+
+void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
+{
+ struct v4l2_async_subdev_list *asdl = &sd->asdl;
+ struct v4l2_async_notifier *notifier = asdl->notifier;
+
+ if (!asdl->asd) {
+ if (!list_empty(&asdl->list))
+ v4l2_async_cleanup(asdl);
+ return;
+ }
+
+ mutex_lock(&list_lock);
+
+ list_add(&asdl->asd->list, &notifier->waiting);
+
+ v4l2_async_cleanup(asdl);
+
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, sd->asdl.asd);
+
+ mutex_unlock(&list_lock);
+}
+EXPORT_SYMBOL(v4l2_async_unregister_subdev);
diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c
new file mode 100644
index 000000000000..b67de8642b5a
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-clk.c
@@ -0,0 +1,242 @@
+/*
+ * V4L2 clock service
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/atomic.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <media/v4l2-clk.h>
+#include <media/v4l2-subdev.h>
+
+static DEFINE_MUTEX(clk_lock);
+static LIST_HEAD(clk_list);
+
+static struct v4l2_clk *v4l2_clk_find(const char *dev_id, const char *id)
+{
+ struct v4l2_clk *clk;
+
+ list_for_each_entry(clk, &clk_list, list) {
+ if (strcmp(dev_id, clk->dev_id))
+ continue;
+
+ if (!id || !clk->id || !strcmp(clk->id, id))
+ return clk;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
+struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
+{
+ struct v4l2_clk *clk;
+
+ mutex_lock(&clk_lock);
+ clk = v4l2_clk_find(dev_name(dev), id);
+
+ if (!IS_ERR(clk))
+ atomic_inc(&clk->use_count);
+ mutex_unlock(&clk_lock);
+
+ return clk;
+}
+EXPORT_SYMBOL(v4l2_clk_get);
+
+void v4l2_clk_put(struct v4l2_clk *clk)
+{
+ struct v4l2_clk *tmp;
+
+ if (IS_ERR(clk))
+ return;
+
+ mutex_lock(&clk_lock);
+
+ list_for_each_entry(tmp, &clk_list, list)
+ if (tmp == clk)
+ atomic_dec(&clk->use_count);
+
+ mutex_unlock(&clk_lock);
+}
+EXPORT_SYMBOL(v4l2_clk_put);
+
+static int v4l2_clk_lock_driver(struct v4l2_clk *clk)
+{
+ struct v4l2_clk *tmp;
+ int ret = -ENODEV;
+
+ mutex_lock(&clk_lock);
+
+ list_for_each_entry(tmp, &clk_list, list)
+ if (tmp == clk) {
+ ret = !try_module_get(clk->ops->owner);
+ if (ret)
+ ret = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&clk_lock);
+
+ return ret;
+}
+
+static void v4l2_clk_unlock_driver(struct v4l2_clk *clk)
+{
+ module_put(clk->ops->owner);
+}
+
+int v4l2_clk_enable(struct v4l2_clk *clk)
+{
+ int ret = v4l2_clk_lock_driver(clk);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&clk->lock);
+
+ if (++clk->enable == 1 && clk->ops->enable) {
+ ret = clk->ops->enable(clk);
+ if (ret < 0)
+ clk->enable--;
+ }
+
+ mutex_unlock(&clk->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_enable);
+
+/*
+ * You might Oops if you try to disabled a disabled clock, because then the
+ * driver isn't locked and could have been unloaded by now, so, don't do that
+ */
+void v4l2_clk_disable(struct v4l2_clk *clk)
+{
+ int enable;
+
+ mutex_lock(&clk->lock);
+
+ enable = --clk->enable;
+ if (WARN(enable < 0, "Unbalanced %s() on %s:%s!\n", __func__,
+ clk->dev_id, clk->id))
+ clk->enable++;
+ else if (!enable && clk->ops->disable)
+ clk->ops->disable(clk);
+
+ mutex_unlock(&clk->lock);
+
+ v4l2_clk_unlock_driver(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_disable);
+
+unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk)
+{
+ int ret = v4l2_clk_lock_driver(clk);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&clk->lock);
+ if (!clk->ops->get_rate)
+ ret = -ENOSYS;
+ else
+ ret = clk->ops->get_rate(clk);
+ mutex_unlock(&clk->lock);
+
+ v4l2_clk_unlock_driver(clk);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_get_rate);
+
+int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate)
+{
+ int ret = v4l2_clk_lock_driver(clk);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&clk->lock);
+ if (!clk->ops->set_rate)
+ ret = -ENOSYS;
+ else
+ ret = clk->ops->set_rate(clk, rate);
+ mutex_unlock(&clk->lock);
+
+ v4l2_clk_unlock_driver(clk);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_set_rate);
+
+struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
+ const char *dev_id,
+ const char *id, void *priv)
+{
+ struct v4l2_clk *clk;
+ int ret;
+
+ if (!ops || !dev_id)
+ return ERR_PTR(-EINVAL);
+
+ clk = kzalloc(sizeof(struct v4l2_clk), GFP_KERNEL);
+ if (!clk)
+ return ERR_PTR(-ENOMEM);
+
+ clk->id = kstrdup(id, GFP_KERNEL);
+ clk->dev_id = kstrdup(dev_id, GFP_KERNEL);
+ if ((id && !clk->id) || !clk->dev_id) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+ clk->ops = ops;
+ clk->priv = priv;
+ atomic_set(&clk->use_count, 0);
+ mutex_init(&clk->lock);
+
+ mutex_lock(&clk_lock);
+ if (!IS_ERR(v4l2_clk_find(dev_id, id))) {
+ mutex_unlock(&clk_lock);
+ ret = -EEXIST;
+ goto eexist;
+ }
+ list_add_tail(&clk->list, &clk_list);
+ mutex_unlock(&clk_lock);
+
+ return clk;
+
+eexist:
+ealloc:
+ kfree(clk->id);
+ kfree(clk->dev_id);
+ kfree(clk);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(v4l2_clk_register);
+
+void v4l2_clk_unregister(struct v4l2_clk *clk)
+{
+ if (WARN(atomic_read(&clk->use_count),
+ "%s(): Refusing to unregister ref-counted %s:%s clock!\n",
+ __func__, clk->dev_id, clk->id))
+ return;
+
+ mutex_lock(&clk_lock);
+ list_del(&clk->list);
+ mutex_unlock(&clk_lock);
+
+ kfree(clk->id);
+ kfree(clk->dev_id);
+ kfree(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_unregister);
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 3fed63f4e026..a95e5e23403f 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -61,7 +61,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@@ -227,62 +226,9 @@ u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
}
EXPORT_SYMBOL(v4l2_ctrl_next);
-int v4l2_chip_match_host(const struct v4l2_dbg_match *match)
-{
- switch (match->type) {
- case V4L2_CHIP_MATCH_BRIDGE:
- return match->addr == 0;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL(v4l2_chip_match_host);
-
-#if IS_ENABLED(CONFIG_I2C)
-int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match)
-{
- int len;
-
- if (c == NULL || match == NULL)
- return 0;
-
- switch (match->type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- if (c->driver == NULL || c->driver->driver.name == NULL)
- return 0;
- len = strlen(c->driver->driver.name);
- return len && !strncmp(c->driver->driver.name, match->name, len);
- case V4L2_CHIP_MATCH_I2C_ADDR:
- return c->addr == match->addr;
- case V4L2_CHIP_MATCH_SUBDEV:
- return 1;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
-
-int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip,
- u32 ident, u32 revision)
-{
- if (!v4l2_chip_match_i2c_client(c, &chip->match))
- return 0;
- if (chip->ident == V4L2_IDENT_NONE) {
- chip->ident = ident;
- chip->revision = revision;
- }
- else {
- chip->ident = V4L2_IDENT_AMBIGUOUS;
- chip->revision = 0;
- }
- return 0;
-}
-EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
-
-/* ----------------------------------------------------------------- */
-
/* I2C Helper functions */
+#if IS_ENABLED(CONFIG_I2C)
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
@@ -291,6 +237,7 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
/* the owner is the same as the i2c_client's driver owner */
sd->owner = client->driver->driver.owner;
+ sd->dev = &client->dev;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, client);
i2c_set_clientdata(client, sd);
@@ -301,8 +248,6 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
}
EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
-
-
/* Load an i2c sub-device. */
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, struct i2c_board_info *info,
@@ -426,6 +371,7 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
/* the owner is the same as the spi_device's driver owner */
sd->owner = spi->dev.driver->owner;
+ sd->dev = &spi->dev;
/* spi_device and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, spi);
spi_set_drvdata(spi, sd);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index f1295519f285..8f7a6a454a4c 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -1074,7 +1074,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_TRY_DECODER_CMD:
case VIDIOC_DBG_S_REGISTER:
case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_G_CHIP_IDENT:
case VIDIOC_S_HW_FREQ_SEEK:
case VIDIOC_S_DV_TIMINGS:
case VIDIOC_G_DV_TIMINGS:
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 5923c5dfacd5..c8859d6ff6ad 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -495,8 +495,8 @@ static const struct file_operations v4l2_fops = {
};
/**
- * get_index - assign stream index number based on parent device
- * @vdev: video_device to assign index number to, vdev->parent should be assigned
+ * get_index - assign stream index number based on v4l2_dev
+ * @vdev: video_device to assign index number to, vdev->v4l2_dev should be assigned
*
* Note that when this is called the new device has not yet been registered
* in the video_device array, but it was able to obtain a minor number.
@@ -514,15 +514,11 @@ static int get_index(struct video_device *vdev)
static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
int i;
- /* Some drivers do not set the parent. In that case always return 0. */
- if (vdev->parent == NULL)
- return 0;
-
bitmap_zero(used, VIDEO_NUM_DEVICES);
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
if (video_device[i] != NULL &&
- video_device[i]->parent == vdev->parent) {
+ video_device[i]->v4l2_dev == vdev->v4l2_dev) {
set_bit(video_device[i]->index, used);
}
}
@@ -596,7 +592,6 @@ static void determine_valid_ioctls(struct video_device *vdev)
set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls);
set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls);
#endif
- SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
/* yes, really vidioc_subscribe_event */
SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
@@ -675,9 +670,8 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
if (ops->vidioc_s_std)
set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
- if (ops->vidioc_g_std || vdev->current_norm)
- set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
+ SET_VALID_IOCTL(ops, VIDIOC_G_STD, vidioc_g_std);
if (is_rx) {
SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd);
SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
@@ -705,7 +699,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
if (ops->vidioc_cropcap || ops->vidioc_g_selection)
set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
- (ops->vidioc_g_std || vdev->current_norm)))
+ ops->vidioc_g_std))
set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
@@ -777,6 +771,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
/* the release callback MUST be present */
if (WARN_ON(!vdev->release))
return -EINVAL;
+ /* the v4l2_dev pointer MUST be present */
+ if (WARN_ON(!vdev->v4l2_dev))
+ return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
@@ -804,16 +801,14 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->vfl_type = type;
vdev->cdev = NULL;
- if (vdev->v4l2_dev) {
- if (vdev->v4l2_dev->dev)
- vdev->parent = vdev->v4l2_dev->dev;
- if (vdev->ctrl_handler == NULL)
- vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
- /* If the prio state pointer is NULL, then use the v4l2_device
- prio state. */
- if (vdev->prio == NULL)
- vdev->prio = &vdev->v4l2_dev->prio;
- }
+ if (vdev->dev_parent == NULL)
+ vdev->dev_parent = vdev->v4l2_dev->dev;
+ if (vdev->ctrl_handler == NULL)
+ vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+ /* If the prio state pointer is NULL, then use the v4l2_device
+ prio state. */
+ if (vdev->prio == NULL)
+ vdev->prio = &vdev->v4l2_dev->prio;
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
@@ -898,8 +893,7 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
- if (vdev->parent)
- vdev->dev.parent = vdev->parent;
+ vdev->dev.parent = vdev->dev_parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 8ed5da2170bf..02d1b6327117 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -44,7 +44,8 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_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]);
+ if (WARN_ON(!v4l2_dev->name[0]))
+ return -EINVAL;
return 0;
}
@@ -105,7 +106,9 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
{
struct v4l2_subdev *sd, *next;
- if (v4l2_dev == NULL)
+ /* Just return if v4l2_dev is NULL or if it was already
+ * unregistered before. */
+ if (v4l2_dev == NULL || !v4l2_dev->name[0])
return;
v4l2_device_disconnect(v4l2_dev);
@@ -135,6 +138,8 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
}
#endif
}
+ /* Mark as unregistered, thus preventing duplicate unregistrations */
+ v4l2_dev->name[0] = '\0';
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister);
@@ -269,8 +274,10 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
sd->v4l2_dev = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (v4l2_dev->mdev)
+ if (v4l2_dev->mdev) {
+ media_entity_remove_links(&sd->entity);
media_device_unregister_entity(&sd->entity);
+ }
#endif
video_unregister_device(sd->devnode);
module_put(sd->owner);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 7658586fe5f4..68e6b5e912ff 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -26,7 +26,6 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/videobuf2-core.h>
/* Zero out the end of the struct pointed to by p. Everything after, but
@@ -619,20 +618,6 @@ static void v4l_print_decoder_cmd(const void *arg, bool write_only)
pr_info("pts=%llu\n", p->stop.pts);
}
-static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
-{
- const struct v4l2_dbg_chip_ident *p = arg;
-
- pr_cont("type=%u, ", p->match.type);
- if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
- pr_cont("name=%.*s, ",
- (int)sizeof(p->match.name), p->match.name);
- else
- pr_cont("addr=%u, ", p->match.addr);
- pr_cont("chip_ident=%u, revision=0x%x\n",
- p->ident, p->revision);
-}
-
static void v4l_print_dbg_chip_info(const void *arg, bool write_only)
{
const struct v4l2_dbg_chip_info *p = arg;
@@ -1359,40 +1344,18 @@ static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
return 0;
}
-static int v4l_g_std(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- v4l2_std_id *id = arg;
-
- /* Calls the specific handler */
- if (ops->vidioc_g_std)
- return ops->vidioc_g_std(file, fh, arg);
- if (vfd->current_norm) {
- *id = vfd->current_norm;
- return 0;
- }
- return -ENOTTY;
-}
-
static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct video_device *vfd = video_devdata(file);
v4l2_std_id id = *(v4l2_std_id *)arg, norm;
- int ret;
norm = id & vfd->tvnorms;
if (vfd->tvnorms && !norm) /* Check if std is supported */
return -EINVAL;
/* Calls the specific handler */
- ret = ops->vidioc_s_std(file, fh, norm);
-
- /* Updates standard information */
- if (ret >= 0)
- vfd->current_norm = norm;
- return ret;
+ return ops->vidioc_s_std(file, fh, norm);
}
static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
@@ -1402,10 +1365,10 @@ static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
v4l2_std_id *p = arg;
/*
- * If nothing detected, it should return all supported
- * standard.
- * Drivers just need to mask the std argument, in order
- * to remove the standards that don't apply from the mask.
+ * If no signal is detected, then the driver should return
+ * V4L2_STD_UNKNOWN. Otherwise it should return tvnorms with
+ * any standards that do not apply removed.
+ *
* This means that tuners, audio and video decoders can join
* their efforts to improve the standards detection.
*/
@@ -1495,7 +1458,6 @@ static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
- struct video_device *vfd = video_devdata(file);
struct v4l2_streamparm *p = arg;
v4l2_std_id std;
int ret = check_fmt(file, p->type);
@@ -1504,16 +1466,13 @@ static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
return ret;
if (ops->vidioc_g_parm)
return ops->vidioc_g_parm(file, fh, p);
- std = vfd->current_norm;
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
p->parm.capture.readbuffers = 2;
- if (is_valid_ioctl(vfd, VIDIOC_G_STD) && ops->vidioc_g_std)
- ret = ops->vidioc_g_std(file, fh, &std);
+ ret = ops->vidioc_g_std(file, fh, &std);
if (ret == 0)
- v4l2_video_std_frame_period(std,
- &p->parm.capture.timeperframe);
+ v4l2_video_std_frame_period(std, &p->parm.capture.timeperframe);
return ret;
}
@@ -1802,7 +1761,8 @@ static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
return v4l2_subdev_call(sd, core, g_register, p);
return -EINVAL;
}
- if (ops->vidioc_g_register)
+ if (ops->vidioc_g_register && p->match.type == V4L2_CHIP_MATCH_BRIDGE &&
+ (ops->vidioc_g_chip_info || p->match.addr == 0))
return ops->vidioc_g_register(file, fh, p);
return -EINVAL;
#else
@@ -1829,7 +1789,8 @@ static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
return v4l2_subdev_call(sd, core, s_register, p);
return -EINVAL;
}
- if (ops->vidioc_s_register)
+ if (ops->vidioc_s_register && p->match.type == V4L2_CHIP_MATCH_BRIDGE &&
+ (ops->vidioc_g_chip_info || p->match.addr == 0))
return ops->vidioc_s_register(file, fh, p);
return -EINVAL;
#else
@@ -1837,18 +1798,6 @@ static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
#endif
}
-static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_dbg_chip_ident *p = arg;
-
- p->ident = V4L2_IDENT_NONE;
- p->revision = 0;
- if (p->match.type == V4L2_CHIP_MATCH_SUBDEV)
- return -EINVAL;
- return ops->vidioc_g_chip_ident(file, fh, p);
-}
-
static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
@@ -1864,12 +1813,7 @@ static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
p->flags |= V4L2_CHIP_FL_WRITABLE;
if (ops->vidioc_g_register)
p->flags |= V4L2_CHIP_FL_READABLE;
- if (vfd->v4l2_dev)
- strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name));
- else if (vfd->parent)
- strlcpy(p->name, vfd->parent->driver->name, sizeof(p->name));
- else
- strlcpy(p->name, "bridge", sizeof(p->name));
+ strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name));
if (ops->vidioc_g_chip_info)
return ops->vidioc_g_chip_info(file, fh, arg);
if (p->match.addr)
@@ -2048,7 +1992,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0),
+ IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0),
IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
@@ -2098,7 +2042,6 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
index 67f572c3fba2..65411adcd0ea 100644
--- a/drivers/media/v4l2-core/videobuf-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf-dma-contig.c
@@ -66,11 +66,14 @@ static void __videobuf_dc_free(struct device *dev,
static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
- dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+ dev_dbg(q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
map, map->count, vma->vm_start, vma->vm_end);
+ videobuf_queue_lock(q);
map->count++;
+ videobuf_queue_unlock(q);
}
static void videobuf_vm_close(struct vm_area_struct *vma)
@@ -82,12 +85,11 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
map, map->count, vma->vm_start, vma->vm_end);
- map->count--;
- if (0 == map->count) {
+ videobuf_queue_lock(q);
+ if (!--map->count) {
struct videobuf_dma_contig_memory *mem;
dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
/* We need first to cancel streams, before unmapping */
if (q->streaming)
@@ -126,8 +128,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
kfree(map);
- videobuf_queue_unlock(q);
}
+ videobuf_queue_unlock(q);
}
static const struct vm_operations_struct videobuf_vm_ops = {
@@ -303,14 +305,9 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
goto error;
/* Try to remap memory */
-
size = vma->vm_end - vma->vm_start;
- size = (size < mem->size) ? size : mem->size;
-
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- retval = remap_pfn_range(vma, vma->vm_start,
- mem->dma_handle >> PAGE_SHIFT,
- size, vma->vm_page_prot);
+ retval = vm_iomap_memory(vma, vma->vm_start, size);
if (retval) {
dev_err(q->dev, "mmap: remap failed with error %d. ",
retval);
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 828e7c10bd70..9db674ccdc68 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -338,11 +338,14 @@ EXPORT_SYMBOL_GPL(videobuf_dma_free);
static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
+ videobuf_queue_lock(q);
map->count++;
+ videobuf_queue_unlock(q);
}
static void videobuf_vm_close(struct vm_area_struct *vma)
@@ -355,10 +358,9 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
- map->count--;
- if (0 == map->count) {
+ videobuf_queue_lock(q);
+ if (!--map->count) {
dprintk(1, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
@@ -374,9 +376,9 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
q->bufs[i]->baddr = 0;
q->ops->buf_release(q, q->bufs[i]);
}
- videobuf_queue_unlock(q);
kfree(map);
}
+ videobuf_queue_unlock(q);
return;
}
diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c
index 2ff7fcc77b11..1365c651c177 100644
--- a/drivers/media/v4l2-core/videobuf-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf-vmalloc.c
@@ -54,11 +54,14 @@ MODULE_LICENSE("GPL");
static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
+ videobuf_queue_lock(q);
map->count++;
+ videobuf_queue_unlock(q);
}
static void videobuf_vm_close(struct vm_area_struct *vma)
@@ -70,12 +73,11 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
- map->count--;
- if (0 == map->count) {
+ videobuf_queue_lock(q);
+ if (!--map->count) {
struct videobuf_vmalloc_memory *mem;
dprintk(1, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
/* We need first to cancel streams, before unmapping */
if (q->streaming)
@@ -114,8 +116,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
kfree(map);
- videobuf_queue_unlock(q);
}
+ videobuf_queue_unlock(q);
return;
}
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index e3bdc3be91e1..9fc4bab2da97 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -2194,8 +2194,10 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
*/
for (i = 0; i < q->num_buffers; i++) {
fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
- if (fileio->bufs[i].vaddr == NULL)
+ if (fileio->bufs[i].vaddr == NULL) {
+ ret = -EINVAL;
goto err_reqbufs;
+ }
fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
}