]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
[media] v4l: move v4l2 core into a separate directory
authorMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 14 Jun 2012 19:35:52 +0000 (16:35 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 14 Aug 2012 02:02:38 +0000 (23:02 -0300)
Currently, the v4l2 core is mixed together with other non-core drivers.
Move them into a separate directory.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
50 files changed:
drivers/media/Kconfig
drivers/media/Makefile
drivers/media/v4l2-core/Kconfig [new file with mode: 0644]
drivers/media/v4l2-core/Makefile [new file with mode: 0644]
drivers/media/v4l2-core/tuner-core.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-common.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-compat-ioctl32.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-ctrls.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-dev.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-device.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-event.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-fh.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-int-device.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-ioctl.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-mem2mem.c [new file with mode: 0644]
drivers/media/v4l2-core/v4l2-subdev.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf-core.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf-dma-contig.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf-dma-sg.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf-dvb.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf-vmalloc.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf2-core.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf2-dma-contig.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf2-dma-sg.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf2-memops.c [new file with mode: 0644]
drivers/media/v4l2-core/videobuf2-vmalloc.c [new file with mode: 0644]
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/tuner-core.c [deleted file]
drivers/media/video/v4l2-common.c [deleted file]
drivers/media/video/v4l2-compat-ioctl32.c [deleted file]
drivers/media/video/v4l2-ctrls.c [deleted file]
drivers/media/video/v4l2-dev.c [deleted file]
drivers/media/video/v4l2-device.c [deleted file]
drivers/media/video/v4l2-event.c [deleted file]
drivers/media/video/v4l2-fh.c [deleted file]
drivers/media/video/v4l2-int-device.c [deleted file]
drivers/media/video/v4l2-ioctl.c [deleted file]
drivers/media/video/v4l2-mem2mem.c [deleted file]
drivers/media/video/v4l2-subdev.c [deleted file]
drivers/media/video/videobuf-core.c [deleted file]
drivers/media/video/videobuf-dma-contig.c [deleted file]
drivers/media/video/videobuf-dma-sg.c [deleted file]
drivers/media/video/videobuf-dvb.c [deleted file]
drivers/media/video/videobuf-vmalloc.c [deleted file]
drivers/media/video/videobuf2-core.c [deleted file]
drivers/media/video/videobuf2-dma-contig.c [deleted file]
drivers/media/video/videobuf2-dma-sg.c [deleted file]
drivers/media/video/videobuf2-memops.c [deleted file]
drivers/media/video/videobuf2-vmalloc.c [deleted file]

index d941581ab92169c935a85754c6fc28d2bc2dd79a..e6253628059ac88ecb61a43a9571d4936ebeb4d2 100644 (file)
@@ -152,6 +152,7 @@ source "drivers/media/common/tuners/Kconfig"
 # Video/Radio/Hybrid adapters
 #
 
+source "drivers/media/v4l2-core/Kconfig"
 source "drivers/media/video/Kconfig"
 
 source "drivers/media/radio/Kconfig"
index 64755c99ded291e526a8dd7fa5a83e73496486e4..2f9abaad18f75b583de8cd7515690895d422e153 100644 (file)
@@ -8,7 +8,7 @@ ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
   obj-$(CONFIG_MEDIA_SUPPORT) += media.o
 endif
 
-obj-y += common/ rc/ video/
+obj-y += v4l2-core/ common/ rc/ video/
 
 obj-$(CONFIG_VIDEO_DEV) += radio/
 obj-$(CONFIG_DVB_CORE)  += dvb/
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
new file mode 100644 (file)
index 0000000..6f53337
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# Generic video config states
+#
+
+config VIDEO_V4L2
+       tristate
+       depends on VIDEO_DEV && VIDEO_V4L2_COMMON
+       default y
+
+config VIDEOBUF_GEN
+       tristate
+
+config VIDEOBUF_DMA_SG
+       depends on HAS_DMA
+       select VIDEOBUF_GEN
+       tristate
+
+config VIDEOBUF_VMALLOC
+       select VIDEOBUF_GEN
+       tristate
+
+config VIDEOBUF_DMA_CONTIG
+       depends on HAS_DMA
+       select VIDEOBUF_GEN
+       tristate
+
+config VIDEOBUF_DVB
+       tristate
+       select VIDEOBUF_GEN
+
+config VIDEO_TUNER
+       tristate
+       depends on MEDIA_TUNER
+
+config V4L2_MEM2MEM_DEV
+        tristate
+        depends on VIDEOBUF2_CORE
+
+config VIDEOBUF2_CORE
+       tristate
+
+config VIDEOBUF2_MEMOPS
+       tristate
+
+config VIDEOBUF2_DMA_CONTIG
+       select VIDEOBUF2_CORE
+       select VIDEOBUF2_MEMOPS
+       tristate
+
+config VIDEOBUF2_VMALLOC
+       select VIDEOBUF2_CORE
+       select VIDEOBUF2_MEMOPS
+       tristate
+
+config VIDEOBUF2_DMA_SG
+       #depends on HAS_DMA
+       select VIDEOBUF2_CORE
+       select VIDEOBUF2_MEMOPS
+       tristate
+
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
new file mode 100644 (file)
index 0000000..7319c27
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Makefile for the V4L2 core
+#
+
+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
+ifeq ($(CONFIG_COMPAT),y)
+  videodev-objs += v4l2-compat-ioctl32.o
+endif
+
+obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
+obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
+
+obj-$(CONFIG_VIDEO_TUNER) += tuner.o
+
+obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
+
+obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
+obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
+obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
+obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
+obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
+
+obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o
+obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o
+obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o
+obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o
+obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o
+
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+
diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
new file mode 100644 (file)
index 0000000..b5a819a
--- /dev/null
@@ -0,0 +1,1354 @@
+/*
+ * i2c tv tuner chip device driver
+ * core core, i.e. kernel interfaces, registering and so on
+ *
+ * Copyright(c) by Ralph Metzler, Gerd Knorr, Gunther Mayer
+ *
+ * Copyright(c) 2005-2011 by Mauro Carvalho Chehab
+ *     - Added support for a separate Radio tuner
+ *     - Major rework and cleanups at the code
+ *
+ * This driver supports many devices and the idea is to let the driver
+ * detect which device is present. So rather than listing all supported
+ * devices here, we pretend to support a single, fake device type that will
+ * handle both radio and analog TV tuning.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/videodev2.h>
+#include <media/tuner.h>
+#include <media/tuner-types.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include "mt20xx.h"
+#include "tda8290.h"
+#include "tea5761.h"
+#include "tea5767.h"
+#include "tuner-xc2028.h"
+#include "tuner-simple.h"
+#include "tda9887.h"
+#include "xc5000.h"
+#include "tda18271.h"
+#include "xc4000.h"
+
+#define UNSET (-1U)
+
+#define PREFIX (t->i2c->driver->driver.name)
+
+/*
+ * Driver modprobe parameters
+ */
+
+/* insmod options used at init time => read/only */
+static unsigned int addr;
+static unsigned int no_autodetect;
+static unsigned int show_i2c;
+
+module_param(addr, int, 0444);
+module_param(no_autodetect, int, 0444);
+module_param(show_i2c, int, 0444);
+
+/* insmod options used at runtime => read/write */
+static int tuner_debug;
+static unsigned int tv_range[2] = { 44, 958 };
+static unsigned int radio_range[2] = { 65, 108 };
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+module_param_named(debug, tuner_debug, int, 0644);
+module_param_array(tv_range, int, NULL, 0644);
+module_param_array(radio_range, int, NULL, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+
+/*
+ * Static vars
+ */
+
+static LIST_HEAD(tuner_list);
+static const struct v4l2_subdev_ops tuner_ops;
+
+/*
+ * Debug macros
+ */
+
+#define tuner_warn(fmt, arg...) do {                   \
+       printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
+              i2c_adapter_id(t->i2c->adapter),         \
+              t->i2c->addr, ##arg);                    \
+        } while (0)
+
+#define tuner_info(fmt, arg...) do {                   \
+       printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX,    \
+              i2c_adapter_id(t->i2c->adapter),         \
+              t->i2c->addr, ##arg);                    \
+        } while (0)
+
+#define tuner_err(fmt, arg...) do {                    \
+       printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX,     \
+              i2c_adapter_id(t->i2c->adapter),         \
+              t->i2c->addr, ##arg);                    \
+        } while (0)
+
+#define tuner_dbg(fmt, arg...) do {                            \
+       if (tuner_debug)                                        \
+               printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX,   \
+                      i2c_adapter_id(t->i2c->adapter),         \
+                      t->i2c->addr, ##arg);                    \
+        } while (0)
+
+/*
+ * Internal struct used inside the driver
+ */
+
+struct tuner {
+       /* device */
+       struct dvb_frontend fe;
+       struct i2c_client   *i2c;
+       struct v4l2_subdev  sd;
+       struct list_head    list;
+
+       /* keep track of the current settings */
+       v4l2_std_id         std;
+       unsigned int        tv_freq;
+       unsigned int        radio_freq;
+       unsigned int        audmode;
+
+       enum v4l2_tuner_type mode;
+       unsigned int        mode_mask; /* Combination of allowable modes */
+
+       bool                standby;    /* Standby mode */
+
+       unsigned int        type; /* chip type id */
+       unsigned int        config;
+       const char          *name;
+};
+
+/*
+ * Function prototypes
+ */
+
+static void set_tv_freq(struct i2c_client *c, unsigned int freq);
+static void set_radio_freq(struct i2c_client *c, unsigned int freq);
+
+/*
+ * tuner attach/detach logic
+ */
+
+/* This macro allows us to probe dynamically, avoiding static links */
+#ifdef CONFIG_MEDIA_ATTACH
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+       int __r = -EINVAL; \
+       typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+       if (__a) { \
+               __r = (int) __a(ARGS); \
+               symbol_put(FUNCTION); \
+       } else { \
+               printk(KERN_ERR "TUNER: Unable to find " \
+                               "symbol "#FUNCTION"()\n"); \
+       } \
+       __r; \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+       if (fe->ops.tuner_ops.release) {
+               fe->ops.tuner_ops.release(fe);
+               symbol_put_addr(fe->ops.tuner_ops.release);
+       }
+       if (fe->ops.analog_ops.release) {
+               fe->ops.analog_ops.release(fe);
+               symbol_put_addr(fe->ops.analog_ops.release);
+       }
+}
+#else
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+       FUNCTION(ARGS); \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+       if (fe->ops.tuner_ops.release)
+               fe->ops.tuner_ops.release(fe);
+       if (fe->ops.analog_ops.release)
+               fe->ops.analog_ops.release(fe);
+}
+#endif
+
+
+static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct tuner, sd);
+}
+
+/*
+ * struct analog_demod_ops callbacks
+ */
+
+static void fe_set_params(struct dvb_frontend *fe,
+                         struct analog_parameters *params)
+{
+       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+       struct tuner *t = fe->analog_demod_priv;
+
+       if (NULL == fe_tuner_ops->set_analog_params) {
+               tuner_warn("Tuner frontend module has no way to set freq\n");
+               return;
+       }
+       fe_tuner_ops->set_analog_params(fe, params);
+}
+
+static void fe_standby(struct dvb_frontend *fe)
+{
+       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+
+       if (fe_tuner_ops->sleep)
+               fe_tuner_ops->sleep(fe);
+}
+
+static int fe_has_signal(struct dvb_frontend *fe)
+{
+       u16 strength = 0;
+
+       if (fe->ops.tuner_ops.get_rf_strength)
+               fe->ops.tuner_ops.get_rf_strength(fe, &strength);
+
+       return strength;
+}
+
+static int fe_get_afc(struct dvb_frontend *fe)
+{
+       s32 afc = 0;
+
+       if (fe->ops.tuner_ops.get_afc)
+               fe->ops.tuner_ops.get_afc(fe, &afc);
+
+       return 0;
+}
+
+static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+       struct tuner *t = fe->analog_demod_priv;
+
+       if (fe_tuner_ops->set_config)
+               return fe_tuner_ops->set_config(fe, priv_cfg);
+
+       tuner_warn("Tuner frontend module has no way to set config\n");
+
+       return 0;
+}
+
+static void tuner_status(struct dvb_frontend *fe);
+
+static struct analog_demod_ops tuner_analog_ops = {
+       .set_params     = fe_set_params,
+       .standby        = fe_standby,
+       .has_signal     = fe_has_signal,
+       .get_afc        = fe_get_afc,
+       .set_config     = fe_set_config,
+       .tuner_status   = tuner_status
+};
+
+/*
+ * Functions to select between radio and TV and tuner probe/remove functions
+ */
+
+/**
+ * set_type - Sets the tuner type for a given device
+ *
+ * @c:                 i2c_client descriptoy
+ * @type:              type of the tuner (e. g. tuner number)
+ * @new_mode_mask:     Indicates if tuner supports TV and/or Radio
+ * @new_config:                an optional parameter ranging from 0-255 used by
+                       a few tuners to adjust an internal parameter,
+                       like LNA mode
+ * @tuner_callback:    an optional function to be called when switching
+ *                     to analog mode
+ *
+ * This function applys the tuner config to tuner specified
+ * by tun_setup structure. It contains several per-tuner initialization "magic"
+ */
+static void set_type(struct i2c_client *c, unsigned int type,
+                    unsigned int new_mode_mask, unsigned int new_config,
+                    int (*tuner_callback) (void *dev, int component, int cmd, int arg))
+{
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
+       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+       unsigned char buffer[4];
+       int tune_now = 1;
+
+       if (type == UNSET || type == TUNER_ABSENT) {
+               tuner_dbg("tuner 0x%02x: Tuner type absent\n", c->addr);
+               return;
+       }
+
+       t->type = type;
+       /* prevent invalid config values */
+       t->config = new_config < 256 ? new_config : 0;
+       if (tuner_callback != NULL) {
+               tuner_dbg("defining GPIO callback\n");
+               t->fe.callback = tuner_callback;
+       }
+
+       /* discard private data, in case set_type() was previously called */
+       tuner_detach(&t->fe);
+       t->fe.analog_demod_priv = NULL;
+
+       switch (t->type) {
+       case TUNER_MT2032:
+               if (!dvb_attach(microtune_attach,
+                          &t->fe, t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
+               break;
+       case TUNER_PHILIPS_TDA8290:
+       {
+               struct tda829x_config cfg = {
+                       .lna_cfg        = t->config,
+               };
+               if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter,
+                               t->i2c->addr, &cfg))
+                       goto attach_failed;
+               break;
+       }
+       case TUNER_TEA5767:
+               if (!dvb_attach(tea5767_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
+               t->mode_mask = T_RADIO;
+               break;
+       case TUNER_TEA5761:
+               if (!dvb_attach(tea5761_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
+               t->mode_mask = T_RADIO;
+               break;
+       case TUNER_PHILIPS_FMD1216ME_MK3:
+       case TUNER_PHILIPS_FMD1216MEX_MK3:
+               buffer[0] = 0x0b;
+               buffer[1] = 0xdc;
+               buffer[2] = 0x9c;
+               buffer[3] = 0x60;
+               i2c_master_send(c, buffer, 4);
+               mdelay(1);
+               buffer[2] = 0x86;
+               buffer[3] = 0x54;
+               i2c_master_send(c, buffer, 4);
+               if (!dvb_attach(simple_tuner_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr, t->type))
+                       goto attach_failed;
+               break;
+       case TUNER_PHILIPS_TD1316:
+               buffer[0] = 0x0b;
+               buffer[1] = 0xdc;
+               buffer[2] = 0x86;
+               buffer[3] = 0xa4;
+               i2c_master_send(c, buffer, 4);
+               if (!dvb_attach(simple_tuner_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr, t->type))
+                       goto attach_failed;
+               break;
+       case TUNER_XC2028:
+       {
+               struct xc2028_config cfg = {
+                       .i2c_adap  = t->i2c->adapter,
+                       .i2c_addr  = t->i2c->addr,
+               };
+               if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
+                       goto attach_failed;
+               tune_now = 0;
+               break;
+       }
+       case TUNER_TDA9887:
+               if (!dvb_attach(tda9887_attach,
+                          &t->fe, t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
+               break;
+       case TUNER_XC5000:
+       {
+               struct xc5000_config xc5000_cfg = {
+                       .i2c_address = t->i2c->addr,
+                       /* if_khz will be set at dvb_attach() */
+                       .if_khz   = 0,
+               };
+
+               if (!dvb_attach(xc5000_attach,
+                               &t->fe, t->i2c->adapter, &xc5000_cfg))
+                       goto attach_failed;
+               tune_now = 0;
+               break;
+       }
+       case TUNER_XC5000C:
+       {
+               struct xc5000_config xc5000c_cfg = {
+                       .i2c_address = t->i2c->addr,
+                       /* if_khz will be set at dvb_attach() */
+                       .if_khz   = 0,
+                       .chip_id  = XC5000C,
+               };
+
+               if (!dvb_attach(xc5000_attach,
+                               &t->fe, t->i2c->adapter, &xc5000c_cfg))
+                       goto attach_failed;
+               tune_now = 0;
+               break;
+       }
+       case TUNER_NXP_TDA18271:
+       {
+               struct tda18271_config cfg = {
+                       .config = t->config,
+                       .small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
+               };
+
+               if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr,
+                               t->i2c->adapter, &cfg))
+                       goto attach_failed;
+               tune_now = 0;
+               break;
+       }
+       case TUNER_XC4000:
+       {
+               struct xc4000_config xc4000_cfg = {
+                       .i2c_address      = t->i2c->addr,
+                       /* FIXME: the correct parameters will be set */
+                       /* only when the digital dvb_attach() occurs */
+                       .default_pm       = 0,
+                       .dvb_amplitude    = 0,
+                       .set_smoothedcvbs = 0,
+                       .if_khz           = 0
+               };
+               if (!dvb_attach(xc4000_attach,
+                               &t->fe, t->i2c->adapter, &xc4000_cfg))
+                       goto attach_failed;
+               tune_now = 0;
+               break;
+       }
+       default:
+               if (!dvb_attach(simple_tuner_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr, t->type))
+                       goto attach_failed;
+
+               break;
+       }
+
+       if ((NULL == analog_ops->set_params) &&
+           (fe_tuner_ops->set_analog_params)) {
+
+               t->name = fe_tuner_ops->info.name;
+
+               t->fe.analog_demod_priv = t;
+               memcpy(analog_ops, &tuner_analog_ops,
+                      sizeof(struct analog_demod_ops));
+
+       } else {
+               t->name = analog_ops->info.name;
+       }
+
+       tuner_dbg("type set to %s\n", t->name);
+
+       t->mode_mask = new_mode_mask;
+
+       /* Some tuners require more initialization setup before use,
+          such as firmware download or device calibration.
+          trying to set a frequency here will just fail
+          FIXME: better to move set_freq to the tuner code. This is needed
+          on analog tuners for PLL to properly work
+        */
+       if (tune_now) {
+               if (V4L2_TUNER_RADIO == t->mode)
+                       set_radio_freq(c, t->radio_freq);
+               else
+                       set_tv_freq(c, t->tv_freq);
+       }
+
+       tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
+                 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
+                 t->mode_mask);
+       return;
+
+attach_failed:
+       tuner_dbg("Tuner attach for type = %d failed.\n", t->type);
+       t->type = TUNER_ABSENT;
+
+       return;
+}
+
+/**
+ * tuner_s_type_addr - Sets the tuner type for a device
+ *
+ * @sd:                subdev descriptor
+ * @tun_setup: type to be associated to a given tuner i2c address
+ *
+ * This function applys the tuner config to tuner specified
+ * by tun_setup structure.
+ * If tuner I2C address is UNSET, then it will only set the device
+ * if the tuner supports the mode specified in the call.
+ * If the address is specified, the change will be applied only if
+ * tuner I2C address matches.
+ * The call can change the tuner number and the tuner mode.
+ */
+static int tuner_s_type_addr(struct v4l2_subdev *sd,
+                            struct tuner_setup *tun_setup)
+{
+       struct tuner *t = to_tuner(sd);
+       struct i2c_client *c = v4l2_get_subdevdata(sd);
+
+       tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
+                       tun_setup->type,
+                       tun_setup->addr,
+                       tun_setup->mode_mask,
+                       tun_setup->config);
+
+       if ((t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
+           (t->mode_mask & tun_setup->mode_mask))) ||
+           (tun_setup->addr == c->addr)) {
+               set_type(c, tun_setup->type, tun_setup->mode_mask,
+                        tun_setup->config, tun_setup->tuner_callback);
+       } else
+               tuner_dbg("set addr discarded for type %i, mask %x. "
+                         "Asked to change tuner at addr 0x%02x, with mask %x\n",
+                         t->type, t->mode_mask,
+                         tun_setup->addr, tun_setup->mode_mask);
+
+       return 0;
+}
+
+/**
+ * tuner_s_config - Sets tuner configuration
+ *
+ * @sd:                subdev descriptor
+ * @cfg:       tuner configuration
+ *
+ * Calls tuner set_config() private function to set some tuner-internal
+ * parameters
+ */
+static int tuner_s_config(struct v4l2_subdev *sd,
+                         const struct v4l2_priv_tun_config *cfg)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       if (t->type != cfg->tuner)
+               return 0;
+
+       if (analog_ops->set_config) {
+               analog_ops->set_config(&t->fe, cfg->priv);
+               return 0;
+       }
+
+       tuner_dbg("Tuner frontend module has no way to set config\n");
+       return 0;
+}
+
+/**
+ * tuner_lookup - Seek for tuner adapters
+ *
+ * @adap:      i2c_adapter struct
+ * @radio:     pointer to be filled if the adapter is radio
+ * @tv:                pointer to be filled if the adapter is TV
+ *
+ * Search for existing radio and/or TV tuners on the given I2C adapter,
+ * discarding demod-only adapters (tda9887).
+ *
+ * Note that when this function is called from tuner_probe you can be
+ * certain no other devices will be added/deleted at the same time, I2C
+ * core protects against that.
+ */
+static void tuner_lookup(struct i2c_adapter *adap,
+               struct tuner **radio, struct tuner **tv)
+{
+       struct tuner *pos;
+
+       *radio = NULL;
+       *tv = NULL;
+
+       list_for_each_entry(pos, &tuner_list, list) {
+               int mode_mask;
+
+               if (pos->i2c->adapter != adap ||
+                   strcmp(pos->i2c->driver->driver.name, "tuner"))
+                       continue;
+
+               mode_mask = pos->mode_mask;
+               if (*radio == NULL && mode_mask == T_RADIO)
+                       *radio = pos;
+               /* Note: currently TDA9887 is the only demod-only
+                  device. If other devices appear then we need to
+                  make this test more general. */
+               else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
+                        (pos->mode_mask & T_ANALOG_TV))
+                       *tv = pos;
+       }
+}
+
+/**
+ *tuner_probe - Probes the existing tuners on an I2C bus
+ *
+ * @client:    i2c_client descriptor
+ * @id:                not used
+ *
+ * This routine probes for tuners at the expected I2C addresses. On most
+ * cases, if a device answers to a given I2C address, it assumes that the
+ * device is a tuner. On a few cases, however, an additional logic is needed
+ * to double check if the device is really a tuner, or to identify the tuner
+ * type, like on tea5767/5761 devices.
+ *
+ * During client attach, set_type is called by adapter's attach_inform callback.
+ * set_type must then be completed by tuner_probe.
+ */
+static int tuner_probe(struct i2c_client *client,
+                      const struct i2c_device_id *id)
+{
+       struct tuner *t;
+       struct tuner *radio;
+       struct tuner *tv;
+
+       t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
+       if (NULL == t)
+               return -ENOMEM;
+       v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
+       t->i2c = client;
+       t->name = "(tuner unset)";
+       t->type = UNSET;
+       t->audmode = V4L2_TUNER_MODE_STEREO;
+       t->standby = 1;
+       t->radio_freq = 87.5 * 16000;   /* Initial freq range */
+       t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */
+
+       if (show_i2c) {
+               unsigned char buffer[16];
+               int i, rc;
+
+               memset(buffer, 0, sizeof(buffer));
+               rc = i2c_master_recv(client, buffer, sizeof(buffer));
+               tuner_info("I2C RECV = ");
+               for (i = 0; i < rc; i++)
+                       printk(KERN_CONT "%02x ", buffer[i]);
+               printk("\n");
+       }
+
+       /* autodetection code based on the i2c addr */
+       if (!no_autodetect) {
+               switch (client->addr) {
+               case 0x10:
+                       if (tuner_symbol_probe(tea5761_autodetection,
+                                              t->i2c->adapter,
+                                              t->i2c->addr) >= 0) {
+                               t->type = TUNER_TEA5761;
+                               t->mode_mask = T_RADIO;
+                               tuner_lookup(t->i2c->adapter, &radio, &tv);
+                               if (tv)
+                                       tv->mode_mask &= ~T_RADIO;
+
+                               goto register_client;
+                       }
+                       kfree(t);
+                       return -ENODEV;
+               case 0x42:
+               case 0x43:
+               case 0x4a:
+               case 0x4b:
+                       /* If chip is not tda8290, don't register.
+                          since it can be tda9887*/
+                       if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
+                                              t->i2c->addr) >= 0) {
+                               tuner_dbg("tda829x detected\n");
+                       } else {
+                               /* Default is being tda9887 */
+                               t->type = TUNER_TDA9887;
+                               t->mode_mask = T_RADIO | T_ANALOG_TV;
+                               goto register_client;
+                       }
+                       break;
+               case 0x60:
+                       if (tuner_symbol_probe(tea5767_autodetection,
+                                              t->i2c->adapter, t->i2c->addr)
+                                       >= 0) {
+                               t->type = TUNER_TEA5767;
+                               t->mode_mask = T_RADIO;
+                               /* Sets freq to FM range */
+                               tuner_lookup(t->i2c->adapter, &radio, &tv);
+                               if (tv)
+                                       tv->mode_mask &= ~T_RADIO;
+
+                               goto register_client;
+                       }
+                       break;
+               }
+       }
+
+       /* Initializes only the first TV tuner on this adapter. Why only the
+          first? Because there are some devices (notably the ones with TI
+          tuners) that have more than one i2c address for the *same* device.
+          Experience shows that, except for just one case, the first
+          address is the right one. The exception is a Russian tuner
+          (ACORP_Y878F). So, the desired behavior is just to enable the
+          first found TV tuner. */
+       tuner_lookup(t->i2c->adapter, &radio, &tv);
+       if (tv == NULL) {
+               t->mode_mask = T_ANALOG_TV;
+               if (radio == NULL)
+                       t->mode_mask |= T_RADIO;
+               tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
+       }
+
+       /* Should be just before return */
+register_client:
+       /* Sets a default mode */
+       if (t->mode_mask & T_ANALOG_TV)
+               t->mode = V4L2_TUNER_ANALOG_TV;
+       else
+               t->mode = V4L2_TUNER_RADIO;
+       set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
+       list_add_tail(&t->list, &tuner_list);
+
+       tuner_info("Tuner %d found with type(s)%s%s.\n",
+                  t->type,
+                  t->mode_mask & T_RADIO ? " Radio" : "",
+                  t->mode_mask & T_ANALOG_TV ? " TV" : "");
+       return 0;
+}
+
+/**
+ * tuner_remove - detaches a tuner
+ *
+ * @client:    i2c_client descriptor
+ */
+
+static int tuner_remove(struct i2c_client *client)
+{
+       struct tuner *t = to_tuner(i2c_get_clientdata(client));
+
+       v4l2_device_unregister_subdev(&t->sd);
+       tuner_detach(&t->fe);
+       t->fe.analog_demod_priv = NULL;
+
+       list_del(&t->list);
+       kfree(t);
+       return 0;
+}
+
+/*
+ * Functions to switch between Radio and TV
+ *
+ * A few cards have a separate I2C tuner for radio. Those routines
+ * take care of switching between TV/Radio mode, filtering only the
+ * commands that apply to the Radio or TV tuner.
+ */
+
+/**
+ * check_mode - Verify if tuner supports the requested mode
+ * @t: a pointer to the module's internal struct_tuner
+ *
+ * This function checks if the tuner is capable of tuning analog TV,
+ * digital TV or radio, depending on what the caller wants. If the
+ * tuner can't support that mode, it returns -EINVAL. Otherwise, it
+ * returns 0.
+ * This function is needed for boards that have a separate tuner for
+ * radio (like devices with tea5767).
+ * NOTE: mt20xx uses V4L2_TUNER_DIGITAL_TV and calls set_tv_freq to
+ *       select a TV frequency. So, t_mode = T_ANALOG_TV could actually
+ *      be used to represent a Digital TV too.
+ */
+static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode)
+{
+       int t_mode;
+       if (mode == V4L2_TUNER_RADIO)
+               t_mode = T_RADIO;
+       else
+               t_mode = T_ANALOG_TV;
+
+       if ((t_mode & t->mode_mask) == 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * set_mode - Switch tuner to other mode.
+ * @t:         a pointer to the module's internal struct_tuner
+ * @mode:      enum v4l2_type (radio or TV)
+ *
+ * If tuner doesn't support the needed mode (radio or TV), prints a
+ * debug message and returns -EINVAL, changing its state to standby.
+ * Otherwise, changes the mode and returns 0.
+ */
+static int set_mode(struct tuner *t, enum v4l2_tuner_type mode)
+{
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       if (mode != t->mode) {
+               if (check_mode(t, mode) == -EINVAL) {
+                       tuner_dbg("Tuner doesn't support mode %d. "
+                                 "Putting tuner to sleep\n", mode);
+                       t->standby = true;
+                       if (analog_ops->standby)
+                               analog_ops->standby(&t->fe);
+                       return -EINVAL;
+               }
+               t->mode = mode;
+               tuner_dbg("Changing to mode %d\n", mode);
+       }
+       return 0;
+}
+
+/**
+ * set_freq - Set the tuner to the desired frequency.
+ * @t:         a pointer to the module's internal struct_tuner
+ * @freq:      frequency to set (0 means to use the current frequency)
+ */
+static void set_freq(struct tuner *t, unsigned int freq)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
+
+       if (t->mode == V4L2_TUNER_RADIO) {
+               if (!freq)
+                       freq = t->radio_freq;
+               set_radio_freq(client, freq);
+       } else {
+               if (!freq)
+                       freq = t->tv_freq;
+               set_tv_freq(client, freq);
+       }
+}
+
+/*
+ * Functions that are specific for TV mode
+ */
+
+/**
+ * set_tv_freq - Set tuner frequency,  freq in Units of 62.5 kHz = 1/16MHz
+ *
+ * @c: i2c_client descriptor
+ * @freq: frequency
+ */
+static void set_tv_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       struct analog_parameters params = {
+               .mode      = t->mode,
+               .audmode   = t->audmode,
+               .std       = t->std
+       };
+
+       if (t->type == UNSET) {
+               tuner_warn("tuner type not set\n");
+               return;
+       }
+       if (NULL == analog_ops->set_params) {
+               tuner_warn("Tuner has no way to set tv freq\n");
+               return;
+       }
+       if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
+               tuner_dbg("TV freq (%d.%02d) out of range (%d-%d)\n",
+                          freq / 16, freq % 16 * 100 / 16, tv_range[0],
+                          tv_range[1]);
+               /* V4L2 spec: if the freq is not possible then the closest
+                  possible value should be selected */
+               if (freq < tv_range[0] * 16)
+                       freq = tv_range[0] * 16;
+               else
+                       freq = tv_range[1] * 16;
+       }
+       params.frequency = freq;
+       tuner_dbg("tv freq set to %d.%02d\n",
+                       freq / 16, freq % 16 * 100 / 16);
+       t->tv_freq = freq;
+       t->standby = false;
+
+       analog_ops->set_params(&t->fe, &params);
+}
+
+/**
+ * tuner_fixup_std - force a given video standard variant
+ *
+ * @t: tuner internal struct
+ * @std:       TV standard
+ *
+ * A few devices or drivers have problem to detect some standard variations.
+ * On other operational systems, the drivers generally have a per-country
+ * code, and some logic to apply per-country hacks. V4L2 API doesn't provide
+ * such hacks. Instead, it relies on a proper video standard selection from
+ * the userspace application. However, as some apps are buggy, not allowing
+ * to distinguish all video standard variations, a modprobe parameter can
+ * be used to force a video standard match.
+ */
+static v4l2_std_id tuner_fixup_std(struct tuner *t, v4l2_std_id std)
+{
+       if (pal[0] != '-' && (std & V4L2_STD_PAL) == V4L2_STD_PAL) {
+               switch (pal[0]) {
+               case '6':
+                       return V4L2_STD_PAL_60;
+               case 'b':
+               case 'B':
+               case 'g':
+               case 'G':
+                       return V4L2_STD_PAL_BG;
+               case 'i':
+               case 'I':
+                       return V4L2_STD_PAL_I;
+               case 'd':
+               case 'D':
+               case 'k':
+               case 'K':
+                       return V4L2_STD_PAL_DK;
+               case 'M':
+               case 'm':
+                       return V4L2_STD_PAL_M;
+               case 'N':
+               case 'n':
+                       if (pal[1] == 'c' || pal[1] == 'C')
+                               return V4L2_STD_PAL_Nc;
+                       return V4L2_STD_PAL_N;
+               default:
+                       tuner_warn("pal= argument not recognised\n");
+                       break;
+               }
+       }
+       if (secam[0] != '-' && (std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
+               switch (secam[0]) {
+               case 'b':
+               case 'B':
+               case 'g':
+               case 'G':
+               case 'h':
+               case 'H':
+                       return V4L2_STD_SECAM_B |
+                              V4L2_STD_SECAM_G |
+                              V4L2_STD_SECAM_H;
+               case 'd':
+               case 'D':
+               case 'k':
+               case 'K':
+                       return V4L2_STD_SECAM_DK;
+               case 'l':
+               case 'L':
+                       if ((secam[1] == 'C') || (secam[1] == 'c'))
+                               return V4L2_STD_SECAM_LC;
+                       return V4L2_STD_SECAM_L;
+               default:
+                       tuner_warn("secam= argument not recognised\n");
+                       break;
+               }
+       }
+
+       if (ntsc[0] != '-' && (std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
+               switch (ntsc[0]) {
+               case 'm':
+               case 'M':
+                       return V4L2_STD_NTSC_M;
+               case 'j':
+               case 'J':
+                       return V4L2_STD_NTSC_M_JP;
+               case 'k':
+               case 'K':
+                       return V4L2_STD_NTSC_M_KR;
+               default:
+                       tuner_info("ntsc= argument not recognised\n");
+                       break;
+               }
+       }
+       return std;
+}
+
+/*
+ * Functions that are specific for Radio mode
+ */
+
+/**
+ * set_radio_freq - Set tuner frequency,  freq in Units of 62.5 Hz  = 1/16kHz
+ *
+ * @c: i2c_client descriptor
+ * @freq: frequency
+ */
+static void set_radio_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       struct analog_parameters params = {
+               .mode      = t->mode,
+               .audmode   = t->audmode,
+               .std       = t->std
+       };
+
+       if (t->type == UNSET) {
+               tuner_warn("tuner type not set\n");
+               return;
+       }
+       if (NULL == analog_ops->set_params) {
+               tuner_warn("tuner has no way to set radio frequency\n");
+               return;
+       }
+       if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
+               tuner_dbg("radio freq (%d.%02d) out of range (%d-%d)\n",
+                          freq / 16000, freq % 16000 * 100 / 16000,
+                          radio_range[0], radio_range[1]);
+               /* V4L2 spec: if the freq is not possible then the closest
+                  possible value should be selected */
+               if (freq < radio_range[0] * 16000)
+                       freq = radio_range[0] * 16000;
+               else
+                       freq = radio_range[1] * 16000;
+       }
+       params.frequency = freq;
+       tuner_dbg("radio freq set to %d.%02d\n",
+                       freq / 16000, freq % 16000 * 100 / 16000);
+       t->radio_freq = freq;
+       t->standby = false;
+
+       analog_ops->set_params(&t->fe, &params);
+}
+
+/*
+ * Debug function for reporting tuner status to userspace
+ */
+
+/**
+ * tuner_status - Dumps the current tuner status at dmesg
+ * @fe: pointer to struct dvb_frontend
+ *
+ * This callback is used only for driver debug purposes, answering to
+ * VIDIOC_LOG_STATUS. No changes should happen on this call.
+ */
+static void tuner_status(struct dvb_frontend *fe)
+{
+       struct tuner *t = fe->analog_demod_priv;
+       unsigned long freq, freq_fraction;
+       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+       struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
+       const char *p;
+
+       switch (t->mode) {
+       case V4L2_TUNER_RADIO:
+               p = "radio";
+               break;
+       case V4L2_TUNER_DIGITAL_TV: /* Used by mt20xx */
+               p = "digital TV";
+               break;
+       case V4L2_TUNER_ANALOG_TV:
+       default:
+               p = "analog TV";
+               break;
+       }
+       if (t->mode == V4L2_TUNER_RADIO) {
+               freq = t->radio_freq / 16000;
+               freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
+       } else {
+               freq = t->tv_freq / 16;
+               freq_fraction = (t->tv_freq % 16) * 100 / 16;
+       }
+       tuner_info("Tuner mode:      %s%s\n", p,
+                  t->standby ? " on standby mode" : "");
+       tuner_info("Frequency:       %lu.%02lu MHz\n", freq, freq_fraction);
+       tuner_info("Standard:        0x%08lx\n", (unsigned long)t->std);
+       if (t->mode != V4L2_TUNER_RADIO)
+               return;
+       if (fe_tuner_ops->get_status) {
+               u32 tuner_status;
+
+               fe_tuner_ops->get_status(&t->fe, &tuner_status);
+               if (tuner_status & TUNER_STATUS_LOCKED)
+                       tuner_info("Tuner is locked.\n");
+               if (tuner_status & TUNER_STATUS_STEREO)
+                       tuner_info("Stereo:          yes\n");
+       }
+       if (analog_ops->has_signal)
+               tuner_info("Signal strength: %d\n",
+                          analog_ops->has_signal(fe));
+}
+
+/*
+ * Function to splicitly change mode to radio. Probably not needed anymore
+ */
+
+static int tuner_s_radio(struct v4l2_subdev *sd)
+{
+       struct tuner *t = to_tuner(sd);
+
+       if (set_mode(t, V4L2_TUNER_RADIO) == 0)
+               set_freq(t, 0);
+       return 0;
+}
+
+/*
+ * Tuner callbacks to handle userspace ioctl's
+ */
+
+/**
+ * tuner_s_power - controls the power state of the tuner
+ * @sd: pointer to struct v4l2_subdev
+ * @on: a zero value puts the tuner to sleep, non-zero wakes it up
+ */
+static int tuner_s_power(struct v4l2_subdev *sd, int on)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       if (on) {
+               if (t->standby && set_mode(t, t->mode) == 0) {
+                       tuner_dbg("Waking up tuner\n");
+                       set_freq(t, 0);
+               }
+               return 0;
+       }
+
+       tuner_dbg("Putting tuner to sleep\n");
+       t->standby = true;
+       if (analog_ops->standby)
+               analog_ops->standby(&t->fe);
+       return 0;
+}
+
+static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+       struct tuner *t = to_tuner(sd);
+
+       if (set_mode(t, V4L2_TUNER_ANALOG_TV))
+               return 0;
+
+       t->std = tuner_fixup_std(t, std);
+       if (t->std != std)
+               tuner_dbg("Fixup standard %llx to %llx\n", std, t->std);
+       set_freq(t, 0);
+       return 0;
+}
+
+static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+       struct tuner *t = to_tuner(sd);
+
+       if (set_mode(t, f->type) == 0)
+               set_freq(t, f->frequency);
+       return 0;
+}
+
+/**
+ * tuner_g_frequency - Get the tuned frequency for the tuner
+ * @sd: pointer to struct v4l2_subdev
+ * @f: pointer to struct v4l2_frequency
+ *
+ * At return, the structure f will be filled with tuner frequency
+ * if the tuner matches the f->type.
+ * Note: f->type should be initialized before calling it.
+ * This is done by either video_ioctl2 or by the bridge driver.
+ */
+static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+       struct tuner *t = to_tuner(sd);
+       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+       if (check_mode(t, f->type) == -EINVAL)
+               return 0;
+       if (f->type == t->mode && fe_tuner_ops->get_frequency && !t->standby) {
+               u32 abs_freq;
+
+               fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
+               f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+                       DIV_ROUND_CLOSEST(abs_freq * 2, 125) :
+                       DIV_ROUND_CLOSEST(abs_freq, 62500);
+       } else {
+               f->frequency = (V4L2_TUNER_RADIO == f->type) ?
+                       t->radio_freq : t->tv_freq;
+       }
+       return 0;
+}
+
+/**
+ * tuner_g_tuner - Fill in tuner information
+ * @sd: pointer to struct v4l2_subdev
+ * @vt: pointer to struct v4l2_tuner
+ *
+ * At return, the structure vt will be filled with tuner information
+ * if the tuner matches vt->type.
+ * Note: vt->type should be initialized before calling it.
+ * This is done by either video_ioctl2 or by the bridge driver.
+ */
+static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+       if (check_mode(t, vt->type) == -EINVAL)
+               return 0;
+       if (vt->type == t->mode && analog_ops->get_afc)
+               vt->afc = analog_ops->get_afc(&t->fe);
+       if (analog_ops->has_signal)
+               vt->signal = analog_ops->has_signal(&t->fe);
+       if (vt->type != V4L2_TUNER_RADIO) {
+               vt->capability |= V4L2_TUNER_CAP_NORM;
+               vt->rangelow = tv_range[0] * 16;
+               vt->rangehigh = tv_range[1] * 16;
+               return 0;
+       }
+
+       /* radio mode */
+       if (vt->type == t->mode) {
+               vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+               if (fe_tuner_ops->get_status) {
+                       u32 tuner_status;
+
+                       fe_tuner_ops->get_status(&t->fe, &tuner_status);
+                       vt->rxsubchans =
+                               (tuner_status & TUNER_STATUS_STEREO) ?
+                               V4L2_TUNER_SUB_STEREO :
+                               V4L2_TUNER_SUB_MONO;
+               }
+               vt->audmode = t->audmode;
+       }
+       vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+       vt->rangelow = radio_range[0] * 16000;
+       vt->rangehigh = radio_range[1] * 16000;
+
+       return 0;
+}
+
+/**
+ * tuner_s_tuner - Set the tuner's audio mode
+ * @sd: pointer to struct v4l2_subdev
+ * @vt: pointer to struct v4l2_tuner
+ *
+ * Sets the audio mode if the tuner matches vt->type.
+ * Note: vt->type should be initialized before calling it.
+ * This is done by either video_ioctl2 or by the bridge driver.
+ */
+static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+       struct tuner *t = to_tuner(sd);
+
+       if (set_mode(t, vt->type))
+               return 0;
+
+       if (t->mode == V4L2_TUNER_RADIO)
+               t->audmode = vt->audmode;
+       set_freq(t, 0);
+
+       return 0;
+}
+
+static int tuner_log_status(struct v4l2_subdev *sd)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       if (analog_ops->tuner_status)
+               analog_ops->tuner_status(&t->fe);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tuner_suspend(struct device *dev)
+{
+       struct i2c_client *c = to_i2c_client(dev);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       tuner_dbg("suspend\n");
+
+       if (!t->standby && analog_ops->standby)
+               analog_ops->standby(&t->fe);
+
+       return 0;
+}
+
+static int tuner_resume(struct device *dev)
+{
+       struct i2c_client *c = to_i2c_client(dev);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
+
+       tuner_dbg("resume\n");
+
+       if (!t->standby)
+               if (set_mode(t, t->mode) == 0)
+                       set_freq(t, 0);
+
+       return 0;
+}
+#endif
+
+static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+       /* TUNER_SET_CONFIG is still called by tuner-simple.c, so we have
+          to handle it here.
+          There must be a better way of doing this... */
+       switch (cmd) {
+       case TUNER_SET_CONFIG:
+               return tuner_s_config(sd, arg);
+       }
+       return -ENOIOCTLCMD;
+}
+
+/*
+ * Callback structs
+ */
+
+static const struct v4l2_subdev_core_ops tuner_core_ops = {
+       .log_status = tuner_log_status,
+       .s_std = tuner_s_std,
+       .s_power = tuner_s_power,
+};
+
+static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+       .s_radio = tuner_s_radio,
+       .g_tuner = tuner_g_tuner,
+       .s_tuner = tuner_s_tuner,
+       .s_frequency = tuner_s_frequency,
+       .g_frequency = tuner_g_frequency,
+       .s_type_addr = tuner_s_type_addr,
+       .s_config = tuner_s_config,
+};
+
+static const struct v4l2_subdev_ops tuner_ops = {
+       .core = &tuner_core_ops,
+       .tuner = &tuner_tuner_ops,
+};
+
+/*
+ * I2C structs and module init functions
+ */
+
+static const struct dev_pm_ops tuner_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(tuner_suspend, tuner_resume)
+};
+
+static const struct i2c_device_id tuner_id[] = {
+       { "tuner", }, /* autodetect */
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tuner_id);
+
+static struct i2c_driver tuner_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "tuner",
+               .pm     = &tuner_pm_ops,
+       },
+       .probe          = tuner_probe,
+       .remove         = tuner_remove,
+       .command        = tuner_command,
+       .id_table       = tuner_id,
+};
+
+module_i2c_driver(tuner_driver);
+
+MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
+MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
new file mode 100644 (file)
index 0000000..105f88c
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ *     Video for Linux Two
+ *
+ *     A generic video device interface for the LINUX operating system
+ *     using a set of device structures/vectors for low level operations.
+ *
+ *     This file replaces the videodev.c file that comes with the
+ *     regular kernel distribution.
+ *
+ *     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.
+ *
+ * Author:     Bill Dirks <bill@thedirks.org>
+ *             based on code by Alan Cox, <alan@cymru.net>
+ *
+ */
+
+/*
+ * Video capture interface for Linux
+ *
+ *     A generic video device interface for the LINUX operating system
+ *     using a set of device structures/vectors for low level operations.
+ *
+ *             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.
+ *
+ * Author:     Alan Cox, <alan@lxorguk.ukuu.org.uk>
+ *
+ * Fixes:
+ */
+
+/*
+ * Video4linux 1/2 integration by Justin Schoeman
+ * <justin@suntiger.ee.up.ac.za>
+ * 2.4 PROCFS support ported from 2.4 kernels by
+ *  Iñaki García Etxebarria <garetxe@euskalnet.net>
+ * Makefile fix by "W. Michael Petullo" <mike@flyn.org>
+ * 2.4 devfs support ported from 2.4 kernels by
+ *  Dan Merillat <dan@merillat.org>
+ * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman)
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#if defined(CONFIG_SPI)
+#include <linux/spi/spi.h>
+#endif
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/div64.h>
+#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>
+
+MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr");
+MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers");
+MODULE_LICENSE("GPL");
+
+/*
+ *
+ *     V 4 L 2   D R I V E R   H E L P E R   A P I
+ *
+ */
+
+/*
+ *  Video Standard Operations (contributed by Michael Schimek)
+ */
+
+/* Helper functions for control handling                            */
+
+/* Check for correctness of the ctrl's value based on the data from
+   struct v4l2_queryctrl and the available menu items. Note that
+   menu_items may be NULL, in that case it is ignored. */
+int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
+               const char * const *menu_items)
+{
+       if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+               return -EINVAL;
+       if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+               return -EBUSY;
+       if (qctrl->type == V4L2_CTRL_TYPE_STRING)
+               return 0;
+       if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+           qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
+           qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+               return 0;
+       if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
+               return -ERANGE;
+       if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
+               if (menu_items[ctrl->value] == NULL ||
+                   menu_items[ctrl->value][0] == '\0')
+                       return -EINVAL;
+       }
+       if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+                       (ctrl->value & ~qctrl->maximum))
+               return -ERANGE;
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_check);
+
+/* Fill in a struct v4l2_queryctrl */
+int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
+{
+       const char *name;
+
+       v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
+                      &min, &max, &step, &def, &qctrl->flags);
+
+       if (name == NULL)
+               return -EINVAL;
+
+       qctrl->minimum = min;
+       qctrl->maximum = max;
+       qctrl->step = step;
+       qctrl->default_value = def;
+       qctrl->reserved[0] = qctrl->reserved[1] = 0;
+       strlcpy(qctrl->name, name, sizeof(qctrl->name));
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_fill);
+
+/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
+   the menu. The qctrl pointer may be NULL, in which case it is ignored.
+   If menu_items is NULL, then the menu items are retrieved using
+   v4l2_ctrl_get_menu. */
+int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl,
+              const char * const *menu_items)
+{
+       int i;
+
+       qmenu->reserved = 0;
+       if (menu_items == NULL)
+               menu_items = v4l2_ctrl_get_menu(qmenu->id);
+       if (menu_items == NULL ||
+           (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum)))
+               return -EINVAL;
+       for (i = 0; i < qmenu->index && menu_items[i]; i++) ;
+       if (menu_items[i] == NULL || menu_items[i][0] == '\0')
+               return -EINVAL;
+       strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name));
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_menu);
+
+/* Fill in a struct v4l2_querymenu based on the specified array of valid
+   menu items (terminated by V4L2_CTRL_MENU_IDS_END).
+   Use this if there are 'holes' in the list of valid menu items. */
+int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids)
+{
+       const char * const *menu_items = v4l2_ctrl_get_menu(qmenu->id);
+
+       qmenu->reserved = 0;
+       if (menu_items == NULL || ids == NULL)
+               return -EINVAL;
+       while (*ids != V4L2_CTRL_MENU_IDS_END) {
+               if (*ids++ == qmenu->index) {
+                       strlcpy(qmenu->name, menu_items[qmenu->index],
+                                       sizeof(qmenu->name));
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
+
+/* ctrl_classes points to an array of u32 pointers, the last element is
+   a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
+   Each array must be sorted low to high and belong to the same control
+   class. The array of u32 pointers must also be sorted, from low class IDs
+   to high class IDs.
+
+   This function returns the first ID that follows after the given ID.
+   When no more controls are available 0 is returned. */
+u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
+{
+       u32 ctrl_class = V4L2_CTRL_ID2CLASS(id);
+       const u32 *pctrl;
+
+       if (ctrl_classes == NULL)
+               return 0;
+
+       /* if no query is desired, then check if the ID is part of ctrl_classes */
+       if ((id & V4L2_CTRL_FLAG_NEXT_CTRL) == 0) {
+               /* find class */
+               while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) != ctrl_class)
+                       ctrl_classes++;
+               if (*ctrl_classes == NULL)
+                       return 0;
+               pctrl = *ctrl_classes;
+               /* find control ID */
+               while (*pctrl && *pctrl != id) pctrl++;
+               return *pctrl ? id : 0;
+       }
+       id &= V4L2_CTRL_ID_MASK;
+       id++;   /* select next control */
+       /* find first class that matches (or is greater than) the class of
+          the ID */
+       while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) < ctrl_class)
+               ctrl_classes++;
+       /* no more classes */
+       if (*ctrl_classes == NULL)
+               return 0;
+       pctrl = *ctrl_classes;
+       /* find first ctrl within the class that is >= ID */
+       while (*pctrl && *pctrl < id) pctrl++;
+       if (*pctrl)
+               return *pctrl;
+       /* we are at the end of the controls of the current class. */
+       /* continue with next class if available */
+       ctrl_classes++;
+       if (*ctrl_classes == NULL)
+               return 0;
+       return **ctrl_classes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_next);
+
+int v4l2_chip_match_host(const struct v4l2_dbg_match *match)
+{
+       switch (match->type) {
+       case V4L2_CHIP_MATCH_HOST:
+               return match->addr == 0;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL(v4l2_chip_match_host);
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+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);
+               /* legacy drivers have a ' suffix, don't try to match that */
+               if (len && c->driver->driver.name[len - 1] == '\'')
+                       len--;
+               return len && !strncmp(c->driver->driver.name, match->name, len);
+       case V4L2_CHIP_MATCH_I2C_ADDR:
+               return c->addr == match->addr;
+       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 */
+
+
+void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+               const struct v4l2_subdev_ops *ops)
+{
+       v4l2_subdev_init(sd, ops);
+       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;
+       /* i2c_client and v4l2_subdev point to one another */
+       v4l2_set_subdevdata(sd, client);
+       i2c_set_clientdata(client, sd);
+       /* initialize name */
+       snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+               client->driver->driver.name, i2c_adapter_id(client->adapter),
+               client->addr);
+}
+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,
+               const unsigned short *probe_addrs)
+{
+       struct v4l2_subdev *sd = NULL;
+       struct i2c_client *client;
+
+       BUG_ON(!v4l2_dev);
+
+       request_module(I2C_MODULE_PREFIX "%s", info->type);
+
+       /* Create the i2c client */
+       if (info->addr == 0 && probe_addrs)
+               client = i2c_new_probed_device(adapter, info, probe_addrs,
+                                              NULL);
+       else
+               client = i2c_new_device(adapter, info);
+
+       /* Note: by loading the module first we are certain that c->driver
+          will be set if the driver was found. If the module was not loaded
+          first, then the i2c core tries to delay-load the module for us,
+          and then c->driver is still NULL until the module is finally
+          loaded. This delay-load mechanism doesn't work if other drivers
+          want to use the i2c device, so explicitly loading the module
+          is the best alternative. */
+       if (client == NULL || client->driver == NULL)
+               goto error;
+
+       /* Lock the module so we can safely get the v4l2_subdev pointer */
+       if (!try_module_get(client->driver->driver.owner))
+               goto error;
+       sd = i2c_get_clientdata(client);
+
+       /* Register with the v4l2_device which increases the module's
+          use count as well. */
+       if (v4l2_device_register_subdev(v4l2_dev, sd))
+               sd = NULL;
+       /* Decrease the module use count to match the first try_module_get. */
+       module_put(client->driver->driver.owner);
+
+error:
+       /* If we have a client but no subdev, then something went wrong and
+          we must unregister the client. */
+       if (client && sd == NULL)
+               i2c_unregister_device(client);
+       return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);
+
+struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
+               struct i2c_adapter *adapter, const char *client_type,
+               u8 addr, const unsigned short *probe_addrs)
+{
+       struct i2c_board_info info;
+
+       /* Setup the i2c board info with the device type and
+          the device address. */
+       memset(&info, 0, sizeof(info));
+       strlcpy(info.type, client_type, sizeof(info.type));
+       info.addr = addr;
+
+       return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
+
+/* Return i2c client address of v4l2_subdev. */
+unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       return client ? client->addr : I2C_CLIENT_END;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);
+
+/* Return a list of I2C tuner addresses to probe. Use only if the tuner
+   addresses are unknown. */
+const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type)
+{
+       static const unsigned short radio_addrs[] = {
+#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE)
+               0x10,
+#endif
+               0x60,
+               I2C_CLIENT_END
+       };
+       static const unsigned short demod_addrs[] = {
+               0x42, 0x43, 0x4a, 0x4b,
+               I2C_CLIENT_END
+       };
+       static const unsigned short tv_addrs[] = {
+               0x42, 0x43, 0x4a, 0x4b,         /* tda8290 */
+               0x60, 0x61, 0x62, 0x63, 0x64,
+               I2C_CLIENT_END
+       };
+
+       switch (type) {
+       case ADDRS_RADIO:
+               return radio_addrs;
+       case ADDRS_DEMOD:
+               return demod_addrs;
+       case ADDRS_TV:
+               return tv_addrs;
+       case ADDRS_TV_WITH_DEMOD:
+               return tv_addrs + 4;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);
+
+#endif /* defined(CONFIG_I2C) */
+
+#if defined(CONFIG_SPI)
+
+/* Load an spi sub-device. */
+
+void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
+               const struct v4l2_subdev_ops *ops)
+{
+       v4l2_subdev_init(sd, ops);
+       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;
+       /* spi_device and v4l2_subdev point to one another */
+       v4l2_set_subdevdata(sd, spi);
+       spi_set_drvdata(spi, sd);
+       /* initialize name */
+       strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name));
+}
+EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);
+
+struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
+               struct spi_master *master, struct spi_board_info *info)
+{
+       struct v4l2_subdev *sd = NULL;
+       struct spi_device *spi = NULL;
+
+       BUG_ON(!v4l2_dev);
+
+       if (info->modalias)
+               request_module(info->modalias);
+
+       spi = spi_new_device(master, info);
+
+       if (spi == NULL || spi->dev.driver == NULL)
+               goto error;
+
+       if (!try_module_get(spi->dev.driver->owner))
+               goto error;
+
+       sd = spi_get_drvdata(spi);
+
+       /* Register with the v4l2_device which increases the module's
+          use count as well. */
+       if (v4l2_device_register_subdev(v4l2_dev, sd))
+               sd = NULL;
+
+       /* Decrease the module use count to match the first try_module_get. */
+       module_put(spi->dev.driver->owner);
+
+error:
+       /* If we have a client but no subdev, then something went wrong and
+          we must unregister the client. */
+       if (spi && sd == NULL)
+               spi_unregister_device(spi);
+
+       return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev);
+
+#endif /* defined(CONFIG_SPI) */
+
+/* Clamp x to be between min and max, aligned to a multiple of 2^align.  min
+ * and max don't have to be aligned, but there must be at least one valid
+ * value.  E.g., min=17,max=31,align=4 is not allowed as there are no multiples
+ * of 16 between 17 and 31.  */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+                               unsigned int max, unsigned int align)
+{
+       /* Bits that must be zero to be aligned */
+       unsigned int mask = ~((1 << align) - 1);
+
+       /* Round to nearest aligned value */
+       if (align)
+               x = (x + (1 << (align - 1))) & mask;
+
+       /* Clamp to aligned value of min and max */
+       if (x < min)
+               x = (min + ~mask) & mask;
+       else if (x > max)
+               x = max & mask;
+
+       return x;
+}
+
+/* Bound an image to have a width between wmin and wmax, and height between
+ * hmin and hmax, inclusive.  Additionally, the width will be a multiple of
+ * 2^walign, the height will be a multiple of 2^halign, and the overall size
+ * (width*height) will be a multiple of 2^salign.  The image may be shrunk
+ * or enlarged to fit the alignment constraints.
+ *
+ * The width or height maximum must not be smaller than the corresponding
+ * minimum.  The alignments must not be so high there are no possible image
+ * sizes within the allowed bounds.  wmin and hmin must be at least 1
+ * (don't use 0).  If you don't care about a certain alignment, specify 0,
+ * as 2^0 is 1 and one byte alignment is equivalent to no alignment.  If
+ * you only want to adjust downward, specify a maximum that's the same as
+ * the initial value.
+ */
+void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
+                          unsigned int walign,
+                          u32 *h, unsigned int hmin, unsigned int hmax,
+                          unsigned int halign, unsigned int salign)
+{
+       *w = clamp_align(*w, wmin, wmax, walign);
+       *h = clamp_align(*h, hmin, hmax, halign);
+
+       /* Usually we don't need to align the size and are done now. */
+       if (!salign)
+               return;
+
+       /* How much alignment do we have? */
+       walign = __ffs(*w);
+       halign = __ffs(*h);
+       /* Enough to satisfy the image alignment? */
+       if (walign + halign < salign) {
+               /* Max walign where there is still a valid width */
+               unsigned int wmaxa = __fls(wmax ^ (wmin - 1));
+               /* Max halign where there is still a valid height */
+               unsigned int hmaxa = __fls(hmax ^ (hmin - 1));
+
+               /* up the smaller alignment until we have enough */
+               do {
+                       if (halign >= hmaxa ||
+                           (walign <= halign && walign < wmaxa)) {
+                               *w = clamp_align(*w, wmin, wmax, walign + 1);
+                               walign = __ffs(*w);
+                       } else {
+                               *h = clamp_align(*h, hmin, hmax, halign + 1);
+                               halign = __ffs(*h);
+                       }
+               } while (halign + walign < salign);
+       }
+}
+EXPORT_SYMBOL_GPL(v4l_bound_align_image);
+
+/**
+ * v4l_fill_dv_preset_info - fill description of a digital video preset
+ * @preset - preset value
+ * @info - pointer to struct v4l2_dv_enum_preset
+ *
+ * drivers can use this helper function to fill description of dv preset
+ * in info.
+ */
+int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info)
+{
+       static const struct v4l2_dv_preset_info {
+               u16 width;
+               u16 height;
+               const char *name;
+       } dv_presets[] = {
+               { 0, 0, "Invalid" },            /* V4L2_DV_INVALID */
+               { 720,  480, "480p@59.94" },    /* V4L2_DV_480P59_94 */
+               { 720,  576, "576p@50" },       /* V4L2_DV_576P50 */
+               { 1280, 720, "720p@24" },       /* V4L2_DV_720P24 */
+               { 1280, 720, "720p@25" },       /* V4L2_DV_720P25 */
+               { 1280, 720, "720p@30" },       /* V4L2_DV_720P30 */
+               { 1280, 720, "720p@50" },       /* V4L2_DV_720P50 */
+               { 1280, 720, "720p@59.94" },    /* V4L2_DV_720P59_94 */
+               { 1280, 720, "720p@60" },       /* V4L2_DV_720P60 */
+               { 1920, 1080, "1080i@29.97" },  /* V4L2_DV_1080I29_97 */
+               { 1920, 1080, "1080i@30" },     /* V4L2_DV_1080I30 */
+               { 1920, 1080, "1080i@25" },     /* V4L2_DV_1080I25 */
+               { 1920, 1080, "1080i@50" },     /* V4L2_DV_1080I50 */
+               { 1920, 1080, "1080i@60" },     /* V4L2_DV_1080I60 */
+               { 1920, 1080, "1080p@24" },     /* V4L2_DV_1080P24 */
+               { 1920, 1080, "1080p@25" },     /* V4L2_DV_1080P25 */
+               { 1920, 1080, "1080p@30" },     /* V4L2_DV_1080P30 */
+               { 1920, 1080, "1080p@50" },     /* V4L2_DV_1080P50 */
+               { 1920, 1080, "1080p@60" },     /* V4L2_DV_1080P60 */
+       };
+
+       if (info == NULL || preset >= ARRAY_SIZE(dv_presets))
+               return -EINVAL;
+
+       info->preset = preset;
+       info->width = dv_presets[preset].width;
+       info->height = dv_presets[preset].height;
+       strlcpy(info->name, dv_presets[preset].name, sizeof(info->name));
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info);
+
+const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
+               const struct v4l2_discrete_probe *probe,
+               s32 width, s32 height)
+{
+       int i;
+       u32 error, min_error = UINT_MAX;
+       const struct v4l2_frmsize_discrete *size, *best = NULL;
+
+       if (!probe)
+               return best;
+
+       for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) {
+               error = abs(size->width - width) + abs(size->height - height);
+               if (error < min_error) {
+                       min_error = error;
+                       best = size;
+               }
+               if (!error)
+                       break;
+       }
+
+       return best;
+}
+EXPORT_SYMBOL_GPL(v4l2_find_nearest_format);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
new file mode 100644 (file)
index 0000000..9ebd5c5
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
+ *     Separated from fs stuff by Arnd Bergmann <arnd@arndb.de>
+ *
+ * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
+ * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 2001,2002  Andi Kleen, SuSE Labs
+ * Copyright (C) 2003       Pavel Machek (pavel@ucw.cz)
+ * Copyright (C) 2005       Philippe De Muyter (phdm@macqel.be)
+ * Copyright (C) 2008       Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * These routines maintain argument size conversion between 32bit and 64bit
+ * ioctls.
+ */
+
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       long ret = -ENOIOCTLCMD;
+
+       if (file->f_op->unlocked_ioctl)
+               ret = file->f_op->unlocked_ioctl(file, cmd, arg);
+
+       return ret;
+}
+
+
+struct v4l2_clip32 {
+       struct v4l2_rect        c;
+       compat_caddr_t          next;
+};
+
+struct v4l2_window32 {
+       struct v4l2_rect        w;
+       __u32                   field;  /* enum v4l2_field */
+       __u32                   chromakey;
+       compat_caddr_t          clips; /* actually struct v4l2_clip32 * */
+       __u32                   clipcount;
+       compat_caddr_t          bitmap;
+};
+
+static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
+               copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
+               get_user(kp->field, &up->field) ||
+               get_user(kp->chromakey, &up->chromakey) ||
+               get_user(kp->clipcount, &up->clipcount))
+                       return -EFAULT;
+       if (kp->clipcount > 2048)
+               return -EINVAL;
+       if (kp->clipcount) {
+               struct v4l2_clip32 __user *uclips;
+               struct v4l2_clip __user *kclips;
+               int n = kp->clipcount;
+               compat_caddr_t p;
+
+               if (get_user(p, &up->clips))
+                       return -EFAULT;
+               uclips = compat_ptr(p);
+               kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
+               kp->clips = kclips;
+               while (--n >= 0) {
+                       if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
+                               return -EFAULT;
+                       if (put_user(n ? kclips + 1 : NULL, &kclips->next))
+                               return -EFAULT;
+                       uclips += 1;
+                       kclips += 1;
+               }
+       } else
+               kp->clips = NULL;
+       return 0;
+}
+
+static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+       if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
+               put_user(kp->field, &up->field) ||
+               put_user(kp->chromakey, &up->chromakey) ||
+               put_user(kp->clipcount, &up->clipcount))
+                       return -EFAULT;
+       return 0;
+}
+
+static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+{
+       if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+                               struct v4l2_pix_format_mplane __user *up)
+{
+       if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+{
+       if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+                               struct v4l2_pix_format_mplane __user *up)
+{
+       if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+{
+       if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+{
+       if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+       if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+       if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
+               return -EFAULT;
+       return 0;
+}
+
+struct v4l2_format32 {
+       __u32   type;   /* enum v4l2_buf_type */
+       union {
+               struct v4l2_pix_format  pix;
+               struct v4l2_pix_format_mplane   pix_mp;
+               struct v4l2_window32    win;
+               struct v4l2_vbi_format  vbi;
+               struct v4l2_sliced_vbi_format   sliced;
+               __u8    raw_data[200];        /* user-defined */
+       } fmt;
+};
+
+/**
+ * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
+ * @index:     on return, index of the first created buffer
+ * @count:     entry: number of requested buffers,
+ *             return: number of created buffers
+ * @memory:    buffer memory type
+ * @format:    frame format, for which buffers are requested
+ * @reserved:  future extensions
+ */
+struct v4l2_create_buffers32 {
+       __u32                   index;
+       __u32                   count;
+       __u32                   memory; /* enum v4l2_memory */
+       struct v4l2_format32    format;
+       __u32                   reserved[8];
+};
+
+static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+       switch (kp->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
+                                                 &up->fmt.pix_mp);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data)))
+                       return -EFAULT;
+               return 0;
+       default:
+               printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+                                                               kp->type);
+               return -EINVAL;
+       }
+}
+
+static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
+                       get_user(kp->type, &up->type))
+                       return -EFAULT;
+       return __get_v4l2_format32(kp, up);
+}
+
+static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
+           copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
+                       return -EFAULT;
+       return __get_v4l2_format32(&kp->format, &up->format);
+}
+
+static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+       switch (kp->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
+                                                 &up->fmt.pix_mp);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (copy_to_user(up, kp, sizeof(up->fmt.raw_data)))
+                       return -EFAULT;
+               return 0;
+       default:
+               printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+                                                               kp->type);
+               return -EINVAL;
+       }
+}
+
+static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+               put_user(kp->type, &up->type))
+               return -EFAULT;
+       return __put_v4l2_format32(kp, up);
+}
+
+static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
+           copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
+                       return -EFAULT;
+       return __put_v4l2_format32(&kp->format, &up->format);
+}
+
+struct v4l2_standard32 {
+       __u32                index;
+       __u32                id[2]; /* __u64 would get the alignment wrong */
+       __u8                 name[24];
+       struct v4l2_fract    frameperiod; /* Frames, not fields */
+       __u32                framelines;
+       __u32                reserved[4];
+};
+
+static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+       /* other fields are not set by the user, nor used by the driver */
+       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
+               get_user(kp->index, &up->index))
+               return -EFAULT;
+       return 0;
+}
+
+static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
+               put_user(kp->index, &up->index) ||
+               copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
+               copy_to_user(up->name, kp->name, 24) ||
+               copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
+               put_user(kp->framelines, &up->framelines) ||
+               copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
+                       return -EFAULT;
+       return 0;
+}
+
+struct v4l2_plane32 {
+       __u32                   bytesused;
+       __u32                   length;
+       union {
+               __u32           mem_offset;
+               compat_long_t   userptr;
+       } m;
+       __u32                   data_offset;
+       __u32                   reserved[11];
+};
+
+struct v4l2_buffer32 {
+       __u32                   index;
+       __u32                   type;   /* enum v4l2_buf_type */
+       __u32                   bytesused;
+       __u32                   flags;
+       __u32                   field;  /* enum v4l2_field */
+       struct compat_timeval   timestamp;
+       struct v4l2_timecode    timecode;
+       __u32                   sequence;
+
+       /* memory location */
+       __u32                   memory; /* enum v4l2_memory */
+       union {
+               __u32           offset;
+               compat_long_t   userptr;
+               compat_caddr_t  planes;
+       } m;
+       __u32                   length;
+       __u32                   reserved2;
+       __u32                   reserved;
+};
+
+static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+                               enum v4l2_memory memory)
+{
+       void __user *up_pln;
+       compat_long_t p;
+
+       if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
+               copy_in_user(&up->data_offset, &up32->data_offset,
+                               sizeof(__u32)))
+               return -EFAULT;
+
+       if (memory == V4L2_MEMORY_USERPTR) {
+               if (get_user(p, &up32->m.userptr))
+                       return -EFAULT;
+               up_pln = compat_ptr(p);
+               if (put_user((unsigned long)up_pln, &up->m.userptr))
+                       return -EFAULT;
+       } else {
+               if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
+                                       sizeof(__u32)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+                               enum v4l2_memory memory)
+{
+       if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
+               copy_in_user(&up32->data_offset, &up->data_offset,
+                               sizeof(__u32)))
+               return -EFAULT;
+
+       /* For MMAP, driver might've set up the offset, so copy it back.
+        * USERPTR stays the same (was userspace-provided), so no copying. */
+       if (memory == V4L2_MEMORY_MMAP)
+               if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
+                                       sizeof(__u32)))
+                       return -EFAULT;
+
+       return 0;
+}
+
+static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+{
+       struct v4l2_plane32 __user *uplane32;
+       struct v4l2_plane __user *uplane;
+       compat_caddr_t p;
+       int num_planes;
+       int ret;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
+               get_user(kp->index, &up->index) ||
+               get_user(kp->type, &up->type) ||
+               get_user(kp->flags, &up->flags) ||
+               get_user(kp->memory, &up->memory))
+                       return -EFAULT;
+
+       if (V4L2_TYPE_IS_OUTPUT(kp->type))
+               if (get_user(kp->bytesused, &up->bytesused) ||
+                       get_user(kp->field, &up->field) ||
+                       get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+                       get_user(kp->timestamp.tv_usec,
+                                       &up->timestamp.tv_usec))
+                       return -EFAULT;
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
+               if (get_user(kp->length, &up->length))
+                       return -EFAULT;
+
+               num_planes = kp->length;
+               if (num_planes == 0) {
+                       kp->m.planes = NULL;
+                       /* num_planes == 0 is legal, e.g. when userspace doesn't
+                        * need planes array on DQBUF*/
+                       return 0;
+               }
+
+               if (get_user(p, &up->m.planes))
+                       return -EFAULT;
+
+               uplane32 = compat_ptr(p);
+               if (!access_ok(VERIFY_READ, uplane32,
+                               num_planes * sizeof(struct v4l2_plane32)))
+                       return -EFAULT;
+
+               /* We don't really care if userspace decides to kill itself
+                * by passing a very big num_planes value */
+               uplane = compat_alloc_user_space(num_planes *
+                                               sizeof(struct v4l2_plane));
+               kp->m.planes = uplane;
+
+               while (--num_planes >= 0) {
+                       ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
+                       if (ret)
+                               return ret;
+                       ++uplane;
+                       ++uplane32;
+               }
+       } else {
+               switch (kp->memory) {
+               case V4L2_MEMORY_MMAP:
+                       if (get_user(kp->length, &up->length) ||
+                               get_user(kp->m.offset, &up->m.offset))
+                               return -EFAULT;
+                       break;
+               case V4L2_MEMORY_USERPTR:
+                       {
+                       compat_long_t tmp;
+
+                       if (get_user(kp->length, &up->length) ||
+                           get_user(tmp, &up->m.userptr))
+                               return -EFAULT;
+
+                       kp->m.userptr = (unsigned long)compat_ptr(tmp);
+                       }
+                       break;
+               case V4L2_MEMORY_OVERLAY:
+                       if (get_user(kp->m.offset, &up->m.offset))
+                               return -EFAULT;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+{
+       struct v4l2_plane32 __user *uplane32;
+       struct v4l2_plane __user *uplane;
+       compat_caddr_t p;
+       int num_planes;
+       int ret;
+
+       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
+               put_user(kp->index, &up->index) ||
+               put_user(kp->type, &up->type) ||
+               put_user(kp->flags, &up->flags) ||
+               put_user(kp->memory, &up->memory))
+                       return -EFAULT;
+
+       if (put_user(kp->bytesused, &up->bytesused) ||
+               put_user(kp->field, &up->field) ||
+               put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+               put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
+               copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
+               put_user(kp->sequence, &up->sequence) ||
+               put_user(kp->reserved2, &up->reserved2) ||
+               put_user(kp->reserved, &up->reserved))
+                       return -EFAULT;
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
+               num_planes = kp->length;
+               if (num_planes == 0)
+                       return 0;
+
+               uplane = kp->m.planes;
+               if (get_user(p, &up->m.planes))
+                       return -EFAULT;
+               uplane32 = compat_ptr(p);
+
+               while (--num_planes >= 0) {
+                       ret = put_v4l2_plane32(uplane, uplane32, kp->memory);
+                       if (ret)
+                               return ret;
+                       ++uplane;
+                       ++uplane32;
+               }
+       } else {
+               switch (kp->memory) {
+               case V4L2_MEMORY_MMAP:
+                       if (put_user(kp->length, &up->length) ||
+                               put_user(kp->m.offset, &up->m.offset))
+                               return -EFAULT;
+                       break;
+               case V4L2_MEMORY_USERPTR:
+                       if (put_user(kp->length, &up->length) ||
+                               put_user(kp->m.userptr, &up->m.userptr))
+                               return -EFAULT;
+                       break;
+               case V4L2_MEMORY_OVERLAY:
+                       if (put_user(kp->m.offset, &up->m.offset))
+                               return -EFAULT;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+struct v4l2_framebuffer32 {
+       __u32                   capability;
+       __u32                   flags;
+       compat_caddr_t          base;
+       struct v4l2_pix_format  fmt;
+};
+
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+       u32 tmp;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
+               get_user(tmp, &up->base) ||
+               get_user(kp->capability, &up->capability) ||
+               get_user(kp->flags, &up->flags))
+                       return -EFAULT;
+       kp->base = compat_ptr(tmp);
+       get_v4l2_pix_format(&kp->fmt, &up->fmt);
+       return 0;
+}
+
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+       u32 tmp = (u32)((unsigned long)kp->base);
+
+       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
+               put_user(tmp, &up->base) ||
+               put_user(kp->capability, &up->capability) ||
+               put_user(kp->flags, &up->flags))
+                       return -EFAULT;
+       put_v4l2_pix_format(&kp->fmt, &up->fmt);
+       return 0;
+}
+
+struct v4l2_input32 {
+       __u32        index;             /*  Which input */
+       __u8         name[32];          /*  Label */
+       __u32        type;              /*  Type of input */
+       __u32        audioset;          /*  Associated audios (bitfield) */
+       __u32        tuner;             /*  Associated tuner */
+       v4l2_std_id  std;
+       __u32        status;
+       __u32        reserved[4];
+} __attribute__ ((packed));
+
+/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
+   Otherwise it is identical to the 32-bit version. */
+static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+       if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
+               return -EFAULT;
+       return 0;
+}
+
+static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+       if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
+               return -EFAULT;
+       return 0;
+}
+
+struct v4l2_ext_controls32 {
+       __u32 ctrl_class;
+       __u32 count;
+       __u32 error_idx;
+       __u32 reserved[2];
+       compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
+};
+
+struct v4l2_ext_control32 {
+       __u32 id;
+       __u32 size;
+       __u32 reserved2[1];
+       union {
+               __s32 value;
+               __s64 value64;
+               compat_caddr_t string; /* actually char * */
+       };
+} __attribute__ ((packed));
+
+/* The following function really belong in v4l2-common, but that causes
+   a circular dependency between modules. We need to think about this, but
+   for now this will do. */
+
+/* Return non-zero if this control is a pointer type. Currently only
+   type STRING is a pointer type. */
+static inline int ctrl_is_pointer(u32 id)
+{
+       switch (id) {
+       case V4L2_CID_RDS_TX_PS_NAME:
+       case V4L2_CID_RDS_TX_RADIO_TEXT:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+       struct v4l2_ext_control32 __user *ucontrols;
+       struct v4l2_ext_control __user *kcontrols;
+       int n;
+       compat_caddr_t p;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
+               get_user(kp->ctrl_class, &up->ctrl_class) ||
+               get_user(kp->count, &up->count) ||
+               get_user(kp->error_idx, &up->error_idx) ||
+               copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+                       return -EFAULT;
+       n = kp->count;
+       if (n == 0) {
+               kp->controls = NULL;
+               return 0;
+       }
+       if (get_user(p, &up->controls))
+               return -EFAULT;
+       ucontrols = compat_ptr(p);
+       if (!access_ok(VERIFY_READ, ucontrols,
+                       n * sizeof(struct v4l2_ext_control32)))
+               return -EFAULT;
+       kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
+       kp->controls = kcontrols;
+       while (--n >= 0) {
+               if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
+                       return -EFAULT;
+               if (ctrl_is_pointer(kcontrols->id)) {
+                       void __user *s;
+
+                       if (get_user(p, &ucontrols->string))
+                               return -EFAULT;
+                       s = compat_ptr(p);
+                       if (put_user(s, &kcontrols->string))
+                               return -EFAULT;
+               }
+               ucontrols++;
+               kcontrols++;
+       }
+       return 0;
+}
+
+static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+       struct v4l2_ext_control32 __user *ucontrols;
+       struct v4l2_ext_control __user *kcontrols = kp->controls;
+       int n = kp->count;
+       compat_caddr_t p;
+
+       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
+               put_user(kp->ctrl_class, &up->ctrl_class) ||
+               put_user(kp->count, &up->count) ||
+               put_user(kp->error_idx, &up->error_idx) ||
+               copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+                       return -EFAULT;
+       if (!kp->count)
+               return 0;
+
+       if (get_user(p, &up->controls))
+               return -EFAULT;
+       ucontrols = compat_ptr(p);
+       if (!access_ok(VERIFY_WRITE, ucontrols,
+                       n * sizeof(struct v4l2_ext_control32)))
+               return -EFAULT;
+
+       while (--n >= 0) {
+               unsigned size = sizeof(*ucontrols);
+
+               /* Do not modify the pointer when copying a pointer control.
+                  The contents of the pointer was changed, not the pointer
+                  itself. */
+               if (ctrl_is_pointer(kcontrols->id))
+                       size -= sizeof(ucontrols->value64);
+               if (copy_in_user(ucontrols, kcontrols, size))
+                       return -EFAULT;
+               ucontrols++;
+               kcontrols++;
+       }
+       return 0;
+}
+
+struct v4l2_event32 {
+       __u32                           type;
+       union {
+               __u8                    data[64];
+       } u;
+       __u32                           pending;
+       __u32                           sequence;
+       struct compat_timespec          timestamp;
+       __u32                           id;
+       __u32                           reserved[8];
+};
+
+static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
+{
+       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
+               put_user(kp->type, &up->type) ||
+               copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
+               put_user(kp->pending, &up->pending) ||
+               put_user(kp->sequence, &up->sequence) ||
+               put_compat_timespec(&kp->timestamp, &up->timestamp) ||
+               put_user(kp->id, &up->id) ||
+               copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+                       return -EFAULT;
+       return 0;
+}
+
+#define VIDIOC_G_FMT32         _IOWR('V',  4, struct v4l2_format32)
+#define VIDIOC_S_FMT32         _IOWR('V',  5, struct v4l2_format32)
+#define VIDIOC_QUERYBUF32      _IOWR('V',  9, struct v4l2_buffer32)
+#define VIDIOC_G_FBUF32                _IOR ('V', 10, struct v4l2_framebuffer32)
+#define VIDIOC_S_FBUF32                _IOW ('V', 11, struct v4l2_framebuffer32)
+#define VIDIOC_QBUF32          _IOWR('V', 15, struct v4l2_buffer32)
+#define VIDIOC_DQBUF32         _IOWR('V', 17, struct v4l2_buffer32)
+#define VIDIOC_ENUMSTD32       _IOWR('V', 25, struct v4l2_standard32)
+#define VIDIOC_ENUMINPUT32     _IOWR('V', 26, struct v4l2_input32)
+#define VIDIOC_TRY_FMT32       _IOWR('V', 64, struct v4l2_format32)
+#define VIDIOC_G_EXT_CTRLS32    _IOWR('V', 71, struct v4l2_ext_controls32)
+#define VIDIOC_S_EXT_CTRLS32    _IOWR('V', 72, struct v4l2_ext_controls32)
+#define VIDIOC_TRY_EXT_CTRLS32  _IOWR('V', 73, struct v4l2_ext_controls32)
+#define        VIDIOC_DQEVENT32        _IOR ('V', 89, struct v4l2_event32)
+#define VIDIOC_CREATE_BUFS32   _IOWR('V', 92, struct v4l2_create_buffers32)
+#define VIDIOC_PREPARE_BUF32   _IOWR('V', 93, struct v4l2_buffer32)
+
+#define VIDIOC_OVERLAY32       _IOW ('V', 14, s32)
+#define VIDIOC_STREAMON32      _IOW ('V', 18, s32)
+#define VIDIOC_STREAMOFF32     _IOW ('V', 19, s32)
+#define VIDIOC_G_INPUT32       _IOR ('V', 38, s32)
+#define VIDIOC_S_INPUT32       _IOWR('V', 39, s32)
+#define VIDIOC_G_OUTPUT32      _IOR ('V', 46, s32)
+#define VIDIOC_S_OUTPUT32      _IOWR('V', 47, s32)
+
+static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       union {
+               struct v4l2_format v2f;
+               struct v4l2_buffer v2b;
+               struct v4l2_framebuffer v2fb;
+               struct v4l2_input v2i;
+               struct v4l2_standard v2s;
+               struct v4l2_ext_controls v2ecs;
+               struct v4l2_event v2ev;
+               struct v4l2_create_buffers v2crt;
+               unsigned long vx;
+               int vi;
+       } karg;
+       void __user *up = compat_ptr(arg);
+       int compatible_arg = 1;
+       long err = 0;
+
+       /* First, convert the command. */
+       switch (cmd) {
+       case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
+       case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
+       case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
+       case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
+       case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
+       case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
+       case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
+       case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break;
+       case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break;
+       case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
+       case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
+       case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
+       case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
+       case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break;
+       case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
+       case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
+       case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
+       case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
+       case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
+       case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
+       case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+       case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
+       case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
+       }
+
+       switch (cmd) {
+       case VIDIOC_OVERLAY:
+       case VIDIOC_STREAMON:
+       case VIDIOC_STREAMOFF:
+       case VIDIOC_S_INPUT:
+       case VIDIOC_S_OUTPUT:
+               err = get_user(karg.vi, (s32 __user *)up);
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_G_INPUT:
+       case VIDIOC_G_OUTPUT:
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_G_FMT:
+       case VIDIOC_S_FMT:
+       case VIDIOC_TRY_FMT:
+               err = get_v4l2_format32(&karg.v2f, up);
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_CREATE_BUFS:
+               err = get_v4l2_create32(&karg.v2crt, up);
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_PREPARE_BUF:
+       case VIDIOC_QUERYBUF:
+       case VIDIOC_QBUF:
+       case VIDIOC_DQBUF:
+               err = get_v4l2_buffer32(&karg.v2b, up);
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_S_FBUF:
+               err = get_v4l2_framebuffer32(&karg.v2fb, up);
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_G_FBUF:
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_ENUMSTD:
+               err = get_v4l2_standard32(&karg.v2s, up);
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_ENUMINPUT:
+               err = get_v4l2_input32(&karg.v2i, up);
+               compatible_arg = 0;
+               break;
+
+       case VIDIOC_G_EXT_CTRLS:
+       case VIDIOC_S_EXT_CTRLS:
+       case VIDIOC_TRY_EXT_CTRLS:
+               err = get_v4l2_ext_controls32(&karg.v2ecs, up);
+               compatible_arg = 0;
+               break;
+       case VIDIOC_DQEVENT:
+               compatible_arg = 0;
+               break;
+       }
+       if (err)
+               return err;
+
+       if (compatible_arg)
+               err = native_ioctl(file, cmd, (unsigned long)up);
+       else {
+               mm_segment_t old_fs = get_fs();
+
+               set_fs(KERNEL_DS);
+               err = native_ioctl(file, cmd, (unsigned long)&karg);
+               set_fs(old_fs);
+       }
+
+       /* Special case: even after an error we need to put the
+          results back for these ioctls since the error_idx will
+          contain information on which control failed. */
+       switch (cmd) {
+       case VIDIOC_G_EXT_CTRLS:
+       case VIDIOC_S_EXT_CTRLS:
+       case VIDIOC_TRY_EXT_CTRLS:
+               if (put_v4l2_ext_controls32(&karg.v2ecs, up))
+                       err = -EFAULT;
+               break;
+       }
+       if (err)
+               return err;
+
+       switch (cmd) {
+       case VIDIOC_S_INPUT:
+       case VIDIOC_S_OUTPUT:
+       case VIDIOC_G_INPUT:
+       case VIDIOC_G_OUTPUT:
+               err = put_user(((s32)karg.vi), (s32 __user *)up);
+               break;
+
+       case VIDIOC_G_FBUF:
+               err = put_v4l2_framebuffer32(&karg.v2fb, up);
+               break;
+
+       case VIDIOC_DQEVENT:
+               err = put_v4l2_event32(&karg.v2ev, up);
+               break;
+
+       case VIDIOC_G_FMT:
+       case VIDIOC_S_FMT:
+       case VIDIOC_TRY_FMT:
+               err = put_v4l2_format32(&karg.v2f, up);
+               break;
+
+       case VIDIOC_CREATE_BUFS:
+               err = put_v4l2_create32(&karg.v2crt, up);
+               break;
+
+       case VIDIOC_QUERYBUF:
+       case VIDIOC_QBUF:
+       case VIDIOC_DQBUF:
+               err = put_v4l2_buffer32(&karg.v2b, up);
+               break;
+
+       case VIDIOC_ENUMSTD:
+               err = put_v4l2_standard32(&karg.v2s, up);
+               break;
+
+       case VIDIOC_ENUMINPUT:
+               err = put_v4l2_input32(&karg.v2i, up);
+               break;
+       }
+       return err;
+}
+
+long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct video_device *vdev = video_devdata(file);
+       long ret = -ENOIOCTLCMD;
+
+       if (!file->f_op->unlocked_ioctl)
+               return ret;
+
+       switch (cmd) {
+       case VIDIOC_QUERYCAP:
+       case VIDIOC_RESERVED:
+       case VIDIOC_ENUM_FMT:
+       case VIDIOC_G_FMT32:
+       case VIDIOC_S_FMT32:
+       case VIDIOC_REQBUFS:
+       case VIDIOC_QUERYBUF32:
+       case VIDIOC_G_FBUF32:
+       case VIDIOC_S_FBUF32:
+       case VIDIOC_OVERLAY32:
+       case VIDIOC_QBUF32:
+       case VIDIOC_DQBUF32:
+       case VIDIOC_STREAMON32:
+       case VIDIOC_STREAMOFF32:
+       case VIDIOC_G_PARM:
+       case VIDIOC_S_PARM:
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+       case VIDIOC_ENUMSTD32:
+       case VIDIOC_ENUMINPUT32:
+       case VIDIOC_G_CTRL:
+       case VIDIOC_S_CTRL:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_AUDIO:
+       case VIDIOC_S_AUDIO:
+       case VIDIOC_QUERYCTRL:
+       case VIDIOC_QUERYMENU:
+       case VIDIOC_G_INPUT32:
+       case VIDIOC_S_INPUT32:
+       case VIDIOC_G_OUTPUT32:
+       case VIDIOC_S_OUTPUT32:
+       case VIDIOC_ENUMOUTPUT:
+       case VIDIOC_G_AUDOUT:
+       case VIDIOC_S_AUDOUT:
+       case VIDIOC_G_MODULATOR:
+       case VIDIOC_S_MODULATOR:
+       case VIDIOC_S_FREQUENCY:
+       case VIDIOC_G_FREQUENCY:
+       case VIDIOC_CROPCAP:
+       case VIDIOC_G_CROP:
+       case VIDIOC_S_CROP:
+       case VIDIOC_G_SELECTION:
+       case VIDIOC_S_SELECTION:
+       case VIDIOC_G_JPEGCOMP:
+       case VIDIOC_S_JPEGCOMP:
+       case VIDIOC_QUERYSTD:
+       case VIDIOC_TRY_FMT32:
+       case VIDIOC_ENUMAUDIO:
+       case VIDIOC_ENUMAUDOUT:
+       case VIDIOC_G_PRIORITY:
+       case VIDIOC_S_PRIORITY:
+       case VIDIOC_G_SLICED_VBI_CAP:
+       case VIDIOC_LOG_STATUS:
+       case VIDIOC_G_EXT_CTRLS32:
+       case VIDIOC_S_EXT_CTRLS32:
+       case VIDIOC_TRY_EXT_CTRLS32:
+       case VIDIOC_ENUM_FRAMESIZES:
+       case VIDIOC_ENUM_FRAMEINTERVALS:
+       case VIDIOC_G_ENC_INDEX:
+       case VIDIOC_ENCODER_CMD:
+       case VIDIOC_TRY_ENCODER_CMD:
+       case VIDIOC_DECODER_CMD:
+       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_ENUM_DV_PRESETS:
+       case VIDIOC_S_DV_PRESET:
+       case VIDIOC_G_DV_PRESET:
+       case VIDIOC_QUERY_DV_PRESET:
+       case VIDIOC_S_DV_TIMINGS:
+       case VIDIOC_G_DV_TIMINGS:
+       case VIDIOC_DQEVENT:
+       case VIDIOC_DQEVENT32:
+       case VIDIOC_SUBSCRIBE_EVENT:
+       case VIDIOC_UNSUBSCRIBE_EVENT:
+       case VIDIOC_CREATE_BUFS32:
+       case VIDIOC_PREPARE_BUF32:
+       case VIDIOC_ENUM_DV_TIMINGS:
+       case VIDIOC_QUERY_DV_TIMINGS:
+       case VIDIOC_DV_TIMINGS_CAP:
+       case VIDIOC_ENUM_FREQ_BANDS:
+               ret = do_video_ioctl(file, cmd, arg);
+               break;
+
+       default:
+               if (vdev->fops->compat_ioctl32)
+                       ret = vdev->fops->compat_ioctl32(file, cmd, arg);
+
+               if (ret == -ENOIOCTLCMD)
+                       printk(KERN_WARNING "compat_ioctl32: "
+                               "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
+                               _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
+                               cmd);
+               break;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
new file mode 100644 (file)
index 0000000..b6a2ee7
--- /dev/null
@@ -0,0 +1,2651 @@
+/*
+    V4L2 controls framework implementation.
+
+    Copyright (C) 2010  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dev.h>
+
+#define has_op(master, op) \
+       (master->ops && master->ops->op)
+#define call_op(master, op) \
+       (has_op(master, op) ? master->ops->op(master) : 0)
+
+/* Internal temporary helper struct, one for each v4l2_ext_control */
+struct v4l2_ctrl_helper {
+       /* Pointer to the control reference of the master control */
+       struct v4l2_ctrl_ref *mref;
+       /* The control corresponding to the v4l2_ext_control ID field. */
+       struct v4l2_ctrl *ctrl;
+       /* v4l2_ext_control index of the next control belonging to the
+          same cluster, or 0 if there isn't any. */
+       u32 next;
+};
+
+/* Small helper function to determine if the autocluster is set to manual
+   mode. */
+static bool is_cur_manual(const struct v4l2_ctrl *master)
+{
+       return master->is_auto && master->cur.val == master->manual_mode_value;
+}
+
+/* Same as above, but this checks the against the new value instead of the
+   current value. */
+static bool is_new_manual(const struct v4l2_ctrl *master)
+{
+       return master->is_auto && master->val == master->manual_mode_value;
+}
+
+/* Returns NULL or a character pointer array containing the menu for
+   the given control ID. The pointer array ends with a NULL pointer.
+   An empty string signifies a menu entry that is invalid. This allows
+   drivers to disable certain options if it is not supported. */
+const char * const *v4l2_ctrl_get_menu(u32 id)
+{
+       static const char * const mpeg_audio_sampling_freq[] = {
+               "44.1 kHz",
+               "48 kHz",
+               "32 kHz",
+               NULL
+       };
+       static const char * const mpeg_audio_encoding[] = {
+               "MPEG-1/2 Layer I",
+               "MPEG-1/2 Layer II",
+               "MPEG-1/2 Layer III",
+               "MPEG-2/4 AAC",
+               "AC-3",
+               NULL
+       };
+       static const char * const mpeg_audio_l1_bitrate[] = {
+               "32 kbps",
+               "64 kbps",
+               "96 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "288 kbps",
+               "320 kbps",
+               "352 kbps",
+               "384 kbps",
+               "416 kbps",
+               "448 kbps",
+               NULL
+       };
+       static const char * const mpeg_audio_l2_bitrate[] = {
+               "32 kbps",
+               "48 kbps",
+               "56 kbps",
+               "64 kbps",
+               "80 kbps",
+               "96 kbps",
+               "112 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "320 kbps",
+               "384 kbps",
+               NULL
+       };
+       static const char * const mpeg_audio_l3_bitrate[] = {
+               "32 kbps",
+               "40 kbps",
+               "48 kbps",
+               "56 kbps",
+               "64 kbps",
+               "80 kbps",
+               "96 kbps",
+               "112 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "320 kbps",
+               NULL
+       };
+       static const char * const mpeg_audio_ac3_bitrate[] = {
+               "32 kbps",
+               "40 kbps",
+               "48 kbps",
+               "56 kbps",
+               "64 kbps",
+               "80 kbps",
+               "96 kbps",
+               "112 kbps",
+               "128 kbps",
+               "160 kbps",
+               "192 kbps",
+               "224 kbps",
+               "256 kbps",
+               "320 kbps",
+               "384 kbps",
+               "448 kbps",
+               "512 kbps",
+               "576 kbps",
+               "640 kbps",
+               NULL
+       };
+       static const char * const mpeg_audio_mode[] = {
+               "Stereo",
+               "Joint Stereo",
+               "Dual",
+               "Mono",
+               NULL
+       };
+       static const char * const mpeg_audio_mode_extension[] = {
+               "Bound 4",
+               "Bound 8",
+               "Bound 12",
+               "Bound 16",
+               NULL
+       };
+       static const char * const mpeg_audio_emphasis[] = {
+               "No Emphasis",
+               "50/15 us",
+               "CCITT J17",
+               NULL
+       };
+       static const char * const mpeg_audio_crc[] = {
+               "No CRC",
+               "16-bit CRC",
+               NULL
+       };
+       static const char * const mpeg_audio_dec_playback[] = {
+               "Auto",
+               "Stereo",
+               "Left",
+               "Right",
+               "Mono",
+               "Swapped Stereo",
+               NULL
+       };
+       static const char * const mpeg_video_encoding[] = {
+               "MPEG-1",
+               "MPEG-2",
+               "MPEG-4 AVC",
+               NULL
+       };
+       static const char * const mpeg_video_aspect[] = {
+               "1x1",
+               "4x3",
+               "16x9",
+               "2.21x1",
+               NULL
+       };
+       static const char * const mpeg_video_bitrate_mode[] = {
+               "Variable Bitrate",
+               "Constant Bitrate",
+               NULL
+       };
+       static const char * const mpeg_stream_type[] = {
+               "MPEG-2 Program Stream",
+               "MPEG-2 Transport Stream",
+               "MPEG-1 System Stream",
+               "MPEG-2 DVD-compatible Stream",
+               "MPEG-1 VCD-compatible Stream",
+               "MPEG-2 SVCD-compatible Stream",
+               NULL
+       };
+       static const char * const mpeg_stream_vbi_fmt[] = {
+               "No VBI",
+               "Private Packet, IVTV Format",
+               NULL
+       };
+       static const char * const camera_power_line_frequency[] = {
+               "Disabled",
+               "50 Hz",
+               "60 Hz",
+               "Auto",
+               NULL
+       };
+       static const char * const camera_exposure_auto[] = {
+               "Auto Mode",
+               "Manual Mode",
+               "Shutter Priority Mode",
+               "Aperture Priority Mode",
+               NULL
+       };
+       static const char * const camera_exposure_metering[] = {
+               "Average",
+               "Center Weighted",
+               "Spot",
+               NULL
+       };
+       static const char * const camera_auto_focus_range[] = {
+               "Auto",
+               "Normal",
+               "Macro",
+               "Infinity",
+               NULL
+       };
+       static const char * const colorfx[] = {
+               "None",
+               "Black & White",
+               "Sepia",
+               "Negative",
+               "Emboss",
+               "Sketch",
+               "Sky Blue",
+               "Grass Green",
+               "Skin Whiten",
+               "Vivid",
+               "Aqua",
+               "Art Freeze",
+               "Silhouette",
+               "Solarization",
+               "Antique",
+               "Set Cb/Cr",
+               NULL
+       };
+       static const char * const auto_n_preset_white_balance[] = {
+               "Manual",
+               "Auto",
+               "Incandescent",
+               "Fluorescent",
+               "Fluorescent H",
+               "Horizon",
+               "Daylight",
+               "Flash",
+               "Cloudy",
+               "Shade",
+               NULL,
+       };
+       static const char * const camera_iso_sensitivity_auto[] = {
+               "Manual",
+               "Auto",
+               NULL
+       };
+       static const char * const scene_mode[] = {
+               "None",
+               "Backlight",
+               "Beach/Snow",
+               "Candle Light",
+               "Dusk/Dawn",
+               "Fall Colors",
+               "Fireworks",
+               "Landscape",
+               "Night",
+               "Party/Indoor",
+               "Portrait",
+               "Sports",
+               "Sunset",
+               "Text",
+               NULL
+       };
+       static const char * const tune_preemphasis[] = {
+               "No Preemphasis",
+               "50 Microseconds",
+               "75 Microseconds",
+               NULL,
+       };
+       static const char * const header_mode[] = {
+               "Separate Buffer",
+               "Joined With 1st Frame",
+               NULL,
+       };
+       static const char * const multi_slice[] = {
+               "Single",
+               "Max Macroblocks",
+               "Max Bytes",
+               NULL,
+       };
+       static const char * const entropy_mode[] = {
+               "CAVLC",
+               "CABAC",
+               NULL,
+       };
+       static const char * const mpeg_h264_level[] = {
+               "1",
+               "1b",
+               "1.1",
+               "1.2",
+               "1.3",
+               "2",
+               "2.1",
+               "2.2",
+               "3",
+               "3.1",
+               "3.2",
+               "4",
+               "4.1",
+               "4.2",
+               "5",
+               "5.1",
+               NULL,
+       };
+       static const char * const h264_loop_filter[] = {
+               "Enabled",
+               "Disabled",
+               "Disabled at Slice Boundary",
+               NULL,
+       };
+       static const char * const h264_profile[] = {
+               "Baseline",
+               "Constrained Baseline",
+               "Main",
+               "Extended",
+               "High",
+               "High 10",
+               "High 422",
+               "High 444 Predictive",
+               "High 10 Intra",
+               "High 422 Intra",
+               "High 444 Intra",
+               "CAVLC 444 Intra",
+               "Scalable Baseline",
+               "Scalable High",
+               "Scalable High Intra",
+               "Multiview High",
+               NULL,
+       };
+       static const char * const vui_sar_idc[] = {
+               "Unspecified",
+               "1:1",
+               "12:11",
+               "10:11",
+               "16:11",
+               "40:33",
+               "24:11",
+               "20:11",
+               "32:11",
+               "80:33",
+               "18:11",
+               "15:11",
+               "64:33",
+               "160:99",
+               "4:3",
+               "3:2",
+               "2:1",
+               "Extended SAR",
+               NULL,
+       };
+       static const char * const mpeg_mpeg4_level[] = {
+               "0",
+               "0b",
+               "1",
+               "2",
+               "3",
+               "3b",
+               "4",
+               "5",
+               NULL,
+       };
+       static const char * const mpeg4_profile[] = {
+               "Simple",
+               "Advanced Simple",
+               "Core",
+               "Simple Scalable",
+               "Advanced Coding Efficency",
+               NULL,
+       };
+
+       static const char * const flash_led_mode[] = {
+               "Off",
+               "Flash",
+               "Torch",
+               NULL,
+       };
+       static const char * const flash_strobe_source[] = {
+               "Software",
+               "External",
+               NULL,
+       };
+
+       static const char * const jpeg_chroma_subsampling[] = {
+               "4:4:4",
+               "4:2:2",
+               "4:2:0",
+               "4:1:1",
+               "4:1:0",
+               "Gray",
+               NULL,
+       };
+
+       switch (id) {
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+               return mpeg_audio_sampling_freq;
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+               return mpeg_audio_encoding;
+       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+               return mpeg_audio_l1_bitrate;
+       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+               return mpeg_audio_l2_bitrate;
+       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+               return mpeg_audio_l3_bitrate;
+       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+               return mpeg_audio_ac3_bitrate;
+       case V4L2_CID_MPEG_AUDIO_MODE:
+               return mpeg_audio_mode;
+       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+               return mpeg_audio_mode_extension;
+       case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+               return mpeg_audio_emphasis;
+       case V4L2_CID_MPEG_AUDIO_CRC:
+               return mpeg_audio_crc;
+       case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+       case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
+               return mpeg_audio_dec_playback;
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               return mpeg_video_encoding;
+       case V4L2_CID_MPEG_VIDEO_ASPECT:
+               return mpeg_video_aspect;
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+               return mpeg_video_bitrate_mode;
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               return mpeg_stream_type;
+       case V4L2_CID_MPEG_STREAM_VBI_FMT:
+               return mpeg_stream_vbi_fmt;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               return camera_power_line_frequency;
+       case V4L2_CID_EXPOSURE_AUTO:
+               return camera_exposure_auto;
+       case V4L2_CID_EXPOSURE_METERING:
+               return camera_exposure_metering;
+       case V4L2_CID_AUTO_FOCUS_RANGE:
+               return camera_auto_focus_range;
+       case V4L2_CID_COLORFX:
+               return colorfx;
+       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+               return auto_n_preset_white_balance;
+       case V4L2_CID_ISO_SENSITIVITY_AUTO:
+               return camera_iso_sensitivity_auto;
+       case V4L2_CID_SCENE_MODE:
+               return scene_mode;
+       case V4L2_CID_TUNE_PREEMPHASIS:
+               return tune_preemphasis;
+       case V4L2_CID_FLASH_LED_MODE:
+               return flash_led_mode;
+       case V4L2_CID_FLASH_STROBE_SOURCE:
+               return flash_strobe_source;
+       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+               return header_mode;
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+               return multi_slice;
+       case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+               return entropy_mode;
+       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+               return mpeg_h264_level;
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+               return h264_loop_filter;
+       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+               return h264_profile;
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+               return vui_sar_idc;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+               return mpeg_mpeg4_level;
+       case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+               return mpeg4_profile;
+       case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+               return jpeg_chroma_subsampling;
+
+       default:
+               return NULL;
+       }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+       switch (id) {
+       /* USER controls */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_USER_CLASS:               return "User Controls";
+       case V4L2_CID_BRIGHTNESS:               return "Brightness";
+       case V4L2_CID_CONTRAST:                 return "Contrast";
+       case V4L2_CID_SATURATION:               return "Saturation";
+       case V4L2_CID_HUE:                      return "Hue";
+       case V4L2_CID_AUDIO_VOLUME:             return "Volume";
+       case V4L2_CID_AUDIO_BALANCE:            return "Balance";
+       case V4L2_CID_AUDIO_BASS:               return "Bass";
+       case V4L2_CID_AUDIO_TREBLE:             return "Treble";
+       case V4L2_CID_AUDIO_MUTE:               return "Mute";
+       case V4L2_CID_AUDIO_LOUDNESS:           return "Loudness";
+       case V4L2_CID_BLACK_LEVEL:              return "Black Level";
+       case V4L2_CID_AUTO_WHITE_BALANCE:       return "White Balance, Automatic";
+       case V4L2_CID_DO_WHITE_BALANCE:         return "Do White Balance";
+       case V4L2_CID_RED_BALANCE:              return "Red Balance";
+       case V4L2_CID_BLUE_BALANCE:             return "Blue Balance";
+       case V4L2_CID_GAMMA:                    return "Gamma";
+       case V4L2_CID_EXPOSURE:                 return "Exposure";
+       case V4L2_CID_AUTOGAIN:                 return "Gain, Automatic";
+       case V4L2_CID_GAIN:                     return "Gain";
+       case V4L2_CID_HFLIP:                    return "Horizontal Flip";
+       case V4L2_CID_VFLIP:                    return "Vertical Flip";
+       case V4L2_CID_HCENTER:                  return "Horizontal Center";
+       case V4L2_CID_VCENTER:                  return "Vertical Center";
+       case V4L2_CID_POWER_LINE_FREQUENCY:     return "Power Line Frequency";
+       case V4L2_CID_HUE_AUTO:                 return "Hue, Automatic";
+       case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+       case V4L2_CID_SHARPNESS:                return "Sharpness";
+       case V4L2_CID_BACKLIGHT_COMPENSATION:   return "Backlight Compensation";
+       case V4L2_CID_CHROMA_AGC:               return "Chroma AGC";
+       case V4L2_CID_COLOR_KILLER:             return "Color Killer";
+       case V4L2_CID_COLORFX:                  return "Color Effects";
+       case V4L2_CID_AUTOBRIGHTNESS:           return "Brightness, Automatic";
+       case V4L2_CID_BAND_STOP_FILTER:         return "Band-Stop Filter";
+       case V4L2_CID_ROTATE:                   return "Rotate";
+       case V4L2_CID_BG_COLOR:                 return "Background Color";
+       case V4L2_CID_CHROMA_GAIN:              return "Chroma Gain";
+       case V4L2_CID_ILLUMINATORS_1:           return "Illuminator 1";
+       case V4L2_CID_ILLUMINATORS_2:           return "Illuminator 2";
+       case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:  return "Min Number of Capture Buffers";
+       case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:   return "Min Number of Output Buffers";
+       case V4L2_CID_ALPHA_COMPONENT:          return "Alpha Component";
+       case V4L2_CID_COLORFX_CBCR:             return "Color Effects, CbCr";
+
+       /* MPEG controls */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_MPEG_CLASS:               return "MPEG Encoder Controls";
+       case V4L2_CID_MPEG_STREAM_TYPE:         return "Stream Type";
+       case V4L2_CID_MPEG_STREAM_PID_PMT:      return "Stream PMT Program ID";
+       case V4L2_CID_MPEG_STREAM_PID_AUDIO:    return "Stream Audio Program ID";
+       case V4L2_CID_MPEG_STREAM_PID_VIDEO:    return "Stream Video Program ID";
+       case V4L2_CID_MPEG_STREAM_PID_PCR:      return "Stream PCR Program ID";
+       case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+       case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+       case V4L2_CID_MPEG_STREAM_VBI_FMT:      return "Stream VBI Format";
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
+       case V4L2_CID_MPEG_AUDIO_ENCODING:      return "Audio Encoding";
+       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:    return "Audio Layer I Bitrate";
+       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:    return "Audio Layer II Bitrate";
+       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:    return "Audio Layer III Bitrate";
+       case V4L2_CID_MPEG_AUDIO_MODE:          return "Audio Stereo Mode";
+       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
+       case V4L2_CID_MPEG_AUDIO_EMPHASIS:      return "Audio Emphasis";
+       case V4L2_CID_MPEG_AUDIO_CRC:           return "Audio CRC";
+       case V4L2_CID_MPEG_AUDIO_MUTE:          return "Audio Mute";
+       case V4L2_CID_MPEG_AUDIO_AAC_BITRATE:   return "Audio AAC Bitrate";
+       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:   return "Audio AC-3 Bitrate";
+       case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:  return "Audio Playback";
+       case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback";
+       case V4L2_CID_MPEG_VIDEO_ENCODING:      return "Video Encoding";
+       case V4L2_CID_MPEG_VIDEO_ASPECT:        return "Video Aspect";
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES:      return "Video B Frames";
+       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:      return "Video GOP Size";
+       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:   return "Video GOP Closure";
+       case V4L2_CID_MPEG_VIDEO_PULLDOWN:      return "Video Pulldown";
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:  return "Video Bitrate Mode";
+       case V4L2_CID_MPEG_VIDEO_BITRATE:       return "Video Bitrate";
+       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:  return "Video Peak Bitrate";
+       case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
+       case V4L2_CID_MPEG_VIDEO_MUTE:          return "Video Mute";
+       case V4L2_CID_MPEG_VIDEO_MUTE_YUV:      return "Video Mute YUV";
+       case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:       return "Decoder Slice Interface";
+       case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:  return "MPEG4 Loop Filter Enable";
+       case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:       return "Number of Intra Refresh MBs";
+       case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:               return "Frame Level Rate Control Enable";
+       case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:                  return "H264 MB Level Rate Control";
+       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:                   return "Sequence Header Mode";
+       case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC:                   return "Max Number of Reference Pics";
+       case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:               return "H263 I-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:               return "H263 P-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:               return "H263 B-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:                   return "H263 Minimum QP Value";
+       case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:                   return "H263 Maximum QP Value";
+       case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:               return "H264 I-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:               return "H264 P-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:               return "H264 B-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:                   return "H264 Maximum QP Value";
+       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:                   return "H264 Minimum QP Value";
+       case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:            return "H264 8x8 Transform Enable";
+       case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:                 return "H264 CPB Buffer Size";
+       case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:             return "H264 Entropy Mode";
+       case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:                 return "H264 I-Frame Period";
+       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:                    return "H264 Level";
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:        return "H264 Loop Filter Alpha Offset";
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:         return "H264 Loop Filter Beta Offset";
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:         return "H264 Loop Filter Mode";
+       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:                  return "H264 Profile";
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:       return "Vertical Size of SAR";
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:        return "Horizontal Size of SAR";
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:           return "Aspect Ratio VUI Enable";
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:              return "VUI Aspect Ratio IDC";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:              return "MPEG4 I-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:              return "MPEG4 P-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:              return "MPEG4 B-Frame QP Value";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:                  return "MPEG4 Minimum QP Value";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:                  return "MPEG4 Maximum QP Value";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:                   return "MPEG4 Level";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:                 return "MPEG4 Profile";
+       case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:                    return "Quarter Pixel Search Enable";
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:         return "Maximum Bytes in a Slice";
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:            return "Number of MBs in a Slice";
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:              return "Slice Partitioning Method";
+       case V4L2_CID_MPEG_VIDEO_VBV_SIZE:                      return "VBV Buffer Size";
+       case V4L2_CID_MPEG_VIDEO_DEC_PTS:                       return "Video Decoder PTS";
+       case V4L2_CID_MPEG_VIDEO_DEC_FRAME:                     return "Video Decoder Frame Count";
+
+       /* CAMERA controls */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_CAMERA_CLASS:             return "Camera Controls";
+       case V4L2_CID_EXPOSURE_AUTO:            return "Auto Exposure";
+       case V4L2_CID_EXPOSURE_ABSOLUTE:        return "Exposure Time, Absolute";
+       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:   return "Exposure, Dynamic Framerate";
+       case V4L2_CID_PAN_RELATIVE:             return "Pan, Relative";
+       case V4L2_CID_TILT_RELATIVE:            return "Tilt, Relative";
+       case V4L2_CID_PAN_RESET:                return "Pan, Reset";
+       case V4L2_CID_TILT_RESET:               return "Tilt, Reset";
+       case V4L2_CID_PAN_ABSOLUTE:             return "Pan, Absolute";
+       case V4L2_CID_TILT_ABSOLUTE:            return "Tilt, Absolute";
+       case V4L2_CID_FOCUS_ABSOLUTE:           return "Focus, Absolute";
+       case V4L2_CID_FOCUS_RELATIVE:           return "Focus, Relative";
+       case V4L2_CID_FOCUS_AUTO:               return "Focus, Automatic Continuous";
+       case V4L2_CID_ZOOM_ABSOLUTE:            return "Zoom, Absolute";
+       case V4L2_CID_ZOOM_RELATIVE:            return "Zoom, Relative";
+       case V4L2_CID_ZOOM_CONTINUOUS:          return "Zoom, Continuous";
+       case V4L2_CID_PRIVACY:                  return "Privacy";
+       case V4L2_CID_IRIS_ABSOLUTE:            return "Iris, Absolute";
+       case V4L2_CID_IRIS_RELATIVE:            return "Iris, Relative";
+       case V4L2_CID_AUTO_EXPOSURE_BIAS:       return "Auto Exposure, Bias";
+       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset";
+       case V4L2_CID_WIDE_DYNAMIC_RANGE:       return "Wide Dynamic Range";
+       case V4L2_CID_IMAGE_STABILIZATION:      return "Image Stabilization";
+       case V4L2_CID_ISO_SENSITIVITY:          return "ISO Sensitivity";
+       case V4L2_CID_ISO_SENSITIVITY_AUTO:     return "ISO Sensitivity, Auto";
+       case V4L2_CID_EXPOSURE_METERING:        return "Exposure, Metering Mode";
+       case V4L2_CID_SCENE_MODE:               return "Scene Mode";
+       case V4L2_CID_3A_LOCK:                  return "3A Lock";
+       case V4L2_CID_AUTO_FOCUS_START:         return "Auto Focus, Start";
+       case V4L2_CID_AUTO_FOCUS_STOP:          return "Auto Focus, Stop";
+       case V4L2_CID_AUTO_FOCUS_STATUS:        return "Auto Focus, Status";
+       case V4L2_CID_AUTO_FOCUS_RANGE:         return "Auto Focus, Range";
+
+       /* FM Radio Modulator control */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_FM_TX_CLASS:              return "FM Radio Modulator Controls";
+       case V4L2_CID_RDS_TX_DEVIATION:         return "RDS Signal Deviation";
+       case V4L2_CID_RDS_TX_PI:                return "RDS Program ID";
+       case V4L2_CID_RDS_TX_PTY:               return "RDS Program Type";
+       case V4L2_CID_RDS_TX_PS_NAME:           return "RDS PS Name";
+       case V4L2_CID_RDS_TX_RADIO_TEXT:        return "RDS Radio Text";
+       case V4L2_CID_AUDIO_LIMITER_ENABLED:    return "Audio Limiter Feature Enabled";
+       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
+       case V4L2_CID_AUDIO_LIMITER_DEVIATION:  return "Audio Limiter Deviation";
+       case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Enabled";
+       case V4L2_CID_AUDIO_COMPRESSION_GAIN:   return "Audio Compression Gain";
+       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
+       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
+       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
+       case V4L2_CID_PILOT_TONE_ENABLED:       return "Pilot Tone Feature Enabled";
+       case V4L2_CID_PILOT_TONE_DEVIATION:     return "Pilot Tone Deviation";
+       case V4L2_CID_PILOT_TONE_FREQUENCY:     return "Pilot Tone Frequency";
+       case V4L2_CID_TUNE_PREEMPHASIS:         return "Pre-Emphasis";
+       case V4L2_CID_TUNE_POWER_LEVEL:         return "Tune Power Level";
+       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:   return "Tune Antenna Capacitor";
+
+       /* Flash controls */
+       case V4L2_CID_FLASH_CLASS:              return "Flash Controls";
+       case V4L2_CID_FLASH_LED_MODE:           return "LED Mode";
+       case V4L2_CID_FLASH_STROBE_SOURCE:      return "Strobe Source";
+       case V4L2_CID_FLASH_STROBE:             return "Strobe";
+       case V4L2_CID_FLASH_STROBE_STOP:        return "Stop Strobe";
+       case V4L2_CID_FLASH_STROBE_STATUS:      return "Strobe Status";
+       case V4L2_CID_FLASH_TIMEOUT:            return "Strobe Timeout";
+       case V4L2_CID_FLASH_INTENSITY:          return "Intensity, Flash Mode";
+       case V4L2_CID_FLASH_TORCH_INTENSITY:    return "Intensity, Torch Mode";
+       case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, Indicator";
+       case V4L2_CID_FLASH_FAULT:              return "Faults";
+       case V4L2_CID_FLASH_CHARGE:             return "Charge";
+       case V4L2_CID_FLASH_READY:              return "Ready to Strobe";
+
+       /* JPEG encoder controls */
+       /* Keep the order of the 'case's the same as in videodev2.h! */
+       case V4L2_CID_JPEG_CLASS:               return "JPEG Compression Controls";
+       case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:  return "Chroma Subsampling";
+       case V4L2_CID_JPEG_RESTART_INTERVAL:    return "Restart Interval";
+       case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
+       case V4L2_CID_JPEG_ACTIVE_MARKER:       return "Active Markers";
+
+       /* Image source controls */
+       case V4L2_CID_IMAGE_SOURCE_CLASS:       return "Image Source Controls";
+       case V4L2_CID_VBLANK:                   return "Vertical Blanking";
+       case V4L2_CID_HBLANK:                   return "Horizontal Blanking";
+       case V4L2_CID_ANALOGUE_GAIN:            return "Analogue Gain";
+
+       /* Image processing controls */
+       case V4L2_CID_IMAGE_PROC_CLASS:         return "Image Processing Controls";
+       case V4L2_CID_LINK_FREQ:                return "Link Frequency";
+       case V4L2_CID_PIXEL_RATE:               return "Pixel Rate";
+
+       default:
+               return NULL;
+       }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+                   s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+       *name = v4l2_ctrl_get_name(id);
+       *flags = 0;
+
+       switch (id) {
+       case V4L2_CID_AUDIO_MUTE:
+       case V4L2_CID_AUDIO_LOUDNESS:
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+       case V4L2_CID_AUTOGAIN:
+       case V4L2_CID_HFLIP:
+       case V4L2_CID_VFLIP:
+       case V4L2_CID_HUE_AUTO:
+       case V4L2_CID_CHROMA_AGC:
+       case V4L2_CID_COLOR_KILLER:
+       case V4L2_CID_AUTOBRIGHTNESS:
+       case V4L2_CID_MPEG_AUDIO_MUTE:
+       case V4L2_CID_MPEG_VIDEO_MUTE:
+       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+       case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+       case V4L2_CID_FOCUS_AUTO:
+       case V4L2_CID_PRIVACY:
+       case V4L2_CID_AUDIO_LIMITER_ENABLED:
+       case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+       case V4L2_CID_PILOT_TONE_ENABLED:
+       case V4L2_CID_ILLUMINATORS_1:
+       case V4L2_CID_ILLUMINATORS_2:
+       case V4L2_CID_FLASH_STROBE_STATUS:
+       case V4L2_CID_FLASH_CHARGE:
+       case V4L2_CID_FLASH_READY:
+       case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+       case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+       case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+       case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+       case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+       case V4L2_CID_WIDE_DYNAMIC_RANGE:
+       case V4L2_CID_IMAGE_STABILIZATION:
+               *type = V4L2_CTRL_TYPE_BOOLEAN;
+               *min = 0;
+               *max = *step = 1;
+               break;
+       case V4L2_CID_PAN_RESET:
+       case V4L2_CID_TILT_RESET:
+       case V4L2_CID_FLASH_STROBE:
+       case V4L2_CID_FLASH_STROBE_STOP:
+       case V4L2_CID_AUTO_FOCUS_START:
+       case V4L2_CID_AUTO_FOCUS_STOP:
+               *type = V4L2_CTRL_TYPE_BUTTON;
+               *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+               *min = *max = *step = *def = 0;
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+       case V4L2_CID_MPEG_AUDIO_MODE:
+       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+       case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+       case V4L2_CID_MPEG_AUDIO_CRC:
+       case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+       case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+       case V4L2_CID_MPEG_VIDEO_ASPECT:
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+       case V4L2_CID_MPEG_STREAM_TYPE:
+       case V4L2_CID_MPEG_STREAM_VBI_FMT:
+       case V4L2_CID_EXPOSURE_AUTO:
+       case V4L2_CID_AUTO_FOCUS_RANGE:
+       case V4L2_CID_COLORFX:
+       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+       case V4L2_CID_TUNE_PREEMPHASIS:
+       case V4L2_CID_FLASH_LED_MODE:
+       case V4L2_CID_FLASH_STROBE_SOURCE:
+       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+       case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+       case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+       case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+       case V4L2_CID_ISO_SENSITIVITY_AUTO:
+       case V4L2_CID_EXPOSURE_METERING:
+       case V4L2_CID_SCENE_MODE:
+               *type = V4L2_CTRL_TYPE_MENU;
+               break;
+       case V4L2_CID_LINK_FREQ:
+               *type = V4L2_CTRL_TYPE_INTEGER_MENU;
+               break;
+       case V4L2_CID_RDS_TX_PS_NAME:
+       case V4L2_CID_RDS_TX_RADIO_TEXT:
+               *type = V4L2_CTRL_TYPE_STRING;
+               break;
+       case V4L2_CID_ISO_SENSITIVITY:
+       case V4L2_CID_AUTO_EXPOSURE_BIAS:
+               *type = V4L2_CTRL_TYPE_INTEGER_MENU;
+               break;
+       case V4L2_CID_USER_CLASS:
+       case V4L2_CID_CAMERA_CLASS:
+       case V4L2_CID_MPEG_CLASS:
+       case V4L2_CID_FM_TX_CLASS:
+       case V4L2_CID_FLASH_CLASS:
+       case V4L2_CID_JPEG_CLASS:
+       case V4L2_CID_IMAGE_SOURCE_CLASS:
+       case V4L2_CID_IMAGE_PROC_CLASS:
+               *type = V4L2_CTRL_TYPE_CTRL_CLASS;
+               /* You can neither read not write these */
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
+               *min = *max = *step = *def = 0;
+               break;
+       case V4L2_CID_BG_COLOR:
+               *type = V4L2_CTRL_TYPE_INTEGER;
+               *step = 1;
+               *min = 0;
+               /* Max is calculated as RGB888 that is 2^24 */
+               *max = 0xFFFFFF;
+               break;
+       case V4L2_CID_FLASH_FAULT:
+       case V4L2_CID_JPEG_ACTIVE_MARKER:
+       case V4L2_CID_3A_LOCK:
+       case V4L2_CID_AUTO_FOCUS_STATUS:
+               *type = V4L2_CTRL_TYPE_BITMASK;
+               break;
+       case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+       case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+               *type = V4L2_CTRL_TYPE_INTEGER;
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+               break;
+       case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
+       case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+               *flags |= V4L2_CTRL_FLAG_VOLATILE;
+               /* Fall through */
+       case V4L2_CID_PIXEL_RATE:
+               *type = V4L2_CTRL_TYPE_INTEGER64;
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+               *min = *max = *step = *def = 0;
+               break;
+       default:
+               *type = V4L2_CTRL_TYPE_INTEGER;
+               break;
+       }
+       switch (id) {
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+       case V4L2_CID_MPEG_AUDIO_MODE:
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               *flags |= V4L2_CTRL_FLAG_UPDATE;
+               break;
+       case V4L2_CID_AUDIO_VOLUME:
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+       case V4L2_CID_BRIGHTNESS:
+       case V4L2_CID_CONTRAST:
+       case V4L2_CID_SATURATION:
+       case V4L2_CID_HUE:
+       case V4L2_CID_RED_BALANCE:
+       case V4L2_CID_BLUE_BALANCE:
+       case V4L2_CID_GAMMA:
+       case V4L2_CID_SHARPNESS:
+       case V4L2_CID_CHROMA_GAIN:
+       case V4L2_CID_RDS_TX_DEVIATION:
+       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+       case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+       case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+       case V4L2_CID_PILOT_TONE_DEVIATION:
+       case V4L2_CID_PILOT_TONE_FREQUENCY:
+       case V4L2_CID_TUNE_POWER_LEVEL:
+       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+               *flags |= V4L2_CTRL_FLAG_SLIDER;
+               break;
+       case V4L2_CID_PAN_RELATIVE:
+       case V4L2_CID_TILT_RELATIVE:
+       case V4L2_CID_FOCUS_RELATIVE:
+       case V4L2_CID_IRIS_RELATIVE:
+       case V4L2_CID_ZOOM_RELATIVE:
+               *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+               break;
+       case V4L2_CID_FLASH_STROBE_STATUS:
+       case V4L2_CID_AUTO_FOCUS_STATUS:
+       case V4L2_CID_FLASH_READY:
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+               break;
+       }
+}
+EXPORT_SYMBOL(v4l2_ctrl_fill);
+
+/* Helper function to determine whether the control type is compatible with
+   VIDIOC_G/S_CTRL. */
+static bool type_is_int(const struct v4l2_ctrl *ctrl)
+{
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER64:
+       case V4L2_CTRL_TYPE_STRING:
+               /* Nope, these need v4l2_ext_control */
+               return false;
+       default:
+               return true;
+       }
+}
+
+static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
+{
+       memset(ev->reserved, 0, sizeof(ev->reserved));
+       ev->type = V4L2_EVENT_CTRL;
+       ev->id = ctrl->id;
+       ev->u.ctrl.changes = changes;
+       ev->u.ctrl.type = ctrl->type;
+       ev->u.ctrl.flags = ctrl->flags;
+       if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+               ev->u.ctrl.value64 = 0;
+       else
+               ev->u.ctrl.value64 = ctrl->cur.val64;
+       ev->u.ctrl.minimum = ctrl->minimum;
+       ev->u.ctrl.maximum = ctrl->maximum;
+       if (ctrl->type == V4L2_CTRL_TYPE_MENU
+           || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
+               ev->u.ctrl.step = 1;
+       else
+               ev->u.ctrl.step = ctrl->step;
+       ev->u.ctrl.default_value = ctrl->default_value;
+}
+
+static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
+{
+       struct v4l2_event ev;
+       struct v4l2_subscribed_event *sev;
+
+       if (list_empty(&ctrl->ev_subs))
+               return;
+       fill_event(&ev, ctrl, changes);
+
+       list_for_each_entry(sev, &ctrl->ev_subs, node)
+               if (sev->fh != fh ||
+                   (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))
+                       v4l2_event_queue_fh(sev->fh, &ev);
+}
+
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl)
+{
+       u32 len;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               len = strlen(ctrl->cur.string);
+               if (c->size < len + 1) {
+                       c->size = len + 1;
+                       return -ENOSPC;
+               }
+               return copy_to_user(c->string, ctrl->cur.string,
+                                               len + 1) ? -EFAULT : 0;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               c->value64 = ctrl->cur.val64;
+               break;
+       default:
+               c->value = ctrl->cur.val;
+               break;
+       }
+       return 0;
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl)
+{
+       int ret;
+       u32 size;
+
+       ctrl->is_new = 1;
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER64:
+               ctrl->val64 = c->value64;
+               break;
+       case V4L2_CTRL_TYPE_STRING:
+               size = c->size;
+               if (size == 0)
+                       return -ERANGE;
+               if (size > ctrl->maximum + 1)
+                       size = ctrl->maximum + 1;
+               ret = copy_from_user(ctrl->string, c->string, size);
+               if (!ret) {
+                       char last = ctrl->string[size - 1];
+
+                       ctrl->string[size - 1] = 0;
+                       /* If the string was longer than ctrl->maximum,
+                          then return an error. */
+                       if (strlen(ctrl->string) == ctrl->maximum && last)
+                               return -ERANGE;
+               }
+               return ret ? -EFAULT : 0;
+       default:
+               ctrl->val = c->value;
+               break;
+       }
+       return 0;
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl)
+{
+       u32 len;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               len = strlen(ctrl->string);
+               if (c->size < len + 1) {
+                       c->size = ctrl->maximum + 1;
+                       return -ENOSPC;
+               }
+               return copy_to_user(c->string, ctrl->string,
+                                               len + 1) ? -EFAULT : 0;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               c->value64 = ctrl->val64;
+               break;
+       default:
+               c->value = ctrl->val;
+               break;
+       }
+       return 0;
+}
+
+/* Copy the new value to the current value. */
+static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
+                                               bool update_inactive)
+{
+       bool changed = false;
+
+       if (ctrl == NULL)
+               return;
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_BUTTON:
+               changed = true;
+               break;
+       case V4L2_CTRL_TYPE_STRING:
+               /* strings are always 0-terminated */
+               changed = strcmp(ctrl->string, ctrl->cur.string);
+               strcpy(ctrl->cur.string, ctrl->string);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               changed = ctrl->val64 != ctrl->cur.val64;
+               ctrl->cur.val64 = ctrl->val64;
+               break;
+       default:
+               changed = ctrl->val != ctrl->cur.val;
+               ctrl->cur.val = ctrl->val;
+               break;
+       }
+       if (update_inactive) {
+               /* Note: update_inactive can only be true for auto clusters. */
+               ctrl->flags &=
+                       ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
+               if (!is_cur_manual(ctrl->cluster[0])) {
+                       ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+                       if (ctrl->cluster[0]->has_volatiles)
+                               ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+               }
+               fh = NULL;
+       }
+       if (changed || update_inactive) {
+               /* If a control was changed that was not one of the controls
+                  modified by the application, then send the event to all. */
+               if (!ctrl->is_new)
+                       fh = NULL;
+               send_event(fh, ctrl,
+                       (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
+                       (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
+       }
+}
+
+/* Copy the current value to the new value */
+static void cur_to_new(struct v4l2_ctrl *ctrl)
+{
+       if (ctrl == NULL)
+               return;
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               /* strings are always 0-terminated */
+               strcpy(ctrl->string, ctrl->cur.string);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               ctrl->val64 = ctrl->cur.val64;
+               break;
+       default:
+               ctrl->val = ctrl->cur.val;
+               break;
+       }
+}
+
+/* Return non-zero if one or more of the controls in the cluster has a new
+   value that differs from the current value. */
+static int cluster_changed(struct v4l2_ctrl *master)
+{
+       int diff = 0;
+       int i;
+
+       for (i = 0; !diff && i < master->ncontrols; i++) {
+               struct v4l2_ctrl *ctrl = master->cluster[i];
+
+               if (ctrl == NULL)
+                       continue;
+               switch (ctrl->type) {
+               case V4L2_CTRL_TYPE_BUTTON:
+                       /* Button controls are always 'different' */
+                       return 1;
+               case V4L2_CTRL_TYPE_STRING:
+                       /* strings are always 0-terminated */
+                       diff = strcmp(ctrl->string, ctrl->cur.string);
+                       break;
+               case V4L2_CTRL_TYPE_INTEGER64:
+                       diff = ctrl->val64 != ctrl->cur.val64;
+                       break;
+               default:
+                       diff = ctrl->val != ctrl->cur.val;
+                       break;
+               }
+       }
+       return diff;
+}
+
+/* Validate integer-type control */
+static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval)
+{
+       s32 val = *pval;
+       u32 offset;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+               /* Round towards the closest legal value */
+               val += ctrl->step / 2;
+               if (val < ctrl->minimum)
+                       val = ctrl->minimum;
+               if (val > ctrl->maximum)
+                       val = ctrl->maximum;
+               offset = val - ctrl->minimum;
+               offset = ctrl->step * (offset / ctrl->step);
+               val = ctrl->minimum + offset;
+               *pval = val;
+               return 0;
+
+       case V4L2_CTRL_TYPE_BOOLEAN:
+               *pval = !!val;
+               return 0;
+
+       case V4L2_CTRL_TYPE_MENU:
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+               if (val < ctrl->minimum || val > ctrl->maximum)
+                       return -ERANGE;
+               if (ctrl->menu_skip_mask & (1 << val))
+                       return -EINVAL;
+               if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
+                   ctrl->qmenu[val][0] == '\0')
+                       return -EINVAL;
+               return 0;
+
+       case V4L2_CTRL_TYPE_BITMASK:
+               *pval &= ctrl->maximum;
+               return 0;
+
+       case V4L2_CTRL_TYPE_BUTTON:
+       case V4L2_CTRL_TYPE_CTRL_CLASS:
+               *pval = 0;
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+/* Validate a new control */
+static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
+{
+       char *s = c->string;
+       size_t len;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+       case V4L2_CTRL_TYPE_BOOLEAN:
+       case V4L2_CTRL_TYPE_MENU:
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+       case V4L2_CTRL_TYPE_BITMASK:
+       case V4L2_CTRL_TYPE_BUTTON:
+       case V4L2_CTRL_TYPE_CTRL_CLASS:
+               return validate_new_int(ctrl, &c->value);
+
+       case V4L2_CTRL_TYPE_INTEGER64:
+               return 0;
+
+       case V4L2_CTRL_TYPE_STRING:
+               len = strlen(s);
+               if (len < ctrl->minimum)
+                       return -ERANGE;
+               if ((len - ctrl->minimum) % ctrl->step)
+                       return -ERANGE;
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static inline u32 node2id(struct list_head *node)
+{
+       return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
+}
+
+/* Set the handler's error code if it wasn't set earlier already */
+static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+{
+       if (hdl->error == 0)
+               hdl->error = err;
+       return err;
+}
+
+/* Initialize the handler */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+                          unsigned nr_of_controls_hint)
+{
+       hdl->lock = &hdl->_lock;
+       mutex_init(hdl->lock);
+       INIT_LIST_HEAD(&hdl->ctrls);
+       INIT_LIST_HEAD(&hdl->ctrl_refs);
+       hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
+       hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]),
+                              GFP_KERNEL);
+       hdl->error = hdl->buckets ? 0 : -ENOMEM;
+       return hdl->error;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_init);
+
+/* Free all controls and control refs */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+{
+       struct v4l2_ctrl_ref *ref, *next_ref;
+       struct v4l2_ctrl *ctrl, *next_ctrl;
+       struct v4l2_subscribed_event *sev, *next_sev;
+
+       if (hdl == NULL || hdl->buckets == NULL)
+               return;
+
+       mutex_lock(hdl->lock);
+       /* Free all nodes */
+       list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+               list_del(&ref->node);
+               kfree(ref);
+       }
+       /* Free all controls owned by the handler */
+       list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
+               list_del(&ctrl->node);
+               list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
+                       list_del(&sev->node);
+               kfree(ctrl);
+       }
+       kfree(hdl->buckets);
+       hdl->buckets = NULL;
+       hdl->cached = NULL;
+       hdl->error = 0;
+       mutex_unlock(hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_free);
+
+/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
+   be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
+   with applications that do not use the NEXT_CTRL flag.
+
+   We just find the n-th private user control. It's O(N), but that should not
+   be an issue in this particular case. */
+static struct v4l2_ctrl_ref *find_private_ref(
+               struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref;
+
+       id -= V4L2_CID_PRIVATE_BASE;
+       list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+               /* Search for private user controls that are compatible with
+                  VIDIOC_G/S_CTRL. */
+               if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+                   V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
+                       if (!type_is_int(ref->ctrl))
+                               continue;
+                       if (id == 0)
+                               return ref;
+                       id--;
+               }
+       }
+       return NULL;
+}
+
+/* Find a control with the given ID. */
+static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref;
+       int bucket;
+
+       id &= V4L2_CTRL_ID_MASK;
+
+       /* Old-style private controls need special handling */
+       if (id >= V4L2_CID_PRIVATE_BASE)
+               return find_private_ref(hdl, id);
+       bucket = id % hdl->nr_of_buckets;
+
+       /* Simple optimization: cache the last control found */
+       if (hdl->cached && hdl->cached->ctrl->id == id)
+               return hdl->cached;
+
+       /* Not in cache, search the hash */
+       ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
+       while (ref && ref->ctrl->id != id)
+               ref = ref->next;
+
+       if (ref)
+               hdl->cached = ref; /* cache it! */
+       return ref;
+}
+
+/* Find a control with the given ID. Take the handler's lock first. */
+static struct v4l2_ctrl_ref *find_ref_lock(
+               struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref = NULL;
+
+       if (hdl) {
+               mutex_lock(hdl->lock);
+               ref = find_ref(hdl, id);
+               mutex_unlock(hdl->lock);
+       }
+       return ref;
+}
+
+/* Find a control with the given ID. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+       struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+       return ref ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_find);
+
+/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
+static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+                          struct v4l2_ctrl *ctrl)
+{
+       struct v4l2_ctrl_ref *ref;
+       struct v4l2_ctrl_ref *new_ref;
+       u32 id = ctrl->id;
+       u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+       int bucket = id % hdl->nr_of_buckets;   /* which bucket to use */
+
+       /* Automatically add the control class if it is not yet present. */
+       if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+               if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
+                       return hdl->error;
+
+       if (hdl->error)
+               return hdl->error;
+
+       new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+       if (!new_ref)
+               return handler_set_err(hdl, -ENOMEM);
+       new_ref->ctrl = ctrl;
+       if (ctrl->handler == hdl) {
+               /* By default each control starts in a cluster of its own.
+                  new_ref->ctrl is basically a cluster array with one
+                  element, so that's perfect to use as the cluster pointer.
+                  But only do this for the handler that owns the control. */
+               ctrl->cluster = &new_ref->ctrl;
+               ctrl->ncontrols = 1;
+       }
+
+       INIT_LIST_HEAD(&new_ref->node);
+
+       mutex_lock(hdl->lock);
+
+       /* Add immediately at the end of the list if the list is empty, or if
+          the last element in the list has a lower ID.
+          This ensures that when elements are added in ascending order the
+          insertion is an O(1) operation. */
+       if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
+               list_add_tail(&new_ref->node, &hdl->ctrl_refs);
+               goto insert_in_hash;
+       }
+
+       /* Find insert position in sorted list */
+       list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+               if (ref->ctrl->id < id)
+                       continue;
+               /* Don't add duplicates */
+               if (ref->ctrl->id == id) {
+                       kfree(new_ref);
+                       goto unlock;
+               }
+               list_add(&new_ref->node, ref->node.prev);
+               break;
+       }
+
+insert_in_hash:
+       /* Insert the control node in the hash */
+       new_ref->next = hdl->buckets[bucket];
+       hdl->buckets[bucket] = new_ref;
+
+unlock:
+       mutex_unlock(hdl->lock);
+       return 0;
+}
+
+/* Add a new control */
+static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, const char *name, enum v4l2_ctrl_type type,
+                       s32 min, s32 max, u32 step, s32 def,
+                       u32 flags, const char * const *qmenu,
+                       const s64 *qmenu_int, void *priv)
+{
+       struct v4l2_ctrl *ctrl;
+       unsigned sz_extra = 0;
+
+       if (hdl->error)
+               return NULL;
+
+       /* Sanity checks */
+       if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+           (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+           (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
+           (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
+           (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) ||
+           (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
+               handler_set_err(hdl, -ERANGE);
+               return NULL;
+       }
+       if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
+               handler_set_err(hdl, -ERANGE);
+               return NULL;
+       }
+       if ((type == V4L2_CTRL_TYPE_INTEGER ||
+            type == V4L2_CTRL_TYPE_MENU ||
+            type == V4L2_CTRL_TYPE_INTEGER_MENU ||
+            type == V4L2_CTRL_TYPE_BOOLEAN) &&
+           (def < min || def > max)) {
+               handler_set_err(hdl, -ERANGE);
+               return NULL;
+       }
+       if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
+               handler_set_err(hdl, -ERANGE);
+               return NULL;
+       }
+
+       if (type == V4L2_CTRL_TYPE_BUTTON)
+               flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+       else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+               flags |= V4L2_CTRL_FLAG_READ_ONLY;
+       else if (type == V4L2_CTRL_TYPE_STRING)
+               sz_extra += 2 * (max + 1);
+
+       ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+       if (ctrl == NULL) {
+               handler_set_err(hdl, -ENOMEM);
+               return NULL;
+       }
+
+       INIT_LIST_HEAD(&ctrl->node);
+       INIT_LIST_HEAD(&ctrl->ev_subs);
+       ctrl->handler = hdl;
+       ctrl->ops = ops;
+       ctrl->id = id;
+       ctrl->name = name;
+       ctrl->type = type;
+       ctrl->flags = flags;
+       ctrl->minimum = min;
+       ctrl->maximum = max;
+       ctrl->step = step;
+       if (type == V4L2_CTRL_TYPE_MENU)
+               ctrl->qmenu = qmenu;
+       else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)
+               ctrl->qmenu_int = qmenu_int;
+       ctrl->priv = priv;
+       ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+
+       if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+               ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
+               ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
+               if (ctrl->minimum)
+                       memset(ctrl->cur.string, ' ', ctrl->minimum);
+       }
+       if (handler_new_ref(hdl, ctrl)) {
+               kfree(ctrl);
+               return NULL;
+       }
+       mutex_lock(hdl->lock);
+       list_add_tail(&ctrl->node, &hdl->ctrls);
+       mutex_unlock(hdl->lock);
+       return ctrl;
+}
+
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_config *cfg, void *priv)
+{
+       bool is_menu;
+       struct v4l2_ctrl *ctrl;
+       const char *name = cfg->name;
+       const char * const *qmenu = cfg->qmenu;
+       const s64 *qmenu_int = cfg->qmenu_int;
+       enum v4l2_ctrl_type type = cfg->type;
+       u32 flags = cfg->flags;
+       s32 min = cfg->min;
+       s32 max = cfg->max;
+       u32 step = cfg->step;
+       s32 def = cfg->def;
+
+       if (name == NULL)
+               v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
+                                                               &def, &flags);
+
+       is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU ||
+                  cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU);
+       if (is_menu)
+               WARN_ON(step);
+       else
+               WARN_ON(cfg->menu_skip_mask);
+       if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL)
+               qmenu = v4l2_ctrl_get_menu(cfg->id);
+       else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU &&
+                qmenu_int == NULL) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+
+       ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
+                       type, min, max,
+                       is_menu ? cfg->menu_skip_mask : step,
+                       def, flags, qmenu, qmenu_int, priv);
+       if (ctrl)
+               ctrl->is_private = cfg->is_private;
+       return ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_custom);
+
+/* Helper function for standard non-menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 min, s32 max, u32 step, s32 def)
+{
+       const char *name;
+       enum v4l2_ctrl_type type;
+       u32 flags;
+
+       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+       if (type == V4L2_CTRL_TYPE_MENU
+           || type == V4L2_CTRL_TYPE_INTEGER_MENU) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+       return v4l2_ctrl_new(hdl, ops, id, name, type,
+                            min, max, step, def, flags, NULL, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std);
+
+/* Helper function for standard menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 max, s32 mask, s32 def)
+{
+       const char * const *qmenu = v4l2_ctrl_get_menu(id);
+       const char *name;
+       enum v4l2_ctrl_type type;
+       s32 min;
+       s32 step;
+       u32 flags;
+
+       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+       if (type != V4L2_CTRL_TYPE_MENU) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+       return v4l2_ctrl_new(hdl, ops, id, name, type,
+                            0, max, mask, def, flags, qmenu, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+
+/* Helper function for standard integer menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
+                       const struct v4l2_ctrl_ops *ops,
+                       u32 id, s32 max, s32 def, const s64 *qmenu_int)
+{
+       const char *name;
+       enum v4l2_ctrl_type type;
+       s32 min;
+       s32 step;
+       u32 flags;
+
+       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+       if (type != V4L2_CTRL_TYPE_INTEGER_MENU) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+       return v4l2_ctrl_new(hdl, ops, id, name, type,
+                            0, max, 0, def, flags, NULL, qmenu_int, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
+
+/* Add a control from another handler to this handler */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+                                         struct v4l2_ctrl *ctrl)
+{
+       if (hdl == NULL || hdl->error)
+               return NULL;
+       if (ctrl == NULL) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
+       if (ctrl->handler == hdl)
+               return ctrl;
+       return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
+}
+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_ref *ref;
+       int ret = 0;
+
+       /* Do nothing if either handler is NULL or if they are the same */
+       if (!hdl || !add || hdl == add)
+               return 0;
+       if (hdl->error)
+               return hdl->error;
+       mutex_lock(add->lock);
+       list_for_each_entry(ref, &add->ctrl_refs, node) {
+               struct v4l2_ctrl *ctrl = ref->ctrl;
+
+               /* Skip handler-private controls. */
+               if (ctrl->is_private)
+                       continue;
+               /* And control classes */
+               if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+                       continue;
+               ret = handler_new_ref(hdl, ctrl);
+               if (ret)
+                       break;
+       }
+       mutex_unlock(add->lock);
+       return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_handler);
+
+/* Cluster controls */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
+{
+       bool has_volatiles = false;
+       int i;
+
+       /* The first control is the master control and it must not be NULL */
+       BUG_ON(ncontrols == 0 || controls[0] == NULL);
+
+       for (i = 0; i < ncontrols; i++) {
+               if (controls[i]) {
+                       controls[i]->cluster = controls;
+                       controls[i]->ncontrols = ncontrols;
+                       if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
+                               has_volatiles = true;
+               }
+       }
+       controls[0]->has_volatiles = has_volatiles;
+}
+EXPORT_SYMBOL(v4l2_ctrl_cluster);
+
+void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
+                           u8 manual_val, bool set_volatile)
+{
+       struct v4l2_ctrl *master = controls[0];
+       u32 flag = 0;
+       int i;
+
+       v4l2_ctrl_cluster(ncontrols, controls);
+       WARN_ON(ncontrols <= 1);
+       WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
+       WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
+       master->is_auto = true;
+       master->has_volatiles = set_volatile;
+       master->manual_mode_value = manual_val;
+       master->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+       if (!is_cur_manual(master))
+               flag = V4L2_CTRL_FLAG_INACTIVE |
+                       (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
+
+       for (i = 1; i < ncontrols; i++)
+               if (controls[i])
+                       controls[i]->flags |= flag;
+}
+EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
+
+/* Activate/deactivate a control. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+{
+       /* invert since the actual flag is called 'inactive' */
+       bool inactive = !active;
+       bool old;
+
+       if (ctrl == NULL)
+               return;
+
+       if (inactive)
+               /* set V4L2_CTRL_FLAG_INACTIVE */
+               old = test_and_set_bit(4, &ctrl->flags);
+       else
+               /* clear V4L2_CTRL_FLAG_INACTIVE */
+               old = test_and_clear_bit(4, &ctrl->flags);
+       if (old != inactive)
+               send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+}
+EXPORT_SYMBOL(v4l2_ctrl_activate);
+
+/* Grab/ungrab a control.
+   Typically used when streaming starts and you want to grab controls,
+   preventing the user from changing them.
+
+   Just call this and the framework will block any attempts to change
+   these controls. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+{
+       bool old;
+
+       if (ctrl == NULL)
+               return;
+
+       v4l2_ctrl_lock(ctrl);
+       if (grabbed)
+               /* set V4L2_CTRL_FLAG_GRABBED */
+               old = test_and_set_bit(1, &ctrl->flags);
+       else
+               /* clear V4L2_CTRL_FLAG_GRABBED */
+               old = test_and_clear_bit(1, &ctrl->flags);
+       if (old != grabbed)
+               send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+       v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_grab);
+
+/* Log the control name and value */
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
+                    const char *prefix, const char *colon)
+{
+       if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
+               return;
+       if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+               return;
+
+       printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+               printk(KERN_CONT "%d", ctrl->cur.val);
+               break;
+       case V4L2_CTRL_TYPE_BOOLEAN:
+               printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
+               break;
+       case V4L2_CTRL_TYPE_MENU:
+               printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+               printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]);
+               break;
+       case V4L2_CTRL_TYPE_BITMASK:
+               printk(KERN_CONT "0x%08x", ctrl->cur.val);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               printk(KERN_CONT "%lld", ctrl->cur.val64);
+               break;
+       case V4L2_CTRL_TYPE_STRING:
+               printk(KERN_CONT "%s", ctrl->cur.string);
+               break;
+       default:
+               printk(KERN_CONT "unknown type %d", ctrl->type);
+               break;
+       }
+       if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
+                          V4L2_CTRL_FLAG_GRABBED |
+                          V4L2_CTRL_FLAG_VOLATILE)) {
+               if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+                       printk(KERN_CONT " inactive");
+               if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+                       printk(KERN_CONT " grabbed");
+               if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
+                       printk(KERN_CONT " volatile");
+       }
+       printk(KERN_CONT "\n");
+}
+
+/* Log all controls owned by the handler */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+                                 const char *prefix)
+{
+       struct v4l2_ctrl *ctrl;
+       const char *colon = "";
+       int len;
+
+       if (hdl == NULL)
+               return;
+       if (prefix == NULL)
+               prefix = "";
+       len = strlen(prefix);
+       if (len && prefix[len - 1] != ' ')
+               colon = ": ";
+       mutex_lock(hdl->lock);
+       list_for_each_entry(ctrl, &hdl->ctrls, node)
+               if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+                       log_ctrl(ctrl, prefix, colon);
+       mutex_unlock(hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
+
+/* Call s_ctrl for all controls owned by the handler */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+       struct v4l2_ctrl *ctrl;
+       int ret = 0;
+
+       if (hdl == NULL)
+               return 0;
+       mutex_lock(hdl->lock);
+       list_for_each_entry(ctrl, &hdl->ctrls, node)
+               ctrl->done = false;
+
+       list_for_each_entry(ctrl, &hdl->ctrls, node) {
+               struct v4l2_ctrl *master = ctrl->cluster[0];
+               int i;
+
+               /* Skip if this control was already handled by a cluster. */
+               /* Skip button controls and read-only controls. */
+               if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+                   (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+                       continue;
+
+               for (i = 0; i < master->ncontrols; i++) {
+                       if (master->cluster[i]) {
+                               cur_to_new(master->cluster[i]);
+                               master->cluster[i]->is_new = 1;
+                               master->cluster[i]->done = true;
+                       }
+               }
+               ret = call_op(master, s_ctrl);
+               if (ret)
+                       break;
+       }
+       mutex_unlock(hdl->lock);
+       return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+       u32 id = qc->id & V4L2_CTRL_ID_MASK;
+       struct v4l2_ctrl_ref *ref;
+       struct v4l2_ctrl *ctrl;
+
+       if (hdl == NULL)
+               return -EINVAL;
+
+       mutex_lock(hdl->lock);
+
+       /* Try to find it */
+       ref = find_ref(hdl, id);
+
+       if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+               /* Find the next control with ID > qc->id */
+
+               /* Did we reach the end of the control list? */
+               if (id >= node2id(hdl->ctrl_refs.prev)) {
+                       ref = NULL; /* Yes, so there is no next control */
+               } else if (ref) {
+                       /* We found a control with the given ID, so just get
+                          the next one in the list. */
+                       ref = list_entry(ref->node.next, typeof(*ref), node);
+               } else {
+                       /* No control with the given ID exists, so start
+                          searching for the next largest ID. We know there
+                          is one, otherwise the first 'if' above would have
+                          been true. */
+                       list_for_each_entry(ref, &hdl->ctrl_refs, node)
+                               if (id < ref->ctrl->id)
+                                       break;
+               }
+       }
+       mutex_unlock(hdl->lock);
+       if (!ref)
+               return -EINVAL;
+
+       ctrl = ref->ctrl;
+       memset(qc, 0, sizeof(*qc));
+       if (id >= V4L2_CID_PRIVATE_BASE)
+               qc->id = id;
+       else
+               qc->id = ctrl->id;
+       strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+       qc->minimum = ctrl->minimum;
+       qc->maximum = ctrl->maximum;
+       qc->default_value = ctrl->default_value;
+       if (ctrl->type == V4L2_CTRL_TYPE_MENU
+           || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
+               qc->step = 1;
+       else
+               qc->step = ctrl->step;
+       qc->flags = ctrl->flags;
+       qc->type = ctrl->type;
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_queryctrl);
+
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+       if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL)
+               return -EINVAL;
+       return v4l2_queryctrl(sd->ctrl_handler, qc);
+}
+EXPORT_SYMBOL(v4l2_subdev_queryctrl);
+
+/* Implement VIDIOC_QUERYMENU */
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
+{
+       struct v4l2_ctrl *ctrl;
+       u32 i = qm->index;
+
+       ctrl = v4l2_ctrl_find(hdl, qm->id);
+       if (!ctrl)
+               return -EINVAL;
+
+       qm->reserved = 0;
+       /* Sanity checks */
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_MENU:
+               if (ctrl->qmenu == NULL)
+                       return -EINVAL;
+               break;
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+               if (ctrl->qmenu_int == NULL)
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (i < ctrl->minimum || i > ctrl->maximum)
+               return -EINVAL;
+
+       /* Use mask to see if this menu item should be skipped */
+       if (ctrl->menu_skip_mask & (1 << i))
+               return -EINVAL;
+       /* Empty menu items should also be skipped */
+       if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
+               if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+                       return -EINVAL;
+               strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+       } else {
+               qm->value = ctrl->qmenu_int[i];
+       }
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_querymenu);
+
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+       return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_subdev_querymenu);
+
+
+
+/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
+
+   It is not a fully atomic operation, just best-effort only. After all, if
+   multiple controls have to be set through multiple i2c writes (for example)
+   then some initial writes may succeed while others fail. Thus leaving the
+   system in an inconsistent state. The question is how much effort you are
+   willing to spend on trying to make something atomic that really isn't.
+
+   From the point of view of an application the main requirement is that
+   when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
+   error should be returned without actually affecting any controls.
+
+   If all the values are correct, then it is acceptable to just give up
+   in case of low-level errors.
+
+   It is important though that the application can tell when only a partial
+   configuration was done. The way we do that is through the error_idx field
+   of struct v4l2_ext_controls: if that is equal to the count field then no
+   controls were affected. Otherwise all controls before that index were
+   successful in performing their 'get' or 'set' operation, the control at
+   the given index failed, and you don't know what happened with the controls
+   after the failed one. Since if they were part of a control cluster they
+   could have been successfully processed (if a cluster member was encountered
+   at index < error_idx), they could have failed (if a cluster member was at
+   error_idx), or they may not have been processed yet (if the first cluster
+   member appeared after error_idx).
+
+   It is all fairly theoretical, though. In practice all you can do is to
+   bail out. If error_idx == count, then it is an application bug. If
+   error_idx < count then it is only an application bug if the error code was
+   EBUSY. That usually means that something started streaming just when you
+   tried to set the controls. In all other cases it is a driver/hardware
+   problem and all you can do is to retry or bail out.
+
+   Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
+   never modifies controls the error_idx is just set to whatever control
+   has an invalid value.
+ */
+
+/* Prepare for the extended g/s/try functions.
+   Find the controls in the control array and do some basic checks. */
+static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+                            struct v4l2_ext_controls *cs,
+                            struct v4l2_ctrl_helper *helpers)
+{
+       struct v4l2_ctrl_helper *h;
+       bool have_clusters = false;
+       u32 i;
+
+       for (i = 0, h = helpers; i < cs->count; i++, h++) {
+               struct v4l2_ext_control *c = &cs->controls[i];
+               struct v4l2_ctrl_ref *ref;
+               struct v4l2_ctrl *ctrl;
+               u32 id = c->id & V4L2_CTRL_ID_MASK;
+
+               cs->error_idx = i;
+
+               if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+                       return -EINVAL;
+
+               /* Old-style private controls are not allowed for
+                  extended controls */
+               if (id >= V4L2_CID_PRIVATE_BASE)
+                       return -EINVAL;
+               ref = find_ref_lock(hdl, id);
+               if (ref == NULL)
+                       return -EINVAL;
+               ctrl = ref->ctrl;
+               if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+                       return -EINVAL;
+
+               if (ctrl->cluster[0]->ncontrols > 1)
+                       have_clusters = true;
+               if (ctrl->cluster[0] != ctrl)
+                       ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
+               /* Store the ref to the master control of the cluster */
+               h->mref = ref;
+               h->ctrl = ctrl;
+               /* Initially set next to 0, meaning that there is no other
+                  control in this helper array belonging to the same
+                  cluster */
+               h->next = 0;
+       }
+
+       /* We are done if there were no controls that belong to a multi-
+          control cluster. */
+       if (!have_clusters)
+               return 0;
+
+       /* The code below figures out in O(n) time which controls in the list
+          belong to the same cluster. */
+
+       /* This has to be done with the handler lock taken. */
+       mutex_lock(hdl->lock);
+
+       /* First zero the helper field in the master control references */
+       for (i = 0; i < cs->count; i++)
+               helpers[i].mref->helper = NULL;
+       for (i = 0, h = helpers; i < cs->count; i++, h++) {
+               struct v4l2_ctrl_ref *mref = h->mref;
+
+               /* If the mref->helper is set, then it points to an earlier
+                  helper that belongs to the same cluster. */
+               if (mref->helper) {
+                       /* Set the next field of mref->helper to the current
+                          index: this means that that earlier helper now
+                          points to the next helper in the same cluster. */
+                       mref->helper->next = i;
+                       /* mref should be set only for the first helper in the
+                          cluster, clear the others. */
+                       h->mref = NULL;
+               }
+               /* Point the mref helper to the current helper struct. */
+               mref->helper = h;
+       }
+       mutex_unlock(hdl->lock);
+       return 0;
+}
+
+/* Handles the corner case where cs->count == 0. It checks whether the
+   specified control class exists. If that class ID is 0, then it checks
+   whether there are any controls at all. */
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+{
+       if (ctrl_class == 0)
+               return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+       return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+}
+
+
+
+/* Get extended controls. Allocates the helpers array if needed. */
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+       struct v4l2_ctrl_helper helper[4];
+       struct v4l2_ctrl_helper *helpers = helper;
+       int ret;
+       int i, j;
+
+       cs->error_idx = cs->count;
+       cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+       if (hdl == NULL)
+               return -EINVAL;
+
+       if (cs->count == 0)
+               return class_check(hdl, cs->ctrl_class);
+
+       if (cs->count > ARRAY_SIZE(helper)) {
+               helpers = kmalloc_array(cs->count, sizeof(helper[0]),
+                                       GFP_KERNEL);
+               if (helpers == NULL)
+                       return -ENOMEM;
+       }
+
+       ret = prepare_ext_ctrls(hdl, cs, helpers);
+       cs->error_idx = cs->count;
+
+       for (i = 0; !ret && i < cs->count; i++)
+               if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+                       ret = -EACCES;
+
+       for (i = 0; !ret && i < cs->count; i++) {
+               int (*ctrl_to_user)(struct v4l2_ext_control *c,
+                                   struct v4l2_ctrl *ctrl) = cur_to_user;
+               struct v4l2_ctrl *master;
+
+               if (helpers[i].mref == NULL)
+                       continue;
+
+               master = helpers[i].mref->ctrl;
+               cs->error_idx = i;
+
+               v4l2_ctrl_lock(master);
+
+               /* g_volatile_ctrl will update the new control values */
+               if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
+                       (master->has_volatiles && !is_cur_manual(master))) {
+                       for (j = 0; j < master->ncontrols; j++)
+                               cur_to_new(master->cluster[j]);
+                       ret = call_op(master, g_volatile_ctrl);
+                       ctrl_to_user = new_to_user;
+               }
+               /* If OK, then copy the current (for non-volatile controls)
+                  or the new (for volatile controls) control values to the
+                  caller */
+               if (!ret) {
+                       u32 idx = i;
+
+                       do {
+                               ret = ctrl_to_user(cs->controls + idx,
+                                                  helpers[idx].ctrl);
+                               idx = helpers[idx].next;
+                       } while (!ret && idx);
+               }
+               v4l2_ctrl_unlock(master);
+       }
+
+       if (cs->count > ARRAY_SIZE(helper))
+               kfree(helpers);
+       return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+       return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
+
+/* Helper function to get a single control */
+static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+       struct v4l2_ctrl *master = ctrl->cluster[0];
+       int ret = 0;
+       int i;
+
+       if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+               return -EACCES;
+
+       v4l2_ctrl_lock(master);
+       /* g_volatile_ctrl will update the current control values */
+       if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
+               for (i = 0; i < master->ncontrols; i++)
+                       cur_to_new(master->cluster[i]);
+               ret = call_op(master, g_volatile_ctrl);
+               *val = ctrl->val;
+       } else {
+               *val = ctrl->cur.val;
+       }
+       v4l2_ctrl_unlock(master);
+       return ret;
+}
+
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+       if (ctrl == NULL || !type_is_int(ctrl))
+               return -EINVAL;
+       return get_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_g_ctrl);
+
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+       return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
+
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+       s32 val = 0;
+
+       /* It's a driver bug if this happens. */
+       WARN_ON(!type_is_int(ctrl));
+       get_ctrl(ctrl, &val);
+       return val;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
+
+
+/* Core function that calls try/s_ctrl and ensures that the new value is
+   copied to the current value on a set.
+   Must be called with ctrl->handler->lock held. */
+static int try_or_set_cluster(struct v4l2_fh *fh,
+                             struct v4l2_ctrl *master, bool set)
+{
+       bool update_flag;
+       int ret;
+       int i;
+
+       /* Go through the cluster and either validate the new value or
+          (if no new value was set), copy the current value to the new
+          value, ensuring a consistent view for the control ops when
+          called. */
+       for (i = 0; i < master->ncontrols; i++) {
+               struct v4l2_ctrl *ctrl = master->cluster[i];
+
+               if (ctrl == NULL)
+                       continue;
+
+               if (!ctrl->is_new) {
+                       cur_to_new(ctrl);
+                       continue;
+               }
+               /* Check again: it may have changed since the
+                  previous check in try_or_set_ext_ctrls(). */
+               if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+                       return -EBUSY;
+       }
+
+       ret = call_op(master, try_ctrl);
+
+       /* Don't set if there is no change */
+       if (ret || !set || !cluster_changed(master))
+               return ret;
+       ret = call_op(master, s_ctrl);
+       if (ret)
+               return ret;
+
+       /* If OK, then make the new values permanent. */
+       update_flag = is_cur_manual(master) != is_new_manual(master);
+       for (i = 0; i < master->ncontrols; i++)
+               new_to_cur(fh, master->cluster[i], update_flag && i > 0);
+       return 0;
+}
+
+/* Validate controls. */
+static int validate_ctrls(struct v4l2_ext_controls *cs,
+                         struct v4l2_ctrl_helper *helpers, bool set)
+{
+       unsigned i;
+       int ret = 0;
+
+       cs->error_idx = cs->count;
+       for (i = 0; i < cs->count; i++) {
+               struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+               cs->error_idx = i;
+
+               if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+                       return -EACCES;
+               /* This test is also done in try_set_control_cluster() which
+                  is called in atomic context, so that has the final say,
+                  but it makes sense to do an up-front check as well. Once
+                  an error occurs in try_set_control_cluster() some other
+                  controls may have been set already and we want to do a
+                  best-effort to avoid that. */
+               if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+                       return -EBUSY;
+               ret = validate_new(ctrl, &cs->controls[i]);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+/* Obtain the current volatile values of an autocluster and mark them
+   as new. */
+static void update_from_auto_cluster(struct v4l2_ctrl *master)
+{
+       int i;
+
+       for (i = 0; i < master->ncontrols; i++)
+               cur_to_new(master->cluster[i]);
+       if (!call_op(master, g_volatile_ctrl))
+               for (i = 1; i < master->ncontrols; i++)
+                       if (master->cluster[i])
+                               master->cluster[i]->is_new = 1;
+}
+
+/* Try or try-and-set controls */
+static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+                            struct v4l2_ext_controls *cs,
+                            bool set)
+{
+       struct v4l2_ctrl_helper helper[4];
+       struct v4l2_ctrl_helper *helpers = helper;
+       unsigned i, j;
+       int ret;
+
+       cs->error_idx = cs->count;
+       cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+       if (hdl == NULL)
+               return -EINVAL;
+
+       if (cs->count == 0)
+               return class_check(hdl, cs->ctrl_class);
+
+       if (cs->count > ARRAY_SIZE(helper)) {
+               helpers = kmalloc_array(cs->count, sizeof(helper[0]),
+                                       GFP_KERNEL);
+               if (!helpers)
+                       return -ENOMEM;
+       }
+       ret = prepare_ext_ctrls(hdl, cs, helpers);
+       if (!ret)
+               ret = validate_ctrls(cs, helpers, set);
+       if (ret && set)
+               cs->error_idx = cs->count;
+       for (i = 0; !ret && i < cs->count; i++) {
+               struct v4l2_ctrl *master;
+               u32 idx = i;
+
+               if (helpers[i].mref == NULL)
+                       continue;
+
+               cs->error_idx = i;
+               master = helpers[i].mref->ctrl;
+               v4l2_ctrl_lock(master);
+
+               /* Reset the 'is_new' flags of the cluster */
+               for (j = 0; j < master->ncontrols; j++)
+                       if (master->cluster[j])
+                               master->cluster[j]->is_new = 0;
+
+               /* For volatile autoclusters that are currently in auto mode
+                  we need to discover if it will be set to manual mode.
+                  If so, then we have to copy the current volatile values
+                  first since those will become the new manual values (which
+                  may be overwritten by explicit new values from this set
+                  of controls). */
+               if (master->is_auto && master->has_volatiles &&
+                                               !is_cur_manual(master)) {
+                       /* Pick an initial non-manual value */
+                       s32 new_auto_val = master->manual_mode_value + 1;
+                       u32 tmp_idx = idx;
+
+                       do {
+                               /* Check if the auto control is part of the
+                                  list, and remember the new value. */
+                               if (helpers[tmp_idx].ctrl == master)
+                                       new_auto_val = cs->controls[tmp_idx].value;
+                               tmp_idx = helpers[tmp_idx].next;
+                       } while (tmp_idx);
+                       /* If the new value == the manual value, then copy
+                          the current volatile values. */
+                       if (new_auto_val == master->manual_mode_value)
+                               update_from_auto_cluster(master);
+               }
+
+               /* Copy the new caller-supplied control values.
+                  user_to_new() sets 'is_new' to 1. */
+               do {
+                       ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
+                       idx = helpers[idx].next;
+               } while (!ret && idx);
+
+               if (!ret)
+                       ret = try_or_set_cluster(fh, master, set);
+
+               /* Copy the new values back to userspace. */
+               if (!ret) {
+                       idx = i;
+                       do {
+                               ret = new_to_user(cs->controls + idx,
+                                               helpers[idx].ctrl);
+                               idx = helpers[idx].next;
+                       } while (!ret && idx);
+               }
+               v4l2_ctrl_unlock(master);
+       }
+
+       if (cs->count > ARRAY_SIZE(helper))
+               kfree(helpers);
+       return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(NULL, hdl, cs, false);
+}
+EXPORT_SYMBOL(v4l2_try_ext_ctrls);
+
+int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+                                       struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(fh, hdl, cs, true);
+}
+EXPORT_SYMBOL(v4l2_s_ext_ctrls);
+
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false);
+}
+EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
+
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+       return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
+{
+       struct v4l2_ctrl *master = ctrl->cluster[0];
+       int ret;
+       int i;
+
+       ret = validate_new_int(ctrl, val);
+       if (ret)
+               return ret;
+
+       v4l2_ctrl_lock(ctrl);
+
+       /* Reset the 'is_new' flags of the cluster */
+       for (i = 0; i < master->ncontrols; i++)
+               if (master->cluster[i])
+                       master->cluster[i]->is_new = 0;
+
+       /* For autoclusters with volatiles that are switched from auto to
+          manual mode we have to update the current volatile values since
+          those will become the initial manual values after such a switch. */
+       if (master->is_auto && master->has_volatiles && ctrl == master &&
+           !is_cur_manual(master) && *val == master->manual_mode_value)
+               update_from_auto_cluster(master);
+       ctrl->val = *val;
+       ctrl->is_new = 1;
+       ret = try_or_set_cluster(fh, master, true);
+       *val = ctrl->cur.val;
+       v4l2_ctrl_unlock(ctrl);
+       return ret;
+}
+
+int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+                                       struct v4l2_control *control)
+{
+       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+       if (ctrl == NULL || !type_is_int(ctrl))
+               return -EINVAL;
+
+       if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+               return -EACCES;
+
+       return set_ctrl(fh, ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_s_ctrl);
+
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+       return v4l2_s_ctrl(NULL, sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
+
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+{
+       /* It's a driver bug if this happens. */
+       WARN_ON(!type_is_int(ctrl));
+       return set_ctrl(NULL, ctrl, &val);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
+
+static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
+{
+       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
+
+       if (ctrl == NULL)
+               return -EINVAL;
+
+       v4l2_ctrl_lock(ctrl);
+       list_add_tail(&sev->node, &ctrl->ev_subs);
+       if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+           (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
+               struct v4l2_event ev;
+               u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+
+               if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
+                       changes |= V4L2_EVENT_CTRL_CH_VALUE;
+               fill_event(&ev, ctrl, changes);
+               /* Mark the queue as active, allowing this initial
+                  event to be accepted. */
+               sev->elems = elems;
+               v4l2_event_queue_fh(sev->fh, &ev);
+       }
+       v4l2_ctrl_unlock(ctrl);
+       return 0;
+}
+
+static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev)
+{
+       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
+
+       v4l2_ctrl_lock(ctrl);
+       list_del(&sev->node);
+       v4l2_ctrl_unlock(ctrl);
+}
+
+void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new)
+{
+       u32 old_changes = old->u.ctrl.changes;
+
+       old->u.ctrl = new->u.ctrl;
+       old->u.ctrl.changes |= old_changes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_replace);
+
+void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new)
+{
+       new->u.ctrl.changes |= old->u.ctrl.changes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_merge);
+
+const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = {
+       .add = v4l2_ctrl_add_event,
+       .del = v4l2_ctrl_del_event,
+       .replace = v4l2_ctrl_replace,
+       .merge = v4l2_ctrl_merge,
+};
+EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops);
+
+int v4l2_ctrl_log_status(struct file *file, void *fh)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_fh *vfh = file->private_data;
+
+       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
+               v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
+                       vfd->v4l2_dev->name);
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_log_status);
+
+int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
+                               struct v4l2_event_subscription *sub)
+{
+       if (sub->type == V4L2_EVENT_CTRL)
+               return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
+       return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
+
+unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
+{
+       struct v4l2_fh *fh = file->private_data;
+
+       if (v4l2_event_pending(fh))
+               return POLLPRI;
+       poll_wait(file, &fh->wait, wait);
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_poll);
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
new file mode 100644 (file)
index 0000000..71237f5
--- /dev/null
@@ -0,0 +1,1003 @@
+/*
+ * Video capture interface for Linux version 2
+ *
+ *     A generic video device interface for the LINUX operating system
+ *     using a set of device structures/vectors for low level operations.
+ *
+ *     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.
+ *
+ * Authors:    Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
+ *              Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
+ *
+ * Fixes:      20000516  Claudio Matsuoka <claudio@conectiva.com>
+ *             - Added procfs support
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#define VIDEO_NUM_DEVICES      256
+#define VIDEO_NAME              "video4linux"
+
+/*
+ *     sysfs stuff
+ */
+
+static ssize_t show_index(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+{
+       struct video_device *vdev = to_video_device(cd);
+
+       return sprintf(buf, "%i\n", vdev->index);
+}
+
+static ssize_t show_debug(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+{
+       struct video_device *vdev = to_video_device(cd);
+
+       return sprintf(buf, "%i\n", vdev->debug);
+}
+
+static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
+                  const char *buf, size_t len)
+{
+       struct video_device *vdev = to_video_device(cd);
+       int res = 0;
+       u16 value;
+
+       res = kstrtou16(buf, 0, &value);
+       if (res)
+               return res;
+
+       vdev->debug = value;
+       return len;
+}
+
+static ssize_t show_name(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+{
+       struct video_device *vdev = to_video_device(cd);
+
+       return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
+}
+
+static struct device_attribute video_device_attrs[] = {
+       __ATTR(name, S_IRUGO, show_name, NULL),
+       __ATTR(debug, 0644, show_debug, set_debug),
+       __ATTR(index, S_IRUGO, show_index, NULL),
+       __ATTR_NULL
+};
+
+/*
+ *     Active devices
+ */
+static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static DEFINE_MUTEX(videodev_lock);
+static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
+
+/* Device node utility functions */
+
+/* Note: these utility functions all assume that vfl_type is in the range
+   [0, VFL_TYPE_MAX-1]. */
+
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+/* Return the bitmap corresponding to vfl_type. */
+static inline unsigned long *devnode_bits(int vfl_type)
+{
+       /* Any types not assigned to fixed minor ranges must be mapped to
+          one single bitmap for the purposes of finding a free node number
+          since all those unassigned types use the same minor range. */
+       int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type;
+
+       return devnode_nums[idx];
+}
+#else
+/* Return the bitmap corresponding to vfl_type. */
+static inline unsigned long *devnode_bits(int vfl_type)
+{
+       return devnode_nums[vfl_type];
+}
+#endif
+
+/* Mark device node number vdev->num as used */
+static inline void devnode_set(struct video_device *vdev)
+{
+       set_bit(vdev->num, devnode_bits(vdev->vfl_type));
+}
+
+/* Mark device node number vdev->num as unused */
+static inline void devnode_clear(struct video_device *vdev)
+{
+       clear_bit(vdev->num, devnode_bits(vdev->vfl_type));
+}
+
+/* Try to find a free device node number in the range [from, to> */
+static inline int devnode_find(struct video_device *vdev, int from, int to)
+{
+       return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from);
+}
+
+struct video_device *video_device_alloc(void)
+{
+       return kzalloc(sizeof(struct video_device), GFP_KERNEL);
+}
+EXPORT_SYMBOL(video_device_alloc);
+
+void video_device_release(struct video_device *vdev)
+{
+       kfree(vdev);
+}
+EXPORT_SYMBOL(video_device_release);
+
+void video_device_release_empty(struct video_device *vdev)
+{
+       /* Do nothing */
+       /* Only valid when the video_device struct is a static. */
+}
+EXPORT_SYMBOL(video_device_release_empty);
+
+static inline void video_get(struct video_device *vdev)
+{
+       get_device(&vdev->dev);
+}
+
+static inline void video_put(struct video_device *vdev)
+{
+       put_device(&vdev->dev);
+}
+
+/* Called when the last user of the video device exits. */
+static void v4l2_device_release(struct device *cd)
+{
+       struct video_device *vdev = to_video_device(cd);
+       struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
+
+       mutex_lock(&videodev_lock);
+       if (WARN_ON(video_device[vdev->minor] != vdev)) {
+               /* should not happen */
+               mutex_unlock(&videodev_lock);
+               return;
+       }
+
+       /* Free up this device for reuse */
+       video_device[vdev->minor] = NULL;
+
+       /* Delete the cdev on this minor as well */
+       cdev_del(vdev->cdev);
+       /* Just in case some driver tries to access this from
+          the release() callback. */
+       vdev->cdev = NULL;
+
+       /* Mark device node number as free */
+       devnode_clear(vdev);
+
+       mutex_unlock(&videodev_lock);
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       if (v4l2_dev && v4l2_dev->mdev &&
+           vdev->vfl_type != VFL_TYPE_SUBDEV)
+               media_device_unregister_entity(&vdev->entity);
+#endif
+
+       /* Do not call v4l2_device_put if there is no release callback set.
+        * Drivers that have no v4l2_device release callback might free the
+        * v4l2_dev instance in the video_device release callback below, so we
+        * must perform this check here.
+        *
+        * TODO: In the long run all drivers that use v4l2_device should use the
+        * v4l2_device release callback. This check will then be unnecessary.
+        */
+       if (v4l2_dev && v4l2_dev->release == NULL)
+               v4l2_dev = NULL;
+
+       /* Release video_device and perform other
+          cleanups as needed. */
+       vdev->release(vdev);
+
+       /* Decrease v4l2_device refcount */
+       if (v4l2_dev)
+               v4l2_device_put(v4l2_dev);
+}
+
+static struct class video_class = {
+       .name = VIDEO_NAME,
+       .dev_attrs = video_device_attrs,
+};
+
+struct video_device *video_devdata(struct file *file)
+{
+       return video_device[iminor(file->f_path.dentry->d_inode)];
+}
+EXPORT_SYMBOL(video_devdata);
+
+
+/* Priority handling */
+
+static inline bool prio_is_valid(enum v4l2_priority prio)
+{
+       return prio == V4L2_PRIORITY_BACKGROUND ||
+              prio == V4L2_PRIORITY_INTERACTIVE ||
+              prio == V4L2_PRIORITY_RECORD;
+}
+
+void v4l2_prio_init(struct v4l2_prio_state *global)
+{
+       memset(global, 0, sizeof(*global));
+}
+EXPORT_SYMBOL(v4l2_prio_init);
+
+int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local,
+                    enum v4l2_priority new)
+{
+       if (!prio_is_valid(new))
+               return -EINVAL;
+       if (*local == new)
+               return 0;
+
+       atomic_inc(&global->prios[new]);
+       if (prio_is_valid(*local))
+               atomic_dec(&global->prios[*local]);
+       *local = new;
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_prio_change);
+
+void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local)
+{
+       v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT);
+}
+EXPORT_SYMBOL(v4l2_prio_open);
+
+void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local)
+{
+       if (prio_is_valid(local))
+               atomic_dec(&global->prios[local]);
+}
+EXPORT_SYMBOL(v4l2_prio_close);
+
+enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global)
+{
+       if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0)
+               return V4L2_PRIORITY_RECORD;
+       if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0)
+               return V4L2_PRIORITY_INTERACTIVE;
+       if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0)
+               return V4L2_PRIORITY_BACKGROUND;
+       return V4L2_PRIORITY_UNSET;
+}
+EXPORT_SYMBOL(v4l2_prio_max);
+
+int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local)
+{
+       return (local < v4l2_prio_max(global)) ? -EBUSY : 0;
+}
+EXPORT_SYMBOL(v4l2_prio_check);
+
+
+static ssize_t v4l2_read(struct file *filp, char __user *buf,
+               size_t sz, loff_t *off)
+{
+       struct video_device *vdev = video_devdata(filp);
+       int ret = -ENODEV;
+
+       if (!vdev->fops->read)
+               return -EINVAL;
+       if (video_is_registered(vdev))
+               ret = vdev->fops->read(filp, buf, sz, off);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: read: %zd (%d)\n",
+                       video_device_node_name(vdev), sz, ret);
+       return ret;
+}
+
+static ssize_t v4l2_write(struct file *filp, const char __user *buf,
+               size_t sz, loff_t *off)
+{
+       struct video_device *vdev = video_devdata(filp);
+       int ret = -ENODEV;
+
+       if (!vdev->fops->write)
+               return -EINVAL;
+       if (video_is_registered(vdev))
+               ret = vdev->fops->write(filp, buf, sz, off);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: write: %zd (%d)\n",
+                       video_device_node_name(vdev), sz, ret);
+       return ret;
+}
+
+static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
+{
+       struct video_device *vdev = video_devdata(filp);
+       unsigned int res = POLLERR | POLLHUP;
+
+       if (!vdev->fops->poll)
+               return DEFAULT_POLLMASK;
+       if (video_is_registered(vdev))
+               res = vdev->fops->poll(filp, poll);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: poll: %08x\n",
+                       video_device_node_name(vdev), res);
+       return res;
+}
+
+static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct video_device *vdev = video_devdata(filp);
+       int ret = -ENODEV;
+
+       if (vdev->fops->unlocked_ioctl) {
+               struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
+
+               if (lock && mutex_lock_interruptible(lock))
+                       return -ERESTARTSYS;
+               if (video_is_registered(vdev))
+                       ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
+               if (lock)
+                       mutex_unlock(lock);
+       } else if (vdev->fops->ioctl) {
+               /* This code path is a replacement for the BKL. It is a major
+                * hack but it will have to do for those drivers that are not
+                * yet converted to use unlocked_ioctl.
+                *
+                * There are two options: if the driver implements struct
+                * v4l2_device, then the lock defined there is used to
+                * serialize the ioctls. Otherwise the v4l2 core lock defined
+                * below is used. This lock is really bad since it serializes
+                * completely independent devices.
+                *
+                * Both variants suffer from the same problem: if the driver
+                * sleeps, then it blocks all ioctls since the lock is still
+                * held. This is very common for VIDIOC_DQBUF since that
+                * normally waits for a frame to arrive. As a result any other
+                * ioctl calls will proceed very, very slowly since each call
+                * will have to wait for the VIDIOC_QBUF to finish. Things that
+                * should take 0.01s may now take 10-20 seconds.
+                *
+                * The workaround is to *not* take the lock for VIDIOC_DQBUF.
+                * This actually works OK for videobuf-based drivers, since
+                * videobuf will take its own internal lock.
+                */
+               static DEFINE_MUTEX(v4l2_ioctl_mutex);
+               struct mutex *m = vdev->v4l2_dev ?
+                       &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;
+
+               if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
+                       return -ERESTARTSYS;
+               if (video_is_registered(vdev))
+                       ret = vdev->fops->ioctl(filp, cmd, arg);
+               if (cmd != VIDIOC_DQBUF)
+                       mutex_unlock(m);
+       } else
+               ret = -ENOTTY;
+
+       return ret;
+}
+
+#ifdef CONFIG_MMU
+#define v4l2_get_unmapped_area NULL
+#else
+static unsigned long v4l2_get_unmapped_area(struct file *filp,
+               unsigned long addr, unsigned long len, unsigned long pgoff,
+               unsigned long flags)
+{
+       struct video_device *vdev = video_devdata(filp);
+       int ret;
+
+       if (!vdev->fops->get_unmapped_area)
+               return -ENOSYS;
+       if (!video_is_registered(vdev))
+               return -ENODEV;
+       ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
+                       video_device_node_name(vdev), ret);
+       return ret;
+}
+#endif
+
+static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
+{
+       struct video_device *vdev = video_devdata(filp);
+       int ret = -ENODEV;
+
+       if (!vdev->fops->mmap)
+               return -ENODEV;
+       if (video_is_registered(vdev))
+               ret = vdev->fops->mmap(filp, vm);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: mmap (%d)\n",
+                       video_device_node_name(vdev), ret);
+       return ret;
+}
+
+/* Override for the open function */
+static int v4l2_open(struct inode *inode, struct file *filp)
+{
+       struct video_device *vdev;
+       int ret = 0;
+
+       /* Check if the video device is available */
+       mutex_lock(&videodev_lock);
+       vdev = video_devdata(filp);
+       /* return ENODEV if the video device has already been removed. */
+       if (vdev == NULL || !video_is_registered(vdev)) {
+               mutex_unlock(&videodev_lock);
+               return -ENODEV;
+       }
+       /* and increase the device refcount */
+       video_get(vdev);
+       mutex_unlock(&videodev_lock);
+       if (vdev->fops->open) {
+               if (video_is_registered(vdev))
+                       ret = vdev->fops->open(filp);
+               else
+                       ret = -ENODEV;
+       }
+
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: open (%d)\n",
+                       video_device_node_name(vdev), ret);
+       /* decrease the refcount in case of an error */
+       if (ret)
+               video_put(vdev);
+       return ret;
+}
+
+/* Override for the release function */
+static int v4l2_release(struct inode *inode, struct file *filp)
+{
+       struct video_device *vdev = video_devdata(filp);
+       int ret = 0;
+
+       if (vdev->fops->release)
+               ret = vdev->fops->release(filp);
+       if (vdev->debug)
+               printk(KERN_DEBUG "%s: release\n",
+                       video_device_node_name(vdev));
+
+       /* decrease the refcount unconditionally since the release()
+          return value is ignored. */
+       video_put(vdev);
+       return ret;
+}
+
+static const struct file_operations v4l2_fops = {
+       .owner = THIS_MODULE,
+       .read = v4l2_read,
+       .write = v4l2_write,
+       .open = v4l2_open,
+       .get_unmapped_area = v4l2_get_unmapped_area,
+       .mmap = v4l2_mmap,
+       .unlocked_ioctl = v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = v4l2_compat_ioctl32,
+#endif
+       .release = v4l2_release,
+       .poll = v4l2_poll,
+       .llseek = no_llseek,
+};
+
+/**
+ * get_index - assign stream index number based on parent device
+ * @vdev: video_device to assign index number to, vdev->parent 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.
+ *
+ * This means that we can always obtain a free stream index number since
+ * the worst case scenario is that there are VIDEO_NUM_DEVICES - 1 slots in
+ * use of the video_device array.
+ *
+ * Returns a free index number.
+ */
+static int get_index(struct video_device *vdev)
+{
+       /* This can be static since this function is called with the global
+          videodev_lock held. */
+       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) {
+                       set_bit(video_device[i]->index, used);
+               }
+       }
+
+       return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
+}
+
+#define SET_VALID_IOCTL(ops, cmd, op)                  \
+       if (ops->op)                                    \
+               set_bit(_IOC_NR(cmd), valid_ioctls)
+
+/* This determines which ioctls are actually implemented in the driver.
+   It's a one-time thing which simplifies video_ioctl2 as it can just do
+   a bit test.
+
+   Note that drivers can override this by setting bits to 1 in
+   vdev->valid_ioctls. If an ioctl is marked as 1 when this function is
+   called, then that ioctl will actually be marked as unimplemented.
+
+   It does that by first setting up the local valid_ioctls bitmap, and
+   at the end do a:
+
+   vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls)
+ */
+static void determine_valid_ioctls(struct video_device *vdev)
+{
+       DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
+       const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
+
+       bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE);
+
+       SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
+       if (ops->vidioc_g_priority ||
+                       test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
+               set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
+       if (ops->vidioc_s_priority ||
+                       test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
+               set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
+       if (ops->vidioc_enum_fmt_vid_cap ||
+           ops->vidioc_enum_fmt_vid_out ||
+           ops->vidioc_enum_fmt_vid_cap_mplane ||
+           ops->vidioc_enum_fmt_vid_out_mplane ||
+           ops->vidioc_enum_fmt_vid_overlay ||
+           ops->vidioc_enum_fmt_type_private)
+               set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
+       if (ops->vidioc_g_fmt_vid_cap ||
+           ops->vidioc_g_fmt_vid_out ||
+           ops->vidioc_g_fmt_vid_cap_mplane ||
+           ops->vidioc_g_fmt_vid_out_mplane ||
+           ops->vidioc_g_fmt_vid_overlay ||
+           ops->vidioc_g_fmt_vbi_cap ||
+           ops->vidioc_g_fmt_vid_out_overlay ||
+           ops->vidioc_g_fmt_vbi_out ||
+           ops->vidioc_g_fmt_sliced_vbi_cap ||
+           ops->vidioc_g_fmt_sliced_vbi_out ||
+           ops->vidioc_g_fmt_type_private)
+               set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+       if (ops->vidioc_s_fmt_vid_cap ||
+           ops->vidioc_s_fmt_vid_out ||
+           ops->vidioc_s_fmt_vid_cap_mplane ||
+           ops->vidioc_s_fmt_vid_out_mplane ||
+           ops->vidioc_s_fmt_vid_overlay ||
+           ops->vidioc_s_fmt_vbi_cap ||
+           ops->vidioc_s_fmt_vid_out_overlay ||
+           ops->vidioc_s_fmt_vbi_out ||
+           ops->vidioc_s_fmt_sliced_vbi_cap ||
+           ops->vidioc_s_fmt_sliced_vbi_out ||
+           ops->vidioc_s_fmt_type_private)
+               set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+       if (ops->vidioc_try_fmt_vid_cap ||
+           ops->vidioc_try_fmt_vid_out ||
+           ops->vidioc_try_fmt_vid_cap_mplane ||
+           ops->vidioc_try_fmt_vid_out_mplane ||
+           ops->vidioc_try_fmt_vid_overlay ||
+           ops->vidioc_try_fmt_vbi_cap ||
+           ops->vidioc_try_fmt_vid_out_overlay ||
+           ops->vidioc_try_fmt_vbi_out ||
+           ops->vidioc_try_fmt_sliced_vbi_cap ||
+           ops->vidioc_try_fmt_sliced_vbi_out ||
+           ops->vidioc_try_fmt_type_private)
+               set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+       SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
+       SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
+       SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
+       SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
+       SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
+       SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
+       SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
+       SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
+       SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
+       if (vdev->tvnorms)
+               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_QUERYSTD, vidioc_querystd);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
+       SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
+       SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
+       SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
+       SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
+       /* Note: the control handler can also be passed through the filehandle,
+          and that can't be tested here. If the bit for these control ioctls
+          is set, then the ioctl is valid. But if it is 0, then it can still
+          be valid if the filehandle passed the control handler. */
+       if (vdev->ctrl_handler || ops->vidioc_queryctrl)
+               set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
+       if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls)
+               set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
+       if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls)
+               set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
+       if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
+               set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
+       if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
+               set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
+       if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls)
+               set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
+       if (vdev->ctrl_handler || ops->vidioc_querymenu)
+               set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio);
+       SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio);
+       SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout);
+       SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout);
+       SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
+       SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator);
+       SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator);
+       if (ops->vidioc_g_crop || ops->vidioc_g_selection)
+               set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
+       if (ops->vidioc_s_crop || ops->vidioc_s_selection)
+               set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
+       SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
+       SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
+       if (ops->vidioc_cropcap || ops->vidioc_g_selection)
+               set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
+       SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp);
+       SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp);
+       SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index);
+       SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd);
+       SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd);
+       SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd);
+       SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
+       if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
+                                       (ops->vidioc_g_std || vdev->tvnorms)))
+               set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
+       SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
+       SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
+       SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner);
+       SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
+       SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
+       SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
+       SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register);
+       SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register);
+#endif
+       SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
+       SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets);
+       SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset);
+       SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset);
+       SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset);
+       SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
+       SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
+       SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings);
+       SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings);
+       SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap);
+       /* yes, really vidioc_subscribe_event */
+       SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
+       SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
+       SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
+       SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
+       SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+       if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
+               set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
+       bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
+                       BASE_VIDIOC_PRIVATE);
+}
+
+/**
+ *     __video_register_device - register video4linux devices
+ *     @vdev: video device structure we want to register
+ *     @type: type of device to register
+ *     @nr:   which device node number (0 == /dev/video0, 1 == /dev/video1, ...
+ *             -1 == first free)
+ *     @warn_if_nr_in_use: warn if the desired device node number
+ *            was already in use and another number was chosen instead.
+ *     @owner: module that owns the video device node
+ *
+ *     The registration code assigns minor numbers and device node numbers
+ *     based on the requested type and registers the new device node with
+ *     the kernel.
+ *
+ *     This function assumes that struct video_device was zeroed when it
+ *     was allocated and does not contain any stale date.
+ *
+ *     An error is returned if no free minor or device node number could be
+ *     found, or if the registration of the device node failed.
+ *
+ *     Zero is returned on success.
+ *
+ *     Valid types are
+ *
+ *     %VFL_TYPE_GRABBER - A frame grabber
+ *
+ *     %VFL_TYPE_VBI - Vertical blank data (undecoded)
+ *
+ *     %VFL_TYPE_RADIO - A radio card
+ *
+ *     %VFL_TYPE_SUBDEV - A subdevice
+ */
+int __video_register_device(struct video_device *vdev, int type, int nr,
+               int warn_if_nr_in_use, struct module *owner)
+{
+       int i = 0;
+       int ret;
+       int minor_offset = 0;
+       int minor_cnt = VIDEO_NUM_DEVICES;
+       const char *name_base;
+
+       /* A minor value of -1 marks this video device as never
+          having been registered */
+       vdev->minor = -1;
+
+       /* the release callback MUST be present */
+       if (WARN_ON(!vdev->release))
+               return -EINVAL;
+
+       /* v4l2_fh support */
+       spin_lock_init(&vdev->fh_lock);
+       INIT_LIST_HEAD(&vdev->fh_list);
+
+       /* Part 1: check device type */
+       switch (type) {
+       case VFL_TYPE_GRABBER:
+               name_base = "video";
+               break;
+       case VFL_TYPE_VBI:
+               name_base = "vbi";
+               break;
+       case VFL_TYPE_RADIO:
+               name_base = "radio";
+               break;
+       case VFL_TYPE_SUBDEV:
+               name_base = "v4l-subdev";
+               break;
+       default:
+               printk(KERN_ERR "%s called with unknown type: %d\n",
+                      __func__, type);
+               return -EINVAL;
+       }
+
+       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;
+       }
+
+       /* Part 2: find a free minor, device node number and device index. */
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+       /* Keep the ranges for the first four types for historical
+        * reasons.
+        * Newer devices (not yet in place) should use the range
+        * of 128-191 and just pick the first free minor there
+        * (new style). */
+       switch (type) {
+       case VFL_TYPE_GRABBER:
+               minor_offset = 0;
+               minor_cnt = 64;
+               break;
+       case VFL_TYPE_RADIO:
+               minor_offset = 64;
+               minor_cnt = 64;
+               break;
+       case VFL_TYPE_VBI:
+               minor_offset = 224;
+               minor_cnt = 32;
+               break;
+       default:
+               minor_offset = 128;
+               minor_cnt = 64;
+               break;
+       }
+#endif
+
+       /* Pick a device node number */
+       mutex_lock(&videodev_lock);
+       nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
+       if (nr == minor_cnt)
+               nr = devnode_find(vdev, 0, minor_cnt);
+       if (nr == minor_cnt) {
+               printk(KERN_ERR "could not get a free device node number\n");
+               mutex_unlock(&videodev_lock);
+               return -ENFILE;
+       }
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+       /* 1-on-1 mapping of device node number to minor number */
+       i = nr;
+#else
+       /* The device node number and minor numbers are independent, so
+          we just find the first free minor number. */
+       for (i = 0; i < VIDEO_NUM_DEVICES; i++)
+               if (video_device[i] == NULL)
+                       break;
+       if (i == VIDEO_NUM_DEVICES) {
+               mutex_unlock(&videodev_lock);
+               printk(KERN_ERR "could not get a free minor\n");
+               return -ENFILE;
+       }
+#endif
+       vdev->minor = i + minor_offset;
+       vdev->num = nr;
+       devnode_set(vdev);
+
+       /* Should not happen since we thought this minor was free */
+       WARN_ON(video_device[vdev->minor] != NULL);
+       vdev->index = get_index(vdev);
+       mutex_unlock(&videodev_lock);
+
+       if (vdev->ioctl_ops)
+               determine_valid_ioctls(vdev);
+
+       /* Part 3: Initialize the character device */
+       vdev->cdev = cdev_alloc();
+       if (vdev->cdev == NULL) {
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+       vdev->cdev->ops = &v4l2_fops;
+       vdev->cdev->owner = owner;
+       ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: cdev_add failed\n", __func__);
+               kfree(vdev->cdev);
+               vdev->cdev = NULL;
+               goto cleanup;
+       }
+
+       /* 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;
+       dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+       ret = device_register(&vdev->dev);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: device_register failed\n", __func__);
+               goto cleanup;
+       }
+       /* Register the release callback that will be called when the last
+          reference to the device goes away. */
+       vdev->dev.release = v4l2_device_release;
+
+       if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
+               printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
+                       name_base, nr, video_device_node_name(vdev));
+
+       /* Increase v4l2_device refcount */
+       if (vdev->v4l2_dev)
+               v4l2_device_get(vdev->v4l2_dev);
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       /* Part 5: Register the entity. */
+       if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+           vdev->vfl_type != VFL_TYPE_SUBDEV) {
+               vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+               vdev->entity.name = vdev->name;
+               vdev->entity.info.v4l.major = VIDEO_MAJOR;
+               vdev->entity.info.v4l.minor = vdev->minor;
+               ret = media_device_register_entity(vdev->v4l2_dev->mdev,
+                       &vdev->entity);
+               if (ret < 0)
+                       printk(KERN_WARNING
+                              "%s: media_device_register_entity failed\n",
+                              __func__);
+       }
+#endif
+       /* Part 6: Activate this minor. The char device can now be used. */
+       set_bit(V4L2_FL_REGISTERED, &vdev->flags);
+       mutex_lock(&videodev_lock);
+       video_device[vdev->minor] = vdev;
+       mutex_unlock(&videodev_lock);
+
+       return 0;
+
+cleanup:
+       mutex_lock(&videodev_lock);
+       if (vdev->cdev)
+               cdev_del(vdev->cdev);
+       devnode_clear(vdev);
+       mutex_unlock(&videodev_lock);
+       /* Mark this video device as never having been registered. */
+       vdev->minor = -1;
+       return ret;
+}
+EXPORT_SYMBOL(__video_register_device);
+
+/**
+ *     video_unregister_device - unregister a video4linux device
+ *     @vdev: the device to unregister
+ *
+ *     This unregisters the passed device. Future open calls will
+ *     be met with errors.
+ */
+void video_unregister_device(struct video_device *vdev)
+{
+       /* Check if vdev was ever registered at all */
+       if (!vdev || !video_is_registered(vdev))
+               return;
+
+       mutex_lock(&videodev_lock);
+       /* This must be in a critical section to prevent a race with v4l2_open.
+        * Once this bit has been cleared video_get may never be called again.
+        */
+       clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
+       mutex_unlock(&videodev_lock);
+       device_unregister(&vdev->dev);
+}
+EXPORT_SYMBOL(video_unregister_device);
+
+/*
+ *     Initialise video for linux
+ */
+static int __init videodev_init(void)
+{
+       dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+       int ret;
+
+       printk(KERN_INFO "Linux video capture interface: v2.00\n");
+       ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
+       if (ret < 0) {
+               printk(KERN_WARNING "videodev: unable to get major %d\n",
+                               VIDEO_MAJOR);
+               return ret;
+       }
+
+       ret = class_register(&video_class);
+       if (ret < 0) {
+               unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+               printk(KERN_WARNING "video_dev: class_register failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void __exit videodev_exit(void)
+{
+       dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+
+       class_unregister(&video_class);
+       unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+}
+
+subsys_initcall(videodev_init);
+module_exit(videodev_exit)
+
+MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
new file mode 100644 (file)
index 0000000..1f203b8
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+    V4L2 device support.
+
+    Copyright (C) 2008  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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 <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#if defined(CONFIG_SPI)
+#include <linux/spi/spi.h>
+#endif
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+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);
diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c
new file mode 100644 (file)
index 0000000..ef2a33c
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * v4l2-event.c
+ *
+ * V4L2 events.
+ *
+ * Copyright (C) 2009--2010 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
+{
+       idx += sev->first;
+       return idx >= sev->elems ? idx - sev->elems : idx;
+}
+
+static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
+{
+       struct v4l2_kevent *kev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+
+       if (list_empty(&fh->available)) {
+               spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+               return -ENOENT;
+       }
+
+       WARN_ON(fh->navailable == 0);
+
+       kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
+       list_del(&kev->list);
+       fh->navailable--;
+
+       kev->event.pending = fh->navailable;
+       *event = kev->event;
+       kev->sev->first = sev_pos(kev->sev, 1);
+       kev->sev->in_use--;
+
+       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+       return 0;
+}
+
+int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
+                      int nonblocking)
+{
+       int ret;
+
+       if (nonblocking)
+               return __v4l2_event_dequeue(fh, event);
+
+       /* Release the vdev lock while waiting */
+       if (fh->vdev->lock)
+               mutex_unlock(fh->vdev->lock);
+
+       do {
+               ret = wait_event_interruptible(fh->wait,
+                                              fh->navailable != 0);
+               if (ret < 0)
+                       break;
+
+               ret = __v4l2_event_dequeue(fh, event);
+       } while (ret == -ENOENT);
+
+       if (fh->vdev->lock)
+               mutex_lock(fh->vdev->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
+
+/* Caller must hold fh->vdev->fh_lock! */
+static struct v4l2_subscribed_event *v4l2_event_subscribed(
+               struct v4l2_fh *fh, u32 type, u32 id)
+{
+       struct v4l2_subscribed_event *sev;
+
+       assert_spin_locked(&fh->vdev->fh_lock);
+
+       list_for_each_entry(sev, &fh->subscribed, list)
+               if (sev->type == type && sev->id == id)
+                       return sev;
+
+       return NULL;
+}
+
+static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
+               const struct timespec *ts)
+{
+       struct v4l2_subscribed_event *sev;
+       struct v4l2_kevent *kev;
+       bool copy_payload = true;
+
+       /* Are we subscribed? */
+       sev = v4l2_event_subscribed(fh, ev->type, ev->id);
+       if (sev == NULL)
+               return;
+
+       /*
+        * If the event has been added to the fh->subscribed list, but its
+        * add op has not completed yet elems will be 0, treat this as
+        * not being subscribed.
+        */
+       if (!sev->elems)
+               return;
+
+       /* Increase event sequence number on fh. */
+       fh->sequence++;
+
+       /* Do we have any free events? */
+       if (sev->in_use == sev->elems) {
+               /* no, remove the oldest one */
+               kev = sev->events + sev_pos(sev, 0);
+               list_del(&kev->list);
+               sev->in_use--;
+               sev->first = sev_pos(sev, 1);
+               fh->navailable--;
+               if (sev->elems == 1) {
+                       if (sev->ops && sev->ops->replace) {
+                               sev->ops->replace(&kev->event, ev);
+                               copy_payload = false;
+                       }
+               } else if (sev->ops && sev->ops->merge) {
+                       struct v4l2_kevent *second_oldest =
+                               sev->events + sev_pos(sev, 0);
+                       sev->ops->merge(&kev->event, &second_oldest->event);
+               }
+       }
+
+       /* Take one and fill it. */
+       kev = sev->events + sev_pos(sev, sev->in_use);
+       kev->event.type = ev->type;
+       if (copy_payload)
+               kev->event.u = ev->u;
+       kev->event.id = ev->id;
+       kev->event.timestamp = *ts;
+       kev->event.sequence = fh->sequence;
+       sev->in_use++;
+       list_add_tail(&kev->list, &fh->available);
+
+       fh->navailable++;
+
+       wake_up_all(&fh->wait);
+}
+
+void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
+{
+       struct v4l2_fh *fh;
+       unsigned long flags;
+       struct timespec timestamp;
+
+       ktime_get_ts(&timestamp);
+
+       spin_lock_irqsave(&vdev->fh_lock, flags);
+
+       list_for_each_entry(fh, &vdev->fh_list, list)
+               __v4l2_event_queue_fh(fh, ev, &timestamp);
+
+       spin_unlock_irqrestore(&vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue);
+
+void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
+{
+       unsigned long flags;
+       struct timespec timestamp;
+
+       ktime_get_ts(&timestamp);
+
+       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+       __v4l2_event_queue_fh(fh, ev, &timestamp);
+       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
+
+int v4l2_event_pending(struct v4l2_fh *fh)
+{
+       return fh->navailable;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_pending);
+
+int v4l2_event_subscribe(struct v4l2_fh *fh,
+                        struct v4l2_event_subscription *sub, unsigned elems,
+                        const struct v4l2_subscribed_event_ops *ops)
+{
+       struct v4l2_subscribed_event *sev, *found_ev;
+       unsigned long flags;
+       unsigned i;
+
+       if (sub->type == V4L2_EVENT_ALL)
+               return -EINVAL;
+
+       if (elems < 1)
+               elems = 1;
+
+       sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
+       if (!sev)
+               return -ENOMEM;
+       for (i = 0; i < elems; i++)
+               sev->events[i].sev = sev;
+       sev->type = sub->type;
+       sev->id = sub->id;
+       sev->flags = sub->flags;
+       sev->fh = fh;
+       sev->ops = ops;
+
+       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+       found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
+       if (!found_ev)
+               list_add(&sev->list, &fh->subscribed);
+       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+       if (found_ev) {
+               kfree(sev);
+               return 0; /* Already listening */
+       }
+
+       if (sev->ops && sev->ops->add) {
+               int ret = sev->ops->add(sev, elems);
+               if (ret) {
+                       sev->ops = NULL;
+                       v4l2_event_unsubscribe(fh, sub);
+                       return ret;
+               }
+       }
+
+       /* Mark as ready for use */
+       sev->elems = elems;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
+
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
+{
+       struct v4l2_event_subscription sub;
+       struct v4l2_subscribed_event *sev;
+       unsigned long flags;
+
+       do {
+               sev = NULL;
+
+               spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+               if (!list_empty(&fh->subscribed)) {
+                       sev = list_first_entry(&fh->subscribed,
+                                       struct v4l2_subscribed_event, list);
+                       sub.type = sev->type;
+                       sub.id = sev->id;
+               }
+               spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+               if (sev)
+                       v4l2_event_unsubscribe(fh, &sub);
+       } while (sev);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
+
+int v4l2_event_unsubscribe(struct v4l2_fh *fh,
+                          struct v4l2_event_subscription *sub)
+{
+       struct v4l2_subscribed_event *sev;
+       unsigned long flags;
+       int i;
+
+       if (sub->type == V4L2_EVENT_ALL) {
+               v4l2_event_unsubscribe_all(fh);
+               return 0;
+       }
+
+       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+
+       sev = v4l2_event_subscribed(fh, sub->type, sub->id);
+       if (sev != NULL) {
+               /* Remove any pending events for this subscription */
+               for (i = 0; i < sev->in_use; i++) {
+                       list_del(&sev->events[sev_pos(sev, i)].list);
+                       fh->navailable--;
+               }
+               list_del(&sev->list);
+       }
+
+       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+       if (sev && sev->ops && sev->ops->del)
+               sev->ops->del(sev);
+
+       kfree(sev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
diff --git a/drivers/media/v4l2-core/v4l2-fh.c b/drivers/media/v4l2-core/v4l2-fh.c
new file mode 100644 (file)
index 0000000..9e3fc04
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * v4l2-fh.c
+ *
+ * V4L2 file handles.
+ *
+ * Copyright (C) 2009--2010 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
+{
+       fh->vdev = vdev;
+       /* Inherit from video_device. May be overridden by the driver. */
+       fh->ctrl_handler = vdev->ctrl_handler;
+       INIT_LIST_HEAD(&fh->list);
+       set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
+       fh->prio = V4L2_PRIORITY_UNSET;
+       init_waitqueue_head(&fh->wait);
+       INIT_LIST_HEAD(&fh->available);
+       INIT_LIST_HEAD(&fh->subscribed);
+       fh->sequence = -1;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_init);
+
+void v4l2_fh_add(struct v4l2_fh *fh)
+{
+       unsigned long flags;
+
+       if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags))
+               v4l2_prio_open(fh->vdev->prio, &fh->prio);
+       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+       list_add(&fh->list, &fh->vdev->fh_list);
+       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_add);
+
+int v4l2_fh_open(struct file *filp)
+{
+       struct video_device *vdev = video_devdata(filp);
+       struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+
+       filp->private_data = fh;
+       if (fh == NULL)
+               return -ENOMEM;
+       v4l2_fh_init(fh, vdev);
+       v4l2_fh_add(fh);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_open);
+
+void v4l2_fh_del(struct v4l2_fh *fh)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+       list_del_init(&fh->list);
+       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+       if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags))
+               v4l2_prio_close(fh->vdev->prio, fh->prio);
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_del);
+
+void v4l2_fh_exit(struct v4l2_fh *fh)
+{
+       if (fh->vdev == NULL)
+               return;
+       v4l2_event_unsubscribe_all(fh);
+       fh->vdev = NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_exit);
+
+int v4l2_fh_release(struct file *filp)
+{
+       struct v4l2_fh *fh = filp->private_data;
+
+       if (fh) {
+               v4l2_fh_del(fh);
+               v4l2_fh_exit(fh);
+               kfree(fh);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_release);
+
+int v4l2_fh_is_singular(struct v4l2_fh *fh)
+{
+       unsigned long flags;
+       int is_singular;
+
+       if (fh == NULL || fh->vdev == NULL)
+               return 0;
+       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+       is_singular = list_is_singular(&fh->list);
+       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+       return is_singular;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_is_singular);
diff --git a/drivers/media/v4l2-core/v4l2-int-device.c b/drivers/media/v4l2-core/v4l2-int-device.c
new file mode 100644 (file)
index 0000000..f447349
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * drivers/media/video/v4l2-int-device.c
+ *
+ * V4L2 internal ioctl interface.
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/sort.h>
+#include <linux/string.h>
+#include <linux/module.h>
+
+#include <media/v4l2-int-device.h>
+
+static DEFINE_MUTEX(mutex);
+static LIST_HEAD(int_list);
+
+void v4l2_int_device_try_attach_all(void)
+{
+       struct v4l2_int_device *m, *s;
+
+       list_for_each_entry(m, &int_list, head) {
+               if (m->type != v4l2_int_type_master)
+                       continue;
+
+               list_for_each_entry(s, &int_list, head) {
+                       if (s->type != v4l2_int_type_slave)
+                               continue;
+
+                       /* Slave is connected? */
+                       if (s->u.slave->master)
+                               continue;
+
+                       /* Slave wants to attach to master? */
+                       if (s->u.slave->attach_to[0] != 0
+                           && strncmp(m->name, s->u.slave->attach_to,
+                                      V4L2NAMESIZE))
+                               continue;
+
+                       if (!try_module_get(m->module))
+                               continue;
+
+                       s->u.slave->master = m;
+                       if (m->u.master->attach(s)) {
+                               s->u.slave->master = NULL;
+                               module_put(m->module);
+                               continue;
+                       }
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);
+
+static int ioctl_sort_cmp(const void *a, const void *b)
+{
+       const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b;
+
+       if (d1->num > d2->num)
+               return 1;
+
+       if (d1->num < d2->num)
+               return -1;
+
+       return 0;
+}
+
+int v4l2_int_device_register(struct v4l2_int_device *d)
+{
+       if (d->type == v4l2_int_type_slave)
+               sort(d->u.slave->ioctls, d->u.slave->num_ioctls,
+                    sizeof(struct v4l2_int_ioctl_desc),
+                    &ioctl_sort_cmp, NULL);
+       mutex_lock(&mutex);
+       list_add(&d->head, &int_list);
+       v4l2_int_device_try_attach_all();
+       mutex_unlock(&mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_int_device_register);
+
+void v4l2_int_device_unregister(struct v4l2_int_device *d)
+{
+       mutex_lock(&mutex);
+       list_del(&d->head);
+       if (d->type == v4l2_int_type_slave
+           && d->u.slave->master != NULL) {
+               d->u.slave->master->u.master->detach(d);
+               module_put(d->u.slave->master->module);
+               d->u.slave->master = NULL;
+       }
+       mutex_unlock(&mutex);
+}
+EXPORT_SYMBOL_GPL(v4l2_int_device_unregister);
+
+/* Adapted from search_extable in extable.c. */
+static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,
+                                      v4l2_int_ioctl_func *no_such_ioctl)
+{
+       const struct v4l2_int_ioctl_desc *first = slave->ioctls;
+       const struct v4l2_int_ioctl_desc *last =
+               first + slave->num_ioctls - 1;
+
+       while (first <= last) {
+               const struct v4l2_int_ioctl_desc *mid;
+
+               mid = (last - first) / 2 + first;
+
+               if (mid->num < cmd)
+                       first = mid + 1;
+               else if (mid->num > cmd)
+                       last = mid - 1;
+               else
+                       return mid->func;
+       }
+
+       return no_such_ioctl;
+}
+
+static int no_such_ioctl_0(struct v4l2_int_device *d)
+{
+       return -ENOIOCTLCMD;
+}
+
+int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd)
+{
+       return ((v4l2_int_ioctl_func_0 *)
+               find_ioctl(d->u.slave, cmd,
+                          (v4l2_int_ioctl_func *)no_such_ioctl_0))(d);
+}
+EXPORT_SYMBOL_GPL(v4l2_int_ioctl_0);
+
+static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg)
+{
+       return -ENOIOCTLCMD;
+}
+
+int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)
+{
+       return ((v4l2_int_ioctl_func_1 *)
+               find_ioctl(d->u.slave, cmd,
+                          (v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);
+}
+EXPORT_SYMBOL_GPL(v4l2_int_ioctl_1);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
new file mode 100644 (file)
index 0000000..c3b7b5f
--- /dev/null
@@ -0,0 +1,2324 @@
+/*
+ * Video capture interface for Linux version 2
+ *
+ * A generic framework to process V4L2 ioctl commands.
+ *
+ * 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.
+ *
+ * Authors:    Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
+ *              Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#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
+ * not including, the specified field is cleared. */
+#define CLEAR_AFTER_FIELD(p, field) \
+       memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
+       0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
+
+struct std_descr {
+       v4l2_std_id std;
+       const char *descr;
+};
+
+static const struct std_descr standards[] = {
+       { V4L2_STD_NTSC,        "NTSC"      },
+       { V4L2_STD_NTSC_M,      "NTSC-M"    },
+       { V4L2_STD_NTSC_M_JP,   "NTSC-M-JP" },
+       { V4L2_STD_NTSC_M_KR,   "NTSC-M-KR" },
+       { V4L2_STD_NTSC_443,    "NTSC-443"  },
+       { V4L2_STD_PAL,         "PAL"       },
+       { V4L2_STD_PAL_BG,      "PAL-BG"    },
+       { V4L2_STD_PAL_B,       "PAL-B"     },
+       { V4L2_STD_PAL_B1,      "PAL-B1"    },
+       { V4L2_STD_PAL_G,       "PAL-G"     },
+       { V4L2_STD_PAL_H,       "PAL-H"     },
+       { V4L2_STD_PAL_I,       "PAL-I"     },
+       { V4L2_STD_PAL_DK,      "PAL-DK"    },
+       { V4L2_STD_PAL_D,       "PAL-D"     },
+       { V4L2_STD_PAL_D1,      "PAL-D1"    },
+       { V4L2_STD_PAL_K,       "PAL-K"     },
+       { V4L2_STD_PAL_M,       "PAL-M"     },
+       { V4L2_STD_PAL_N,       "PAL-N"     },
+       { V4L2_STD_PAL_Nc,      "PAL-Nc"    },
+       { V4L2_STD_PAL_60,      "PAL-60"    },
+       { V4L2_STD_SECAM,       "SECAM"     },
+       { V4L2_STD_SECAM_B,     "SECAM-B"   },
+       { V4L2_STD_SECAM_G,     "SECAM-G"   },
+       { V4L2_STD_SECAM_H,     "SECAM-H"   },
+       { V4L2_STD_SECAM_DK,    "SECAM-DK"  },
+       { V4L2_STD_SECAM_D,     "SECAM-D"   },
+       { V4L2_STD_SECAM_K,     "SECAM-K"   },
+       { V4L2_STD_SECAM_K1,    "SECAM-K1"  },
+       { V4L2_STD_SECAM_L,     "SECAM-L"   },
+       { V4L2_STD_SECAM_LC,    "SECAM-Lc"  },
+       { 0,                    "Unknown"   }
+};
+
+/* video4linux standard ID conversion to standard name
+ */
+const char *v4l2_norm_to_name(v4l2_std_id id)
+{
+       u32 myid = id;
+       int i;
+
+       /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle
+          64 bit comparations. So, on that architecture, with some gcc
+          variants, compilation fails. Currently, the max value is 30bit wide.
+        */
+       BUG_ON(myid != id);
+
+       for (i = 0; standards[i].std; i++)
+               if (myid == standards[i].std)
+                       break;
+       return standards[i].descr;
+}
+EXPORT_SYMBOL(v4l2_norm_to_name);
+
+/* Returns frame period for the given standard */
+void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod)
+{
+       if (id & V4L2_STD_525_60) {
+               frameperiod->numerator = 1001;
+               frameperiod->denominator = 30000;
+       } else {
+               frameperiod->numerator = 1;
+               frameperiod->denominator = 25;
+       }
+}
+EXPORT_SYMBOL(v4l2_video_std_frame_period);
+
+/* Fill in the fields of a v4l2_standard structure according to the
+   'id' and 'transmission' parameters.  Returns negative on error.  */
+int v4l2_video_std_construct(struct v4l2_standard *vs,
+                            int id, const char *name)
+{
+       vs->id = id;
+       v4l2_video_std_frame_period(id, &vs->frameperiod);
+       vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625;
+       strlcpy(vs->name, name, sizeof(vs->name));
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_video_std_construct);
+
+/* ----------------------------------------------------------------- */
+/* some arrays for pretty-printing debug messages of enum types      */
+
+const char *v4l2_field_names[] = {
+       [V4L2_FIELD_ANY]        = "any",
+       [V4L2_FIELD_NONE]       = "none",
+       [V4L2_FIELD_TOP]        = "top",
+       [V4L2_FIELD_BOTTOM]     = "bottom",
+       [V4L2_FIELD_INTERLACED] = "interlaced",
+       [V4L2_FIELD_SEQ_TB]     = "seq-tb",
+       [V4L2_FIELD_SEQ_BT]     = "seq-bt",
+       [V4L2_FIELD_ALTERNATE]  = "alternate",
+       [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
+       [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
+};
+EXPORT_SYMBOL(v4l2_field_names);
+
+const char *v4l2_type_names[] = {
+       [V4L2_BUF_TYPE_VIDEO_CAPTURE]      = "vid-cap",
+       [V4L2_BUF_TYPE_VIDEO_OVERLAY]      = "vid-overlay",
+       [V4L2_BUF_TYPE_VIDEO_OUTPUT]       = "vid-out",
+       [V4L2_BUF_TYPE_VBI_CAPTURE]        = "vbi-cap",
+       [V4L2_BUF_TYPE_VBI_OUTPUT]         = "vbi-out",
+       [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
+       [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "sliced-vbi-out",
+       [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay",
+       [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane",
+       [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane",
+};
+EXPORT_SYMBOL(v4l2_type_names);
+
+static const char *v4l2_memory_names[] = {
+       [V4L2_MEMORY_MMAP]    = "mmap",
+       [V4L2_MEMORY_USERPTR] = "userptr",
+       [V4L2_MEMORY_OVERLAY] = "overlay",
+};
+
+#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \
+                          arr[a] : "unknown")
+
+/* ------------------------------------------------------------------ */
+/* debug help functions                                               */
+
+static void v4l_print_querycap(const void *arg, bool write_only)
+{
+       const struct v4l2_capability *p = arg;
+
+       pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
+               "capabilities=0x%08x, device_caps=0x%08x\n",
+               p->driver, p->card, p->bus_info,
+               p->version, p->capabilities, p->device_caps);
+}
+
+static void v4l_print_enuminput(const void *arg, bool write_only)
+{
+       const struct v4l2_input *p = arg;
+
+       pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
+               "std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
+               p->index, p->name, p->type, p->audioset, p->tuner,
+               (unsigned long long)p->std, p->status, p->capabilities);
+}
+
+static void v4l_print_enumoutput(const void *arg, bool write_only)
+{
+       const struct v4l2_output *p = arg;
+
+       pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
+               "modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
+               p->index, p->name, p->type, p->audioset, p->modulator,
+               (unsigned long long)p->std, p->capabilities);
+}
+
+static void v4l_print_audio(const void *arg, bool write_only)
+{
+       const struct v4l2_audio *p = arg;
+
+       if (write_only)
+               pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
+       else
+               pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+                       p->index, p->name, p->capability, p->mode);
+}
+
+static void v4l_print_audioout(const void *arg, bool write_only)
+{
+       const struct v4l2_audioout *p = arg;
+
+       if (write_only)
+               pr_cont("index=%u\n", p->index);
+       else
+               pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+                       p->index, p->name, p->capability, p->mode);
+}
+
+static void v4l_print_fmtdesc(const void *arg, bool write_only)
+{
+       const struct v4l2_fmtdesc *p = arg;
+
+       pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
+               p->index, prt_names(p->type, v4l2_type_names),
+               p->flags, (p->pixelformat & 0xff),
+               (p->pixelformat >>  8) & 0xff,
+               (p->pixelformat >> 16) & 0xff,
+               (p->pixelformat >> 24) & 0xff,
+               p->description);
+}
+
+static void v4l_print_format(const void *arg, bool write_only)
+{
+       const struct v4l2_format *p = arg;
+       const struct v4l2_pix_format *pix;
+       const struct v4l2_pix_format_mplane *mp;
+       const struct v4l2_vbi_format *vbi;
+       const struct v4l2_sliced_vbi_format *sliced;
+       const struct v4l2_window *win;
+       const struct v4l2_clip *clip;
+       unsigned i;
+
+       pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               pix = &p->fmt.pix;
+               pr_cont(", width=%u, height=%u, "
+                       "pixelformat=%c%c%c%c, field=%s, "
+                       "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+                       pix->width, pix->height,
+                       (pix->pixelformat & 0xff),
+                       (pix->pixelformat >>  8) & 0xff,
+                       (pix->pixelformat >> 16) & 0xff,
+                       (pix->pixelformat >> 24) & 0xff,
+                       prt_names(pix->field, v4l2_field_names),
+                       pix->bytesperline, pix->sizeimage,
+                       pix->colorspace);
+               break;
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               mp = &p->fmt.pix_mp;
+               pr_cont(", width=%u, height=%u, "
+                       "format=%c%c%c%c, field=%s, "
+                       "colorspace=%d, num_planes=%u\n",
+                       mp->width, mp->height,
+                       (mp->pixelformat & 0xff),
+                       (mp->pixelformat >>  8) & 0xff,
+                       (mp->pixelformat >> 16) & 0xff,
+                       (mp->pixelformat >> 24) & 0xff,
+                       prt_names(mp->field, v4l2_field_names),
+                       mp->colorspace, mp->num_planes);
+               for (i = 0; i < mp->num_planes; i++)
+                       printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
+                                       mp->plane_fmt[i].bytesperline,
+                                       mp->plane_fmt[i].sizeimage);
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               win = &p->fmt.win;
+               pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, "
+                       "chromakey=0x%08x, bitmap=%p, "
+                       "global_alpha=0x%02x\n",
+                       win->w.width, win->w.height,
+                       win->w.left, win->w.top,
+                       prt_names(win->field, v4l2_field_names),
+                       win->chromakey, win->bitmap, win->global_alpha);
+               clip = win->clips;
+               for (i = 0; i < win->clipcount; i++) {
+                       printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n",
+                                       i, clip->c.width, clip->c.height,
+                                       clip->c.left, clip->c.top);
+                       clip = clip->next;
+               }
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               vbi = &p->fmt.vbi;
+               pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, "
+                       "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n",
+                       vbi->sampling_rate, vbi->offset,
+                       vbi->samples_per_line,
+                       (vbi->sample_format & 0xff),
+                       (vbi->sample_format >>  8) & 0xff,
+                       (vbi->sample_format >> 16) & 0xff,
+                       (vbi->sample_format >> 24) & 0xff,
+                       vbi->start[0], vbi->start[1],
+                       vbi->count[0], vbi->count[1]);
+               break;
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               sliced = &p->fmt.sliced;
+               pr_cont(", service_set=0x%08x, io_size=%d\n",
+                               sliced->service_set, sliced->io_size);
+               for (i = 0; i < 24; i++)
+                       printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+                               sliced->service_lines[0][i],
+                               sliced->service_lines[1][i]);
+               break;
+       case V4L2_BUF_TYPE_PRIVATE:
+               pr_cont("\n");
+               break;
+       }
+}
+
+static void v4l_print_framebuffer(const void *arg, bool write_only)
+{
+       const struct v4l2_framebuffer *p = arg;
+
+       pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, "
+               "height=%u, pixelformat=%c%c%c%c, "
+               "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+                       p->capability, p->flags, p->base,
+                       p->fmt.width, p->fmt.height,
+                       (p->fmt.pixelformat & 0xff),
+                       (p->fmt.pixelformat >>  8) & 0xff,
+                       (p->fmt.pixelformat >> 16) & 0xff,
+                       (p->fmt.pixelformat >> 24) & 0xff,
+                       p->fmt.bytesperline, p->fmt.sizeimage,
+                       p->fmt.colorspace);
+}
+
+static void v4l_print_buftype(const void *arg, bool write_only)
+{
+       pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names));
+}
+
+static void v4l_print_modulator(const void *arg, bool write_only)
+{
+       const struct v4l2_modulator *p = arg;
+
+       if (write_only)
+               pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
+       else
+               pr_cont("index=%u, name=%s, capability=0x%x, "
+                       "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
+                       p->index, p->name, p->capability,
+                       p->rangelow, p->rangehigh, p->txsubchans);
+}
+
+static void v4l_print_tuner(const void *arg, bool write_only)
+{
+       const struct v4l2_tuner *p = arg;
+
+       if (write_only)
+               pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
+       else
+               pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
+                       "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
+                       "rxsubchans=0x%x, audmode=%u\n",
+                       p->index, p->name, p->type,
+                       p->capability, p->rangelow,
+                       p->rangehigh, p->signal, p->afc,
+                       p->rxsubchans, p->audmode);
+}
+
+static void v4l_print_frequency(const void *arg, bool write_only)
+{
+       const struct v4l2_frequency *p = arg;
+
+       pr_cont("tuner=%u, type=%u, frequency=%u\n",
+                               p->tuner, p->type, p->frequency);
+}
+
+static void v4l_print_standard(const void *arg, bool write_only)
+{
+       const struct v4l2_standard *p = arg;
+
+       pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
+               "framelines=%u\n", p->index,
+               (unsigned long long)p->id, p->name,
+               p->frameperiod.numerator,
+               p->frameperiod.denominator,
+               p->framelines);
+}
+
+static void v4l_print_std(const void *arg, bool write_only)
+{
+       pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg);
+}
+
+static void v4l_print_hw_freq_seek(const void *arg, bool write_only)
+{
+       const struct v4l2_hw_freq_seek *p = arg;
+
+       pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
+               p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
+}
+
+static void v4l_print_requestbuffers(const void *arg, bool write_only)
+{
+       const struct v4l2_requestbuffers *p = arg;
+
+       pr_cont("count=%d, type=%s, memory=%s\n",
+               p->count,
+               prt_names(p->type, v4l2_type_names),
+               prt_names(p->memory, v4l2_memory_names));
+}
+
+static void v4l_print_buffer(const void *arg, bool write_only)
+{
+       const struct v4l2_buffer *p = arg;
+       const struct v4l2_timecode *tc = &p->timecode;
+       const struct v4l2_plane *plane;
+       int i;
+
+       pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, "
+               "flags=0x%08x, field=%s, sequence=%d, memory=%s",
+                       p->timestamp.tv_sec / 3600,
+                       (int)(p->timestamp.tv_sec / 60) % 60,
+                       (int)(p->timestamp.tv_sec % 60),
+                       (long)p->timestamp.tv_usec,
+                       p->index,
+                       prt_names(p->type, v4l2_type_names),
+                       p->flags, prt_names(p->field, v4l2_field_names),
+                       p->sequence, prt_names(p->memory, v4l2_memory_names));
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
+               pr_cont("\n");
+               for (i = 0; i < p->length; ++i) {
+                       plane = &p->m.planes[i];
+                       printk(KERN_DEBUG
+                               "plane %d: bytesused=%d, data_offset=0x%08x "
+                               "offset/userptr=0x%lx, length=%d\n",
+                               i, plane->bytesused, plane->data_offset,
+                               plane->m.userptr, plane->length);
+               }
+       } else {
+               pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n",
+                       p->bytesused, p->m.userptr, p->length);
+       }
+
+       printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, "
+               "flags=0x%08x, frames=%d, userbits=0x%08x\n",
+                       tc->hours, tc->minutes, tc->seconds,
+                       tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
+}
+
+static void v4l_print_create_buffers(const void *arg, bool write_only)
+{
+       const struct v4l2_create_buffers *p = arg;
+
+       pr_cont("index=%d, count=%d, memory=%s, ",
+                       p->index, p->count,
+                       prt_names(p->memory, v4l2_memory_names));
+       v4l_print_format(&p->format, write_only);
+}
+
+static void v4l_print_streamparm(const void *arg, bool write_only)
+{
+       const struct v4l2_streamparm *p = arg;
+
+       pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+
+       if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               const struct v4l2_captureparm *c = &p->parm.capture;
+
+               pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, "
+                       "extendedmode=%d, readbuffers=%d\n",
+                       c->capability, c->capturemode,
+                       c->timeperframe.numerator, c->timeperframe.denominator,
+                       c->extendedmode, c->readbuffers);
+       } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+                  p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               const struct v4l2_outputparm *c = &p->parm.output;
+
+               pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, "
+                       "extendedmode=%d, writebuffers=%d\n",
+                       c->capability, c->outputmode,
+                       c->timeperframe.numerator, c->timeperframe.denominator,
+                       c->extendedmode, c->writebuffers);
+       }
+}
+
+static void v4l_print_queryctrl(const void *arg, bool write_only)
+{
+       const struct v4l2_queryctrl *p = arg;
+
+       pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
+               "step=%d, default=%d, flags=0x%08x\n",
+                       p->id, p->type, p->name,
+                       p->minimum, p->maximum,
+                       p->step, p->default_value, p->flags);
+}
+
+static void v4l_print_querymenu(const void *arg, bool write_only)
+{
+       const struct v4l2_querymenu *p = arg;
+
+       pr_cont("id=0x%x, index=%d\n", p->id, p->index);
+}
+
+static void v4l_print_control(const void *arg, bool write_only)
+{
+       const struct v4l2_control *p = arg;
+
+       pr_cont("id=0x%x, value=%d\n", p->id, p->value);
+}
+
+static void v4l_print_ext_controls(const void *arg, bool write_only)
+{
+       const struct v4l2_ext_controls *p = arg;
+       int i;
+
+       pr_cont("class=0x%x, count=%d, error_idx=%d",
+                       p->ctrl_class, p->count, p->error_idx);
+       for (i = 0; i < p->count; i++) {
+               if (p->controls[i].size)
+                       pr_cont(", id/val=0x%x/0x%x",
+                               p->controls[i].id, p->controls[i].value);
+               else
+                       pr_cont(", id/size=0x%x/%u",
+                               p->controls[i].id, p->controls[i].size);
+       }
+       pr_cont("\n");
+}
+
+static void v4l_print_cropcap(const void *arg, bool write_only)
+{
+       const struct v4l2_cropcap *p = arg;
+
+       pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
+               "defrect wxh=%dx%d, x,y=%d,%d\n, "
+               "pixelaspect %d/%d\n",
+               prt_names(p->type, v4l2_type_names),
+               p->bounds.width, p->bounds.height,
+               p->bounds.left, p->bounds.top,
+               p->defrect.width, p->defrect.height,
+               p->defrect.left, p->defrect.top,
+               p->pixelaspect.numerator, p->pixelaspect.denominator);
+}
+
+static void v4l_print_crop(const void *arg, bool write_only)
+{
+       const struct v4l2_crop *p = arg;
+
+       pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
+               prt_names(p->type, v4l2_type_names),
+               p->c.width, p->c.height,
+               p->c.left, p->c.top);
+}
+
+static void v4l_print_selection(const void *arg, bool write_only)
+{
+       const struct v4l2_selection *p = arg;
+
+       pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
+               prt_names(p->type, v4l2_type_names),
+               p->target, p->flags,
+               p->r.width, p->r.height, p->r.left, p->r.top);
+}
+
+static void v4l_print_jpegcompression(const void *arg, bool write_only)
+{
+       const struct v4l2_jpegcompression *p = arg;
+
+       pr_cont("quality=%d, APPn=%d, APP_len=%d, "
+               "COM_len=%d, jpeg_markers=0x%x\n",
+               p->quality, p->APPn, p->APP_len,
+               p->COM_len, p->jpeg_markers);
+}
+
+static void v4l_print_enc_idx(const void *arg, bool write_only)
+{
+       const struct v4l2_enc_idx *p = arg;
+
+       pr_cont("entries=%d, entries_cap=%d\n",
+                       p->entries, p->entries_cap);
+}
+
+static void v4l_print_encoder_cmd(const void *arg, bool write_only)
+{
+       const struct v4l2_encoder_cmd *p = arg;
+
+       pr_cont("cmd=%d, flags=0x%x\n",
+                       p->cmd, p->flags);
+}
+
+static void v4l_print_decoder_cmd(const void *arg, bool write_only)
+{
+       const struct v4l2_decoder_cmd *p = arg;
+
+       pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags);
+
+       if (p->cmd == V4L2_DEC_CMD_START)
+               pr_info("speed=%d, format=%u\n",
+                               p->start.speed, p->start.format);
+       else if (p->cmd == V4L2_DEC_CMD_STOP)
+               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, ", 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_register(const void *arg, bool write_only)
+{
+       const struct v4l2_dbg_register *p = arg;
+
+       pr_cont("type=%u, ", p->match.type);
+       if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+               pr_cont("name=%s, ", p->match.name);
+       else
+               pr_cont("addr=%u, ", p->match.addr);
+       pr_cont("reg=0x%llx, val=0x%llx\n",
+                       p->reg, p->val);
+}
+
+static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_enum_preset *p = arg;
+
+       pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
+                       p->index, p->preset, p->name, p->width, p->height);
+}
+
+static void v4l_print_dv_preset(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_preset *p = arg;
+
+       pr_cont("preset=%u\n", p->preset);
+}
+
+static void v4l_print_dv_timings(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_timings *p = arg;
+
+       switch (p->type) {
+       case V4L2_DV_BT_656_1120:
+               pr_cont("type=bt-656/1120, interlaced=%u, "
+                       "pixelclock=%llu, "
+                       "width=%u, height=%u, polarities=0x%x, "
+                       "hfrontporch=%u, hsync=%u, "
+                       "hbackporch=%u, vfrontporch=%u, "
+                       "vsync=%u, vbackporch=%u, "
+                       "il_vfrontporch=%u, il_vsync=%u, "
+                       "il_vbackporch=%u, standards=0x%x, flags=0x%x\n",
+                               p->bt.interlaced, p->bt.pixelclock,
+                               p->bt.width, p->bt.height,
+                               p->bt.polarities, p->bt.hfrontporch,
+                               p->bt.hsync, p->bt.hbackporch,
+                               p->bt.vfrontporch, p->bt.vsync,
+                               p->bt.vbackporch, p->bt.il_vfrontporch,
+                               p->bt.il_vsync, p->bt.il_vbackporch,
+                               p->bt.standards, p->bt.flags);
+               break;
+       default:
+               pr_cont("type=%d\n", p->type);
+               break;
+       }
+}
+
+static void v4l_print_enum_dv_timings(const void *arg, bool write_only)
+{
+       const struct v4l2_enum_dv_timings *p = arg;
+
+       pr_cont("index=%u, ", p->index);
+       v4l_print_dv_timings(&p->timings, write_only);
+}
+
+static void v4l_print_dv_timings_cap(const void *arg, bool write_only)
+{
+       const struct v4l2_dv_timings_cap *p = arg;
+
+       switch (p->type) {
+       case V4L2_DV_BT_656_1120:
+               pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, "
+                       "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n",
+                       p->bt.min_width, p->bt.max_width,
+                       p->bt.min_height, p->bt.max_height,
+                       p->bt.min_pixelclock, p->bt.max_pixelclock,
+                       p->bt.standards, p->bt.capabilities);
+               break;
+       default:
+               pr_cont("type=%u\n", p->type);
+               break;
+       }
+}
+
+static void v4l_print_frmsizeenum(const void *arg, bool write_only)
+{
+       const struct v4l2_frmsizeenum *p = arg;
+
+       pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u",
+                       p->index,
+                       (p->pixel_format & 0xff),
+                       (p->pixel_format >>  8) & 0xff,
+                       (p->pixel_format >> 16) & 0xff,
+                       (p->pixel_format >> 24) & 0xff,
+                       p->type);
+       switch (p->type) {
+       case V4L2_FRMSIZE_TYPE_DISCRETE:
+               pr_cont(" wxh=%ux%u\n",
+                       p->discrete.width, p->discrete.height);
+               break;
+       case V4L2_FRMSIZE_TYPE_STEPWISE:
+               pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n",
+                               p->stepwise.min_width,  p->stepwise.min_height,
+                               p->stepwise.step_width, p->stepwise.step_height,
+                               p->stepwise.max_width,  p->stepwise.max_height);
+               break;
+       case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+               /* fall through */
+       default:
+               pr_cont("\n");
+               break;
+       }
+}
+
+static void v4l_print_frmivalenum(const void *arg, bool write_only)
+{
+       const struct v4l2_frmivalenum *p = arg;
+
+       pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u",
+                       p->index,
+                       (p->pixel_format & 0xff),
+                       (p->pixel_format >>  8) & 0xff,
+                       (p->pixel_format >> 16) & 0xff,
+                       (p->pixel_format >> 24) & 0xff,
+                       p->width, p->height, p->type);
+       switch (p->type) {
+       case V4L2_FRMIVAL_TYPE_DISCRETE:
+               pr_cont(" fps=%d/%d\n",
+                               p->discrete.numerator,
+                               p->discrete.denominator);
+               break;
+       case V4L2_FRMIVAL_TYPE_STEPWISE:
+               pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n",
+                               p->stepwise.min.numerator,
+                               p->stepwise.min.denominator,
+                               p->stepwise.max.numerator,
+                               p->stepwise.max.denominator,
+                               p->stepwise.step.numerator,
+                               p->stepwise.step.denominator);
+               break;
+       case V4L2_FRMIVAL_TYPE_CONTINUOUS:
+               /* fall through */
+       default:
+               pr_cont("\n");
+               break;
+       }
+}
+
+static void v4l_print_event(const void *arg, bool write_only)
+{
+       const struct v4l2_event *p = arg;
+       const struct v4l2_event_ctrl *c;
+
+       pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, "
+               "timestamp=%lu.%9.9lu\n",
+                       p->type, p->pending, p->sequence, p->id,
+                       p->timestamp.tv_sec, p->timestamp.tv_nsec);
+       switch (p->type) {
+       case V4L2_EVENT_VSYNC:
+               printk(KERN_DEBUG "field=%s\n",
+                       prt_names(p->u.vsync.field, v4l2_field_names));
+               break;
+       case V4L2_EVENT_CTRL:
+               c = &p->u.ctrl;
+               printk(KERN_DEBUG "changes=0x%x, type=%u, ",
+                       c->changes, c->type);
+               if (c->type == V4L2_CTRL_TYPE_INTEGER64)
+                       pr_cont("value64=%lld, ", c->value64);
+               else
+                       pr_cont("value=%d, ", c->value);
+               pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d,"
+                               " default_value=%d\n",
+                       c->flags, c->minimum, c->maximum,
+                       c->step, c->default_value);
+               break;
+       case V4L2_EVENT_FRAME_SYNC:
+               pr_cont("frame_sequence=%u\n",
+                       p->u.frame_sync.frame_sequence);
+               break;
+       }
+}
+
+static void v4l_print_event_subscription(const void *arg, bool write_only)
+{
+       const struct v4l2_event_subscription *p = arg;
+
+       pr_cont("type=0x%x, id=0x%x, flags=0x%x\n",
+                       p->type, p->id, p->flags);
+}
+
+static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only)
+{
+       const struct v4l2_sliced_vbi_cap *p = arg;
+       int i;
+
+       pr_cont("type=%s, service_set=0x%08x\n",
+                       prt_names(p->type, v4l2_type_names), p->service_set);
+       for (i = 0; i < 24; i++)
+               printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+                               p->service_lines[0][i],
+                               p->service_lines[1][i]);
+}
+
+static void v4l_print_freq_band(const void *arg, bool write_only)
+{
+       const struct v4l2_frequency_band *p = arg;
+
+       pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, "
+                       "rangelow=%u, rangehigh=%u, modulation=0x%x\n",
+                       p->tuner, p->type, p->index,
+                       p->capability, p->rangelow,
+                       p->rangehigh, p->modulation);
+}
+
+static void v4l_print_u32(const void *arg, bool write_only)
+{
+       pr_cont("value=%u\n", *(const u32 *)arg);
+}
+
+static void v4l_print_newline(const void *arg, bool write_only)
+{
+       pr_cont("\n");
+}
+
+static void v4l_print_default(const void *arg, bool write_only)
+{
+       pr_cont("driver-specific ioctl\n");
+}
+
+static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
+{
+       __u32 i;
+
+       /* zero the reserved fields */
+       c->reserved[0] = c->reserved[1] = 0;
+       for (i = 0; i < c->count; i++)
+               c->controls[i].reserved2[0] = 0;
+
+       /* V4L2_CID_PRIVATE_BASE cannot be used as control class
+          when using extended controls.
+          Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
+          is it allowed for backwards compatibility.
+        */
+       if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+               return 0;
+       /* Check that all controls are from the same control class. */
+       for (i = 0; i < c->count; i++) {
+               if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
+                       c->error_idx = i;
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
+{
+       if (ops == NULL)
+               return -EINVAL;
+
+       switch (type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (ops->vidioc_g_fmt_vid_cap ||
+                               ops->vidioc_g_fmt_vid_cap_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (ops->vidioc_g_fmt_vid_cap_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (ops->vidioc_g_fmt_vid_overlay)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (ops->vidioc_g_fmt_vid_out ||
+                               ops->vidioc_g_fmt_vid_out_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (ops->vidioc_g_fmt_vid_out_mplane)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (ops->vidioc_g_fmt_vid_out_overlay)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (ops->vidioc_g_fmt_vbi_cap)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (ops->vidioc_g_fmt_vbi_out)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (ops->vidioc_g_fmt_sliced_vbi_cap)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (ops->vidioc_g_fmt_sliced_vbi_out)
+                       return 0;
+               break;
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (ops->vidioc_g_fmt_type_private)
+                       return 0;
+               break;
+       }
+       return -EINVAL;
+}
+
+static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_capability *cap = (struct v4l2_capability *)arg;
+
+       cap->version = LINUX_VERSION_CODE;
+       return ops->vidioc_querycap(file, fh, cap);
+}
+
+static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd;
+       u32 *p = arg;
+
+       if (ops->vidioc_g_priority)
+               return ops->vidioc_g_priority(file, fh, arg);
+       vfd = video_devdata(file);
+       *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
+       return 0;
+}
+
+static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd;
+       struct v4l2_fh *vfh;
+       u32 *p = arg;
+
+       if (ops->vidioc_s_priority)
+               return ops->vidioc_s_priority(file, fh, *p);
+       vfd = video_devdata(file);
+       vfh = file->private_data;
+       return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
+}
+
+static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_input *p = arg;
+
+       /*
+        * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+        * CAP_STD here based on ioctl handler provided by the
+        * driver. If the driver doesn't support these
+        * for a specific input, it must override these flags.
+        */
+       if (ops->vidioc_s_std)
+               p->capabilities |= V4L2_IN_CAP_STD;
+       if (ops->vidioc_s_dv_preset)
+               p->capabilities |= V4L2_IN_CAP_PRESETS;
+       if (ops->vidioc_s_dv_timings)
+               p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
+
+       return ops->vidioc_enum_input(file, fh, p);
+}
+
+static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_output *p = arg;
+
+       /*
+        * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+        * CAP_STD here based on ioctl handler provided by the
+        * driver. If the driver doesn't support these
+        * for a specific output, it must override these flags.
+        */
+       if (ops->vidioc_s_std)
+               p->capabilities |= V4L2_OUT_CAP_STD;
+       if (ops->vidioc_s_dv_preset)
+               p->capabilities |= V4L2_OUT_CAP_PRESETS;
+       if (ops->vidioc_s_dv_timings)
+               p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
+
+       return ops->vidioc_enum_output(file, fh, p);
+}
+
+static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_fmtdesc *p = arg;
+
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
+                       break;
+               return ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
+                       break;
+               return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
+                       break;
+               return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_out))
+                       break;
+               return ops->vidioc_enum_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
+                       break;
+               return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_enum_fmt_type_private))
+                       break;
+               return ops->vidioc_enum_fmt_type_private(file, fh, arg);
+       }
+       return -EINVAL;
+}
+
+static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_format *p = arg;
+
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_g_fmt_vid_cap))
+                       break;
+               return ops->vidioc_g_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane))
+                       break;
+               return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_g_fmt_vid_overlay))
+                       break;
+               return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_g_fmt_vid_out))
+                       break;
+               return ops->vidioc_g_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane))
+                       break;
+               return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay))
+                       break;
+               return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_g_fmt_vbi_cap))
+                       break;
+               return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_g_fmt_vbi_out))
+                       break;
+               return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap))
+                       break;
+               return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out))
+                       break;
+               return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_g_fmt_type_private))
+                       break;
+               return ops->vidioc_g_fmt_type_private(file, fh, arg);
+       }
+       return -EINVAL;
+}
+
+static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_format *p = arg;
+
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_vid_cap))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_s_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_vid_out))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_s_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_vbi_out))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_s_fmt_type_private))
+                       break;
+               return ops->vidioc_s_fmt_type_private(file, fh, arg);
+       }
+       return -EINVAL;
+}
+
+static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_format *p = arg;
+
+       switch (p->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_vid_cap))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_try_fmt_vid_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_vid_out))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix);
+               return ops->vidioc_try_fmt_vid_out(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+               return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.win);
+               return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_vbi_out))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.vbi);
+               return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
+                       break;
+               CLEAR_AFTER_FIELD(p, fmt.sliced);
+               return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_PRIVATE:
+               if (unlikely(!ops->vidioc_try_fmt_type_private))
+                       break;
+               return ops->vidioc_try_fmt_type_private(file, fh, arg);
+       }
+       return -EINVAL;
+}
+
+static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_tuner *p = arg;
+       int err;
+
+       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       err = ops->vidioc_g_tuner(file, fh, p);
+       if (!err)
+               p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+       return err;
+}
+
+static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_tuner *p = arg;
+
+       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       return ops->vidioc_s_tuner(file, fh, p);
+}
+
+static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_modulator *p = arg;
+       int err;
+
+       err = ops->vidioc_g_modulator(file, fh, p);
+       if (!err)
+               p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+       return err;
+}
+
+static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_frequency *p = arg;
+
+       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       return ops->vidioc_g_frequency(file, fh, p);
+}
+
+static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_frequency *p = arg;
+       enum v4l2_tuner_type type;
+
+       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       if (p->type != type)
+               return -EINVAL;
+       return ops->vidioc_s_frequency(file, fh, p);
+}
+
+static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_standard *p = arg;
+       v4l2_std_id id = vfd->tvnorms, curr_id = 0;
+       unsigned int index = p->index, i, j = 0;
+       const char *descr = "";
+
+       /* Return norm array in a canonical way */
+       for (i = 0; i <= index && id; i++) {
+               /* last std value in the standards array is 0, so this
+                  while always ends there since (id & 0) == 0. */
+               while ((id & standards[j].std) != standards[j].std)
+                       j++;
+               curr_id = standards[j].std;
+               descr = standards[j].descr;
+               j++;
+               if (curr_id == 0)
+                       break;
+               if (curr_id != V4L2_STD_PAL &&
+                               curr_id != V4L2_STD_SECAM &&
+                               curr_id != V4L2_STD_NTSC)
+                       id &= ~curr_id;
+       }
+       if (i <= index)
+               return -EINVAL;
+
+       v4l2_video_std_construct(p, curr_id, descr);
+       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 = 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;
+}
+
+static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       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.
+        * This means that tuners, audio and video decoders can join
+        * their efforts to improve the standards detection.
+        */
+       *p = vfd->tvnorms;
+       return ops->vidioc_querystd(file, fh, arg);
+}
+
+static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_hw_freq_seek *p = arg;
+       enum v4l2_tuner_type type;
+
+       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+               V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       if (p->type != type)
+               return -EINVAL;
+       return ops->vidioc_s_hw_freq_seek(file, fh, p);
+}
+
+static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_requestbuffers *p = arg;
+       int ret = check_fmt(ops, p->type);
+
+       if (ret)
+               return ret;
+
+       if (p->type < V4L2_BUF_TYPE_PRIVATE)
+               CLEAR_AFTER_FIELD(p, memory);
+
+       return ops->vidioc_reqbufs(file, fh, p);
+}
+
+static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *p = arg;
+       int ret = check_fmt(ops, p->type);
+
+       return ret ? ret : ops->vidioc_querybuf(file, fh, p);
+}
+
+static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *p = arg;
+       int ret = check_fmt(ops, p->type);
+
+       return ret ? ret : ops->vidioc_qbuf(file, fh, p);
+}
+
+static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *p = arg;
+       int ret = check_fmt(ops, p->type);
+
+       return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
+}
+
+static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_create_buffers *create = arg;
+       int ret = check_fmt(ops, create->format.type);
+
+       return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
+}
+
+static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_buffer *b = arg;
+       int ret = check_fmt(ops, b->type);
+
+       return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
+}
+
+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(ops, p->type);
+
+       if (ret)
+               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 (ops->vidioc_g_std)
+               ret = ops->vidioc_g_std(file, fh, &std);
+       if (ret == 0)
+               v4l2_video_std_frame_period(std,
+                           &p->parm.capture.timeperframe);
+       return ret;
+}
+
+static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_streamparm *p = arg;
+       int ret = check_fmt(ops, p->type);
+
+       return ret ? ret : ops->vidioc_s_parm(file, fh, p);
+}
+
+static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_queryctrl *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_queryctrl(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_queryctrl(vfd->ctrl_handler, p);
+       if (ops->vidioc_queryctrl)
+               return ops->vidioc_queryctrl(file, fh, p);
+       return -ENOTTY;
+}
+
+static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_querymenu *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_querymenu(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_querymenu(vfd->ctrl_handler, p);
+       if (ops->vidioc_querymenu)
+               return ops->vidioc_querymenu(file, fh, p);
+       return -ENOTTY;
+}
+
+static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_control *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+       struct v4l2_ext_controls ctrls;
+       struct v4l2_ext_control ctrl;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_g_ctrl(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_g_ctrl(vfd->ctrl_handler, p);
+       if (ops->vidioc_g_ctrl)
+               return ops->vidioc_g_ctrl(file, fh, p);
+       if (ops->vidioc_g_ext_ctrls == NULL)
+               return -ENOTTY;
+
+       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+       ctrls.count = 1;
+       ctrls.controls = &ctrl;
+       ctrl.id = p->id;
+       ctrl.value = p->value;
+       if (check_ext_ctrls(&ctrls, 1)) {
+               int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
+
+               if (ret == 0)
+                       p->value = ctrl.value;
+               return ret;
+       }
+       return -EINVAL;
+}
+
+static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_control *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+       struct v4l2_ext_controls ctrls;
+       struct v4l2_ext_control ctrl;
+
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
+       if (ops->vidioc_s_ctrl)
+               return ops->vidioc_s_ctrl(file, fh, p);
+       if (ops->vidioc_s_ext_ctrls == NULL)
+               return -ENOTTY;
+
+       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+       ctrls.count = 1;
+       ctrls.controls = &ctrl;
+       ctrl.id = p->id;
+       ctrl.value = p->value;
+       if (check_ext_ctrls(&ctrls, 1))
+               return ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
+       return -EINVAL;
+}
+
+static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_ext_controls *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       p->error_idx = p->count;
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+       if (ops->vidioc_g_ext_ctrls == NULL)
+               return -ENOTTY;
+       return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
+                                       -EINVAL;
+}
+
+static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_ext_controls *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       p->error_idx = p->count;
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
+       if (ops->vidioc_s_ext_ctrls == NULL)
+               return -ENOTTY;
+       return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
+                                       -EINVAL;
+}
+
+static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_ext_controls *p = arg;
+       struct v4l2_fh *vfh =
+               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+       p->error_idx = p->count;
+       if (vfh && vfh->ctrl_handler)
+               return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+       if (vfd->ctrl_handler)
+               return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+       if (ops->vidioc_try_ext_ctrls == NULL)
+               return -ENOTTY;
+       return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
+                                       -EINVAL;
+}
+
+static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_crop *p = arg;
+       struct v4l2_selection s = {
+               .type = p->type,
+       };
+       int ret;
+
+       if (ops->vidioc_g_crop)
+               return ops->vidioc_g_crop(file, fh, p);
+       /* simulate capture crop using selection api */
+
+       /* crop means compose for output devices */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+       else
+               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+       ret = ops->vidioc_g_selection(file, fh, &s);
+
+       /* copying results to old structure on success */
+       if (!ret)
+               p->c = s.r;
+       return ret;
+}
+
+static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_crop *p = arg;
+       struct v4l2_selection s = {
+               .type = p->type,
+               .r = p->c,
+       };
+
+       if (ops->vidioc_s_crop)
+               return ops->vidioc_s_crop(file, fh, p);
+       /* simulate capture crop using selection api */
+
+       /* crop means compose for output devices */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+       else
+               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+       return ops->vidioc_s_selection(file, fh, &s);
+}
+
+static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_cropcap *p = arg;
+       struct v4l2_selection s = { .type = p->type };
+       int ret;
+
+       if (ops->vidioc_cropcap)
+               return ops->vidioc_cropcap(file, fh, p);
+
+       /* obtaining bounds */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
+       else
+               s.target = V4L2_SEL_TGT_CROP_BOUNDS;
+
+       ret = ops->vidioc_g_selection(file, fh, &s);
+       if (ret)
+               return ret;
+       p->bounds = s.r;
+
+       /* obtaining defrect */
+       if (V4L2_TYPE_IS_OUTPUT(p->type))
+               s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
+       else
+               s.target = V4L2_SEL_TGT_CROP_DEFAULT;
+
+       ret = ops->vidioc_g_selection(file, fh, &s);
+       if (ret)
+               return ret;
+       p->defrect = s.r;
+
+       /* setting trivial pixelaspect */
+       p->pixelaspect.numerator = 1;
+       p->pixelaspect.denominator = 1;
+       return 0;
+}
+
+static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       int ret;
+
+       if (vfd->v4l2_dev)
+               pr_info("%s: =================  START STATUS  =================\n",
+                       vfd->v4l2_dev->name);
+       ret = ops->vidioc_log_status(file, fh);
+       if (vfd->v4l2_dev)
+               pr_info("%s: ==================  END STATUS  ==================\n",
+                       vfd->v4l2_dev->name);
+       return ret;
+}
+
+static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       struct v4l2_dbg_register *p = arg;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       return ops->vidioc_g_register(file, fh, p);
+#else
+       return -ENOTTY;
+#endif
+}
+
+static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       struct v4l2_dbg_register *p = arg;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       return ops->vidioc_s_register(file, fh, p);
+#else
+       return -ENOTTY;
+#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;
+       return ops->vidioc_g_chip_ident(file, fh, p);
+}
+
+static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
+}
+
+static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_subscribe_event(fh, arg);
+}
+
+static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       return ops->vidioc_unsubscribe_event(fh, arg);
+}
+
+static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct v4l2_sliced_vbi_cap *p = arg;
+
+       /* Clear up to type, everything after type is zeroed already */
+       memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
+
+       return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
+}
+
+static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       struct v4l2_frequency_band *p = arg;
+       enum v4l2_tuner_type type;
+       int err;
+
+       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+
+       if (type != p->type)
+               return -EINVAL;
+       if (ops->vidioc_enum_freq_bands)
+               return ops->vidioc_enum_freq_bands(file, fh, p);
+       if (ops->vidioc_g_tuner) {
+               struct v4l2_tuner t = {
+                       .index = p->tuner,
+                       .type = type,
+               };
+
+               err = ops->vidioc_g_tuner(file, fh, &t);
+               if (err)
+                       return err;
+               p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+               p->rangelow = t.rangelow;
+               p->rangehigh = t.rangehigh;
+               p->modulation = (type == V4L2_TUNER_RADIO) ?
+                       V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+               return 0;
+       }
+       if (ops->vidioc_g_modulator) {
+               struct v4l2_modulator m = {
+                       .index = p->tuner,
+               };
+
+               if (type != V4L2_TUNER_RADIO)
+                       return -EINVAL;
+               err = ops->vidioc_g_modulator(file, fh, &m);
+               if (err)
+                       return err;
+               p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+               p->rangelow = m.rangelow;
+               p->rangehigh = m.rangehigh;
+               p->modulation = (type == V4L2_TUNER_RADIO) ?
+                       V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+               return 0;
+       }
+       return -ENOTTY;
+}
+
+struct v4l2_ioctl_info {
+       unsigned int ioctl;
+       u32 flags;
+       const char * const name;
+       union {
+               u32 offset;
+               int (*func)(const struct v4l2_ioctl_ops *ops,
+                               struct file *file, void *fh, void *p);
+       } u;
+       void (*debug)(const void *arg, bool write_only);
+};
+
+/* This control needs a priority check */
+#define INFO_FL_PRIO   (1 << 0)
+/* This control can be valid if the filehandle passes a control handler. */
+#define INFO_FL_CTRL   (1 << 1)
+/* This is a standard ioctl, no need for special code */
+#define INFO_FL_STD    (1 << 2)
+/* This is ioctl has its own function */
+#define INFO_FL_FUNC   (1 << 3)
+/* Queuing ioctl */
+#define INFO_FL_QUEUE  (1 << 4)
+/* Zero struct from after the field to the end */
+#define INFO_FL_CLEAR(v4l2_struct, field)                      \
+       ((offsetof(struct v4l2_struct, field) +                 \
+         sizeof(((struct v4l2_struct *)0)->field)) << 16)
+#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
+
+#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags)                        \
+       [_IOC_NR(_ioctl)] = {                                           \
+               .ioctl = _ioctl,                                        \
+               .flags = _flags | INFO_FL_STD,                          \
+               .name = #_ioctl,                                        \
+               .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc),   \
+               .debug = _debug,                                        \
+       }
+
+#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags)                  \
+       [_IOC_NR(_ioctl)] = {                                           \
+               .ioctl = _ioctl,                                        \
+               .flags = _flags | INFO_FL_FUNC,                         \
+               .name = #_ioctl,                                        \
+               .u.func = _func,                                        \
+               .debug = _debug,                                        \
+       }
+
+static struct v4l2_ioctl_info v4l2_ioctls[] = {
+       IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
+       IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
+       IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
+       IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
+       IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
+       IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+       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_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)),
+       IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
+       IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
+       IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
+       IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
+       IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
+       IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
+       IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
+       IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
+       IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
+       IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
+       IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
+       IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
+       IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0),
+       IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
+       IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
+       IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
+       IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
+       IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
+       IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
+       IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
+       IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
+       IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
+       IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+       IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
+       IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+       IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
+       IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
+       IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
+       IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+       IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+       IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
+       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_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
+       IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
+       IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
+       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),
+       IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
+       IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
+       IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
+       IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+       IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
+       IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
+       IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
+       IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
+       IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
+};
+#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+
+bool v4l2_is_known_ioctl(unsigned int cmd)
+{
+       if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+               return false;
+       return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
+}
+
+struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
+{
+       if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+               return vdev->lock;
+       if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
+               return NULL;
+       if (vdev->queue && vdev->queue->lock &&
+                       (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
+               return vdev->queue->lock;
+       return vdev->lock;
+}
+
+/* Common ioctl debug function. This function can be used by
+   external ioctl messages as well as internal V4L ioctl */
+void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
+{
+       const char *dir, *type;
+
+       if (prefix)
+               printk(KERN_DEBUG "%s: ", prefix);
+
+       switch (_IOC_TYPE(cmd)) {
+       case 'd':
+               type = "v4l2_int";
+               break;
+       case 'V':
+               if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
+                       type = "v4l2";
+                       break;
+               }
+               pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
+               return;
+       default:
+               type = "unknown";
+               break;
+       }
+
+       switch (_IOC_DIR(cmd)) {
+       case _IOC_NONE:              dir = "--"; break;
+       case _IOC_READ:              dir = "r-"; break;
+       case _IOC_WRITE:             dir = "-w"; break;
+       case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
+       default:                     dir = "*ERR*"; break;
+       }
+       pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
+               type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
+}
+EXPORT_SYMBOL(v4l_printk_ioctl);
+
+static long __video_do_ioctl(struct file *file,
+               unsigned int cmd, void *arg)
+{
+       struct video_device *vfd = video_devdata(file);
+       const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
+       bool write_only = false;
+       struct v4l2_ioctl_info default_info;
+       const struct v4l2_ioctl_info *info;
+       void *fh = file->private_data;
+       struct v4l2_fh *vfh = NULL;
+       int use_fh_prio = 0;
+       int debug = vfd->debug;
+       long ret = -ENOTTY;
+
+       if (ops == NULL) {
+               pr_warn("%s: has no ioctl_ops.\n",
+                               video_device_node_name(vfd));
+               return ret;
+       }
+
+       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+               vfh = file->private_data;
+               use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+       }
+
+       if (v4l2_is_known_ioctl(cmd)) {
+               info = &v4l2_ioctls[_IOC_NR(cmd)];
+
+               if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
+                   !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
+                       goto done;
+
+               if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
+                       ret = v4l2_prio_check(vfd->prio, vfh->prio);
+                       if (ret)
+                               goto done;
+               }
+       } else {
+               default_info.ioctl = cmd;
+               default_info.flags = 0;
+               default_info.debug = v4l_print_default;
+               info = &default_info;
+       }
+
+       write_only = _IOC_DIR(cmd) == _IOC_WRITE;
+       if (write_only && debug > V4L2_DEBUG_IOCTL) {
+               v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+               pr_cont(": ");
+               info->debug(arg, write_only);
+       }
+       if (info->flags & INFO_FL_STD) {
+               typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
+               const void *p = vfd->ioctl_ops;
+               const vidioc_op *vidioc = p + info->u.offset;
+
+               ret = (*vidioc)(file, fh, arg);
+       } else if (info->flags & INFO_FL_FUNC) {
+               ret = info->u.func(ops, file, fh, arg);
+       } else if (!ops->vidioc_default) {
+               ret = -ENOTTY;
+       } else {
+               ret = ops->vidioc_default(file, fh,
+                       use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
+                       cmd, arg);
+       }
+
+done:
+       if (debug) {
+               if (write_only && debug > V4L2_DEBUG_IOCTL) {
+                       if (ret < 0)
+                               printk(KERN_DEBUG "%s: error %ld\n",
+                                       video_device_node_name(vfd), ret);
+                       return ret;
+               }
+               v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+               if (ret < 0)
+                       pr_cont(": error %ld\n", ret);
+               else if (debug == V4L2_DEBUG_IOCTL)
+                       pr_cont("\n");
+               else if (_IOC_DIR(cmd) == _IOC_NONE)
+                       info->debug(arg, write_only);
+               else {
+                       pr_cont(": ");
+                       info->debug(arg, write_only);
+               }
+       }
+
+       return ret;
+}
+
+static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
+                           void * __user *user_ptr, void ***kernel_ptr)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       case VIDIOC_QUERYBUF:
+       case VIDIOC_QBUF:
+       case VIDIOC_DQBUF: {
+               struct v4l2_buffer *buf = parg;
+
+               if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) {
+                       if (buf->length > VIDEO_MAX_PLANES) {
+                               ret = -EINVAL;
+                               break;
+                       }
+                       *user_ptr = (void __user *)buf->m.planes;
+                       *kernel_ptr = (void *)&buf->m.planes;
+                       *array_size = sizeof(struct v4l2_plane) * buf->length;
+                       ret = 1;
+               }
+               break;
+       }
+
+       case VIDIOC_S_EXT_CTRLS:
+       case VIDIOC_G_EXT_CTRLS:
+       case VIDIOC_TRY_EXT_CTRLS: {
+               struct v4l2_ext_controls *ctrls = parg;
+
+               if (ctrls->count != 0) {
+                       if (ctrls->count > V4L2_CID_MAX_CTRLS) {
+                               ret = -EINVAL;
+                               break;
+                       }
+                       *user_ptr = (void __user *)ctrls->controls;
+                       *kernel_ptr = (void *)&ctrls->controls;
+                       *array_size = sizeof(struct v4l2_ext_control)
+                                   * ctrls->count;
+                       ret = 1;
+               }
+               break;
+       }
+       }
+
+       return ret;
+}
+
+long
+video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+              v4l2_kioctl func)
+{
+       char    sbuf[128];
+       void    *mbuf = NULL;
+       void    *parg = (void *)arg;
+       long    err  = -EINVAL;
+       bool    has_array_args;
+       size_t  array_size = 0;
+       void __user *user_ptr = NULL;
+       void    **kernel_ptr = NULL;
+
+       /*  Copy arguments into temp kernel buffer  */
+       if (_IOC_DIR(cmd) != _IOC_NONE) {
+               if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+                       parg = sbuf;
+               } else {
+                       /* too big to allocate from stack */
+                       mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+                       if (NULL == mbuf)
+                               return -ENOMEM;
+                       parg = mbuf;
+               }
+
+               err = -EFAULT;
+               if (_IOC_DIR(cmd) & _IOC_WRITE) {
+                       unsigned int n = _IOC_SIZE(cmd);
+
+                       /*
+                        * In some cases, only a few fields are used as input,
+                        * i.e. when the app sets "index" and then the driver
+                        * fills in the rest of the structure for the thing
+                        * with that index.  We only need to copy up the first
+                        * non-input field.
+                        */
+                       if (v4l2_is_known_ioctl(cmd)) {
+                               u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
+                               if (flags & INFO_FL_CLEAR_MASK)
+                                       n = (flags & INFO_FL_CLEAR_MASK) >> 16;
+                       }
+
+                       if (copy_from_user(parg, (void __user *)arg, n))
+                               goto out;
+
+                       /* zero out anything we don't copy from userspace */
+                       if (n < _IOC_SIZE(cmd))
+                               memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
+               } else {
+                       /* read-only ioctl */
+                       memset(parg, 0, _IOC_SIZE(cmd));
+               }
+       }
+
+       err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
+       if (err < 0)
+               goto out;
+       has_array_args = err;
+
+       if (has_array_args) {
+               /*
+                * When adding new types of array args, make sure that the
+                * parent argument to ioctl (which contains the pointer to the
+                * array) fits into sbuf (so that mbuf will still remain
+                * unused up to here).
+                */
+               mbuf = kmalloc(array_size, GFP_KERNEL);
+               err = -ENOMEM;
+               if (NULL == mbuf)
+                       goto out_array_args;
+               err = -EFAULT;
+               if (copy_from_user(mbuf, user_ptr, array_size))
+                       goto out_array_args;
+               *kernel_ptr = mbuf;
+       }
+
+       /* Handles IOCTL */
+       err = func(file, cmd, parg);
+       if (err == -ENOIOCTLCMD)
+               err = -ENOTTY;
+
+       if (has_array_args) {
+               *kernel_ptr = user_ptr;
+               if (copy_to_user(user_ptr, mbuf, array_size))
+                       err = -EFAULT;
+               goto out_array_args;
+       }
+       /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid
+          results that must be returned. */
+       if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS)
+               goto out;
+
+out_array_args:
+       /*  Copy results into user buffer  */
+       switch (_IOC_DIR(cmd)) {
+       case _IOC_READ:
+       case (_IOC_WRITE | _IOC_READ):
+               if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+                       err = -EFAULT;
+               break;
+       }
+
+out:
+       kfree(mbuf);
+       return err;
+}
+EXPORT_SYMBOL(video_usercopy);
+
+long video_ioctl2(struct file *file,
+              unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(file, cmd, arg, __video_do_ioctl);
+}
+EXPORT_SYMBOL(video_ioctl2);
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
new file mode 100644 (file)
index 0000000..97b4831
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * Memory-to-memory device framework for Video for Linux 2 and videobuf.
+ *
+ * Helper functions for devices that use videobuf buffers for both their
+ * source and destination.
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/videobuf2-core.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
+MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+#define dprintk(fmt, arg...)                                           \
+       do {                                                            \
+               if (debug)                                              \
+                       printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\
+       } while (0)
+
+
+/* Instance is already queued on the job_queue */
+#define TRANS_QUEUED           (1 << 0)
+/* Instance is currently running in hardware */
+#define TRANS_RUNNING          (1 << 1)
+
+
+/* Offset base for buffers on the destination queue - used to distinguish
+ * between source and destination buffers when mmapping - they receive the same
+ * offsets but for different queues */
+#define DST_QUEUE_OFF_BASE     (1 << 30)
+
+
+/**
+ * struct v4l2_m2m_dev - per-device context
+ * @curr_ctx:          currently running instance
+ * @job_queue:         instances queued to run
+ * @job_spinlock:      protects job_queue
+ * @m2m_ops:           driver callbacks
+ */
+struct v4l2_m2m_dev {
+       struct v4l2_m2m_ctx     *curr_ctx;
+
+       struct list_head        job_queue;
+       spinlock_t              job_spinlock;
+
+       struct v4l2_m2m_ops     *m2m_ops;
+};
+
+static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
+                                               enum v4l2_buf_type type)
+{
+       if (V4L2_TYPE_IS_OUTPUT(type))
+               return &m2m_ctx->out_q_ctx;
+       else
+               return &m2m_ctx->cap_q_ctx;
+}
+
+/**
+ * v4l2_m2m_get_vq() - return vb2_queue for the given type
+ */
+struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
+                                      enum v4l2_buf_type type)
+{
+       struct v4l2_m2m_queue_ctx *q_ctx;
+
+       q_ctx = get_queue_ctx(m2m_ctx, type);
+       if (!q_ctx)
+               return NULL;
+
+       return &q_ctx->q;
+}
+EXPORT_SYMBOL(v4l2_m2m_get_vq);
+
+/**
+ * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers
+ */
+void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
+{
+       struct v4l2_m2m_buffer *b = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+
+       if (list_empty(&q_ctx->rdy_queue)) {
+               spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+               return NULL;
+       }
+
+       b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
+       spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+       return &b->vb;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
+
+/**
+ * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and
+ * return it
+ */
+void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
+{
+       struct v4l2_m2m_buffer *b = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+       if (list_empty(&q_ctx->rdy_queue)) {
+               spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+               return NULL;
+       }
+       b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
+       list_del(&b->list);
+       q_ctx->num_rdy--;
+       spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+
+       return &b->vb;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
+
+/*
+ * Scheduling handlers
+ */
+
+/**
+ * v4l2_m2m_get_curr_priv() - return driver private data for the currently
+ * running instance or NULL if no instance is running
+ */
+void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev)
+{
+       unsigned long flags;
+       void *ret = NULL;
+
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+       if (m2m_dev->curr_ctx)
+               ret = m2m_dev->curr_ctx->priv;
+       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(v4l2_m2m_get_curr_priv);
+
+/**
+ * v4l2_m2m_try_run() - select next job to perform and run it if possible
+ *
+ * Get next transaction (if present) from the waiting jobs list and run it.
+ */
+static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+       if (NULL != m2m_dev->curr_ctx) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+               dprintk("Another instance is running, won't run now\n");
+               return;
+       }
+
+       if (list_empty(&m2m_dev->job_queue)) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+               dprintk("No job pending\n");
+               return;
+       }
+
+       m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next,
+                                  struct v4l2_m2m_ctx, queue);
+       m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING;
+       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+       m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv);
+}
+
+/**
+ * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to
+ * the pending job queue and add it if so.
+ * @m2m_ctx:   m2m context assigned to the instance to be checked
+ *
+ * There are three basic requirements an instance has to meet to be able to run:
+ * 1) at least one source buffer has to be queued,
+ * 2) at least one destination buffer has to be queued,
+ * 3) streaming has to be on.
+ *
+ * There may also be additional, custom requirements. In such case the driver
+ * should supply a custom callback (job_ready in v4l2_m2m_ops) that should
+ * return 1 if the instance is ready.
+ * An example of the above could be an instance that requires more than one
+ * src/dst buffer per transaction.
+ */
+static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
+{
+       struct v4l2_m2m_dev *m2m_dev;
+       unsigned long flags_job, flags;
+
+       m2m_dev = m2m_ctx->m2m_dev;
+       dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
+
+       if (!m2m_ctx->out_q_ctx.q.streaming
+           || !m2m_ctx->cap_q_ctx.q.streaming) {
+               dprintk("Streaming needs to be on for both queues\n");
+               return;
+       }
+
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
+       if (m2m_ctx->job_flags & TRANS_QUEUED) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+               dprintk("On job queue already\n");
+               return;
+       }
+
+       spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+       if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) {
+               spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+               dprintk("No input buffers available\n");
+               return;
+       }
+       if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) {
+               spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+               dprintk("No output buffers available\n");
+               return;
+       }
+       spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+
+       if (m2m_dev->m2m_ops->job_ready
+               && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+               dprintk("Driver not ready\n");
+               return;
+       }
+
+       list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
+       m2m_ctx->job_flags |= TRANS_QUEUED;
+
+       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+
+       v4l2_m2m_try_run(m2m_dev);
+}
+
+/**
+ * v4l2_m2m_job_finish() - inform the framework that a job has been finished
+ * and have it clean up
+ *
+ * Called by a driver to yield back the device after it has finished with it.
+ * Should be called as soon as possible after reaching a state which allows
+ * other instances to take control of the device.
+ *
+ * This function has to be called only after device_run() callback has been
+ * called on the driver. To prevent recursion, it should not be called directly
+ * from the device_run() callback though.
+ */
+void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+                        struct v4l2_m2m_ctx *m2m_ctx)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+       if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+               dprintk("Called by an instance not currently running\n");
+               return;
+       }
+
+       list_del(&m2m_dev->curr_ctx->queue);
+       m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+       wake_up(&m2m_dev->curr_ctx->finished);
+       m2m_dev->curr_ctx = NULL;
+
+       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+       /* This instance might have more buffers ready, but since we do not
+        * allow more than one job on the job_queue per instance, each has
+        * to be scheduled separately after the previous one finishes. */
+       v4l2_m2m_try_schedule(m2m_ctx);
+       v4l2_m2m_try_run(m2m_dev);
+}
+EXPORT_SYMBOL(v4l2_m2m_job_finish);
+
+/**
+ * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
+ */
+int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                    struct v4l2_requestbuffers *reqbufs)
+{
+       struct vb2_queue *vq;
+
+       vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
+       return vb2_reqbufs(vq, reqbufs);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
+
+/**
+ * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer
+ *
+ * See v4l2_m2m_mmap() documentation for details.
+ */
+int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                     struct v4l2_buffer *buf)
+{
+       struct vb2_queue *vq;
+       int ret = 0;
+       unsigned int i;
+
+       vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+       ret = vb2_querybuf(vq, buf);
+
+       /* Adjust MMAP memory offsets for the CAPTURE queue */
+       if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) {
+               if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+                       for (i = 0; i < buf->length; ++i)
+                               buf->m.planes[i].m.mem_offset
+                                       += DST_QUEUE_OFF_BASE;
+               } else {
+                       buf->m.offset += DST_QUEUE_OFF_BASE;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
+
+/**
+ * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on
+ * the type
+ */
+int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                 struct v4l2_buffer *buf)
+{
+       struct vb2_queue *vq;
+       int ret;
+
+       vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+       ret = vb2_qbuf(vq, buf);
+       if (!ret)
+               v4l2_m2m_try_schedule(m2m_ctx);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
+
+/**
+ * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on
+ * the type
+ */
+int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                  struct v4l2_buffer *buf)
+{
+       struct vb2_queue *vq;
+
+       vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+       return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
+
+/**
+ * v4l2_m2m_streamon() - turn on streaming for a video queue
+ */
+int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                     enum v4l2_buf_type type)
+{
+       struct vb2_queue *vq;
+       int ret;
+
+       vq = v4l2_m2m_get_vq(m2m_ctx, type);
+       ret = vb2_streamon(vq, type);
+       if (!ret)
+               v4l2_m2m_try_schedule(m2m_ctx);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
+
+/**
+ * v4l2_m2m_streamoff() - turn off streaming for a video queue
+ */
+int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                      enum v4l2_buf_type type)
+{
+       struct vb2_queue *vq;
+
+       vq = v4l2_m2m_get_vq(m2m_ctx, type);
+       return vb2_streamoff(vq, type);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
+
+/**
+ * v4l2_m2m_poll() - poll replacement, for destination buffers only
+ *
+ * Call from the driver's poll() function. Will poll both queues. If a buffer
+ * is available to dequeue (with dqbuf) from the source queue, this will
+ * indicate that a non-blocking write can be performed, while read will be
+ * returned in case of the destination queue.
+ */
+unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                          struct poll_table_struct *wait)
+{
+       struct video_device *vfd = video_devdata(file);
+       unsigned long req_events = poll_requested_events(wait);
+       struct vb2_queue *src_q, *dst_q;
+       struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+       unsigned int rc = 0;
+       unsigned long flags;
+
+       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+               struct v4l2_fh *fh = file->private_data;
+
+               if (v4l2_event_pending(fh))
+                       rc = POLLPRI;
+               else if (req_events & POLLPRI)
+                       poll_wait(file, &fh->wait, wait);
+               if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM)))
+                       return rc;
+       }
+
+       src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+       dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
+
+       /*
+        * There has to be at least one buffer queued on each queued_list, which
+        * means either in driver already or waiting for driver to claim it
+        * and start processing.
+        */
+       if ((!src_q->streaming || list_empty(&src_q->queued_list))
+               && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
+               rc |= POLLERR;
+               goto end;
+       }
+
+       if (m2m_ctx->m2m_dev->m2m_ops->unlock)
+               m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv);
+
+       poll_wait(file, &src_q->done_wq, wait);
+       poll_wait(file, &dst_q->done_wq, wait);
+
+       if (m2m_ctx->m2m_dev->m2m_ops->lock)
+               m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv);
+
+       spin_lock_irqsave(&src_q->done_lock, flags);
+       if (!list_empty(&src_q->done_list))
+               src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+                                               done_entry);
+       if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
+                       || src_vb->state == VB2_BUF_STATE_ERROR))
+               rc |= POLLOUT | POLLWRNORM;
+       spin_unlock_irqrestore(&src_q->done_lock, flags);
+
+       spin_lock_irqsave(&dst_q->done_lock, flags);
+       if (!list_empty(&dst_q->done_list))
+               dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+                                               done_entry);
+       if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
+                       || dst_vb->state == VB2_BUF_STATE_ERROR))
+               rc |= POLLIN | POLLRDNORM;
+       spin_unlock_irqrestore(&dst_q->done_lock, flags);
+
+end:
+       return rc;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
+
+/**
+ * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer
+ *
+ * Call from driver's mmap() function. Will handle mmap() for both queues
+ * seamlessly for videobuffer, which will receive normal per-queue offsets and
+ * proper videobuf queue pointers. The differentiation is made outside videobuf
+ * by adding a predefined offset to buffers from one of the queues and
+ * subtracting it before passing it back to videobuf. Only drivers (and
+ * thus applications) receive modified offsets.
+ */
+int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                        struct vm_area_struct *vma)
+{
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       struct vb2_queue *vq;
+
+       if (offset < DST_QUEUE_OFF_BASE) {
+               vq = v4l2_m2m_get_src_vq(m2m_ctx);
+       } else {
+               vq = v4l2_m2m_get_dst_vq(m2m_ctx);
+               vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+       }
+
+       return vb2_mmap(vq, vma);
+}
+EXPORT_SYMBOL(v4l2_m2m_mmap);
+
+/**
+ * v4l2_m2m_init() - initialize per-driver m2m data
+ *
+ * Usually called from driver's probe() function.
+ */
+struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops)
+{
+       struct v4l2_m2m_dev *m2m_dev;
+
+       if (!m2m_ops)
+               return ERR_PTR(-EINVAL);
+
+       BUG_ON(!m2m_ops->device_run);
+       BUG_ON(!m2m_ops->job_abort);
+
+       m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
+       if (!m2m_dev)
+               return ERR_PTR(-ENOMEM);
+
+       m2m_dev->curr_ctx = NULL;
+       m2m_dev->m2m_ops = m2m_ops;
+       INIT_LIST_HEAD(&m2m_dev->job_queue);
+       spin_lock_init(&m2m_dev->job_spinlock);
+
+       return m2m_dev;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_init);
+
+/**
+ * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
+ *
+ * Usually called from driver's remove() function.
+ */
+void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
+{
+       kfree(m2m_dev);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_release);
+
+/**
+ * v4l2_m2m_ctx_init() - allocate and initialize a m2m context
+ * @priv - driver's instance private data
+ * @m2m_dev - a previously initialized m2m_dev struct
+ * @vq_init - a callback for queue type-specific initialization function to be
+ * used for initializing videobuf_queues
+ *
+ * Usually called from driver's open() function.
+ */
+struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
+               void *drv_priv,
+               int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq))
+{
+       struct v4l2_m2m_ctx *m2m_ctx;
+       struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx;
+       int ret;
+
+       m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
+       if (!m2m_ctx)
+               return ERR_PTR(-ENOMEM);
+
+       m2m_ctx->priv = drv_priv;
+       m2m_ctx->m2m_dev = m2m_dev;
+       init_waitqueue_head(&m2m_ctx->finished);
+
+       out_q_ctx = &m2m_ctx->out_q_ctx;
+       cap_q_ctx = &m2m_ctx->cap_q_ctx;
+
+       INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
+       INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
+       spin_lock_init(&out_q_ctx->rdy_spinlock);
+       spin_lock_init(&cap_q_ctx->rdy_spinlock);
+
+       INIT_LIST_HEAD(&m2m_ctx->queue);
+
+       ret = queue_init(drv_priv, &out_q_ctx->q, &cap_q_ctx->q);
+
+       if (ret)
+               goto err;
+
+       return m2m_ctx;
+err:
+       kfree(m2m_ctx);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
+
+/**
+ * v4l2_m2m_ctx_release() - release m2m context
+ *
+ * Usually called from driver's release() function.
+ */
+void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
+{
+       struct v4l2_m2m_dev *m2m_dev;
+       unsigned long flags;
+
+       m2m_dev = m2m_ctx->m2m_dev;
+
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+       if (m2m_ctx->job_flags & TRANS_RUNNING) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+               m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
+               dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
+               wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING));
+       } else if (m2m_ctx->job_flags & TRANS_QUEUED) {
+               list_del(&m2m_ctx->queue);
+               m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+               dprintk("m2m_ctx: %p had been on queue and was removed\n",
+                       m2m_ctx);
+       } else {
+               /* Do nothing, was not on queue/running */
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+       }
+
+       vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
+       vb2_queue_release(&m2m_ctx->out_q_ctx.q);
+
+       kfree(m2m_ctx);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release);
+
+/**
+ * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list.
+ *
+ * Call from buf_queue(), videobuf_queue_ops callback.
+ */
+void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb)
+{
+       struct v4l2_m2m_buffer *b = container_of(vb, struct v4l2_m2m_buffer, vb);
+       struct v4l2_m2m_queue_ctx *q_ctx;
+       unsigned long flags;
+
+       q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type);
+       if (!q_ctx)
+               return;
+
+       spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+       list_add_tail(&b->list, &q_ctx->rdy_queue);
+       q_ctx->num_rdy++;
+       spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
+
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
new file mode 100644 (file)
index 0000000..9182f81
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * V4L2 sub-device
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *         Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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.
+ *
+ * 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 <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/export.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
+{
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+       fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL);
+       if (fh->pad == NULL)
+               return -ENOMEM;
+#endif
+       return 0;
+}
+
+static void subdev_fh_free(struct v4l2_subdev_fh *fh)
+{
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+       kfree(fh->pad);
+       fh->pad = NULL;
+#endif
+}
+
+static int subdev_open(struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+       struct v4l2_subdev_fh *subdev_fh;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       struct media_entity *entity = NULL;
+#endif
+       int ret;
+
+       subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL);
+       if (subdev_fh == NULL)
+               return -ENOMEM;
+
+       ret = subdev_fh_init(subdev_fh, sd);
+       if (ret) {
+               kfree(subdev_fh);
+               return ret;
+       }
+
+       v4l2_fh_init(&subdev_fh->vfh, vdev);
+       v4l2_fh_add(&subdev_fh->vfh);
+       file->private_data = &subdev_fh->vfh;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       if (sd->v4l2_dev->mdev) {
+               entity = media_entity_get(&sd->entity);
+               if (!entity) {
+                       ret = -EBUSY;
+                       goto err;
+               }
+       }
+#endif
+
+       if (sd->internal_ops && sd->internal_ops->open) {
+               ret = sd->internal_ops->open(sd, subdev_fh);
+               if (ret < 0)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       if (entity)
+               media_entity_put(entity);
+#endif
+       v4l2_fh_del(&subdev_fh->vfh);
+       v4l2_fh_exit(&subdev_fh->vfh);
+       subdev_fh_free(subdev_fh);
+       kfree(subdev_fh);
+
+       return ret;
+}
+
+static int subdev_close(struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+       struct v4l2_fh *vfh = file->private_data;
+       struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+
+       if (sd->internal_ops && sd->internal_ops->close)
+               sd->internal_ops->close(sd, subdev_fh);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       if (sd->v4l2_dev->mdev)
+               media_entity_put(&sd->entity);
+#endif
+       v4l2_fh_del(vfh);
+       v4l2_fh_exit(vfh);
+       subdev_fh_free(subdev_fh);
+       kfree(subdev_fh);
+       file->private_data = NULL;
+
+       return 0;
+}
+
+static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+       struct v4l2_fh *vfh = file->private_data;
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+       struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+#endif
+
+       switch (cmd) {
+       case VIDIOC_QUERYCTRL:
+               return v4l2_queryctrl(vfh->ctrl_handler, arg);
+
+       case VIDIOC_QUERYMENU:
+               return v4l2_querymenu(vfh->ctrl_handler, arg);
+
+       case VIDIOC_G_CTRL:
+               return v4l2_g_ctrl(vfh->ctrl_handler, arg);
+
+       case VIDIOC_S_CTRL:
+               return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
+
+       case VIDIOC_G_EXT_CTRLS:
+               return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
+
+       case VIDIOC_S_EXT_CTRLS:
+               return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
+
+       case VIDIOC_TRY_EXT_CTRLS:
+               return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
+
+       case VIDIOC_DQEVENT:
+               if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+                       return -ENOIOCTLCMD;
+
+               return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+       case VIDIOC_SUBSCRIBE_EVENT:
+               return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+       case VIDIOC_UNSUBSCRIBE_EVENT:
+               return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       case VIDIOC_DBG_G_REGISTER:
+       {
+               struct v4l2_dbg_register *p = arg;
+
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               return v4l2_subdev_call(sd, core, g_register, p);
+       }
+       case VIDIOC_DBG_S_REGISTER:
+       {
+               struct v4l2_dbg_register *p = arg;
+
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               return v4l2_subdev_call(sd, core, s_register, p);
+       }
+#endif
+
+       case VIDIOC_LOG_STATUS: {
+               int ret;
+
+               pr_info("%s: =================  START STATUS  =================\n",
+                       sd->name);
+               ret = v4l2_subdev_call(sd, core, log_status);
+               pr_info("%s: ==================  END STATUS  ==================\n",
+                       sd->name);
+               return ret;
+       }
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+       case VIDIOC_SUBDEV_G_FMT: {
+               struct v4l2_subdev_format *format = arg;
+
+               if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (format->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format);
+       }
+
+       case VIDIOC_SUBDEV_S_FMT: {
+               struct v4l2_subdev_format *format = arg;
+
+               if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (format->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format);
+       }
+
+       case VIDIOC_SUBDEV_G_CROP: {
+               struct v4l2_subdev_crop *crop = arg;
+               struct v4l2_subdev_selection sel;
+               int rval;
+
+               if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (crop->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
+               if (rval != -ENOIOCTLCMD)
+                       return rval;
+
+               memset(&sel, 0, sizeof(sel));
+               sel.which = crop->which;
+               sel.pad = crop->pad;
+               sel.target = V4L2_SEL_TGT_CROP;
+
+               rval = v4l2_subdev_call(
+                       sd, pad, get_selection, subdev_fh, &sel);
+
+               crop->rect = sel.r;
+
+               return rval;
+       }
+
+       case VIDIOC_SUBDEV_S_CROP: {
+               struct v4l2_subdev_crop *crop = arg;
+               struct v4l2_subdev_selection sel;
+               int rval;
+
+               if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (crop->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
+               if (rval != -ENOIOCTLCMD)
+                       return rval;
+
+               memset(&sel, 0, sizeof(sel));
+               sel.which = crop->which;
+               sel.pad = crop->pad;
+               sel.target = V4L2_SEL_TGT_CROP;
+               sel.r = crop->rect;
+
+               rval = v4l2_subdev_call(
+                       sd, pad, set_selection, subdev_fh, &sel);
+
+               crop->rect = sel.r;
+
+               return rval;
+       }
+
+       case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
+               struct v4l2_subdev_mbus_code_enum *code = arg;
+
+               if (code->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh,
+                                       code);
+       }
+
+       case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
+               struct v4l2_subdev_frame_size_enum *fse = arg;
+
+               if (fse->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
+                                       fse);
+       }
+
+       case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
+               return v4l2_subdev_call(sd, video, g_frame_interval, arg);
+
+       case VIDIOC_SUBDEV_S_FRAME_INTERVAL:
+               return v4l2_subdev_call(sd, video, s_frame_interval, arg);
+
+       case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
+               struct v4l2_subdev_frame_interval_enum *fie = arg;
+
+               if (fie->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
+                                       fie);
+       }
+
+       case VIDIOC_SUBDEV_G_SELECTION: {
+               struct v4l2_subdev_selection *sel = arg;
+
+               if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (sel->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(
+                       sd, pad, get_selection, subdev_fh, sel);
+       }
+
+       case VIDIOC_SUBDEV_S_SELECTION: {
+               struct v4l2_subdev_selection *sel = arg;
+
+               if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (sel->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(
+                       sd, pad, set_selection, subdev_fh, sel);
+       }
+#endif
+       default:
+               return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+       }
+
+       return 0;
+}
+
+static long subdev_ioctl(struct file *file, unsigned int cmd,
+       unsigned long arg)
+{
+       return video_usercopy(file, cmd, arg, subdev_do_ioctl);
+}
+
+static unsigned int subdev_poll(struct file *file, poll_table *wait)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+       struct v4l2_fh *fh = file->private_data;
+
+       if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+               return POLLERR;
+
+       poll_wait(file, &fh->wait, wait);
+
+       if (v4l2_event_pending(fh))
+               return POLLPRI;
+
+       return 0;
+}
+
+const struct v4l2_file_operations v4l2_subdev_fops = {
+       .owner = THIS_MODULE,
+       .open = subdev_open,
+       .unlocked_ioctl = subdev_ioctl,
+       .release = subdev_close,
+       .poll = subdev_poll,
+};
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
+                                     struct media_link *link,
+                                     struct v4l2_subdev_format *source_fmt,
+                                     struct v4l2_subdev_format *sink_fmt)
+{
+       if (source_fmt->format.width != sink_fmt->format.width
+           || source_fmt->format.height != sink_fmt->format.height
+           || source_fmt->format.code != sink_fmt->format.code)
+               return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
+
+static int
+v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+                                    struct v4l2_subdev_format *fmt)
+{
+       switch (media_entity_type(pad->entity)) {
+       case MEDIA_ENT_T_V4L2_SUBDEV:
+               fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+               fmt->pad = pad->index;
+               return v4l2_subdev_call(media_entity_to_v4l2_subdev(
+                                               pad->entity),
+                                       pad, get_fmt, NULL, fmt);
+       default:
+               WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n",
+                    media_entity_type(pad->entity), pad->entity->name);
+               /* Fall through */
+       case MEDIA_ENT_T_DEVNODE_V4L:
+               return -EINVAL;
+       }
+}
+
+int v4l2_subdev_link_validate(struct media_link *link)
+{
+       struct v4l2_subdev *sink;
+       struct v4l2_subdev_format sink_fmt, source_fmt;
+       int rval;
+
+       rval = v4l2_subdev_link_validate_get_format(
+               link->source, &source_fmt);
+       if (rval < 0)
+               return 0;
+
+       rval = v4l2_subdev_link_validate_get_format(
+               link->sink, &sink_fmt);
+       if (rval < 0)
+               return 0;
+
+       sink = media_entity_to_v4l2_subdev(link->sink->entity);
+
+       rval = v4l2_subdev_call(sink, pad, link_validate, link,
+                               &source_fmt, &sink_fmt);
+       if (rval != -ENOIOCTLCMD)
+               return rval;
+
+       return v4l2_subdev_link_validate_default(
+               sink, link, &source_fmt, &sink_fmt);
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
+#endif /* CONFIG_MEDIA_CONTROLLER */
+
+void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
+{
+       INIT_LIST_HEAD(&sd->list);
+       BUG_ON(!ops);
+       sd->ops = ops;
+       sd->v4l2_dev = NULL;
+       sd->flags = 0;
+       sd->name[0] = '\0';
+       sd->grp_id = 0;
+       sd->dev_priv = NULL;
+       sd->host_priv = NULL;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       sd->entity.name = sd->name;
+       sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+#endif
+}
+EXPORT_SYMBOL(v4l2_subdev_init);
diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c
new file mode 100644 (file)
index 0000000..bf7a326
--- /dev/null
@@ -0,0 +1,1189 @@
+/*
+ * generic helper functions for handling video4linux capture buffers
+ *
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * Highly based on video-buf written originally by:
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ * (c) 2006 Ted Walther and John Sokol
+ *
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <media/videobuf-core.h>
+
+#define MAGIC_BUFFER 0x20070728
+#define MAGIC_CHECK(is, should)                                                \
+       do {                                                            \
+               if (unlikely((is) != (should))) {                       \
+                       printk(KERN_ERR                                 \
+                               "magic mismatch: %x (expected %x)\n",   \
+                                       is, should);                    \
+                       BUG();                                          \
+               }                                                       \
+       } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("helper module to manage video4linux buffers");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, fmt, arg...)                                    \
+       do {                                                            \
+               if (debug >= level)                                     \
+                       printk(KERN_DEBUG "vbuf: " fmt, ## arg);        \
+       } while (0)
+
+/* --------------------------------------------------------------------- */
+
+#define CALL(q, f, arg...)                                             \
+       ((q->int_ops->f) ? q->int_ops->f(arg) : 0)
+
+struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q)
+{
+       struct videobuf_buffer *vb;
+
+       BUG_ON(q->msize < sizeof(*vb));
+
+       if (!q->int_ops || !q->int_ops->alloc_vb) {
+               printk(KERN_ERR "No specific ops defined!\n");
+               BUG();
+       }
+
+       vb = q->int_ops->alloc_vb(q->msize);
+       if (NULL != vb) {
+               init_waitqueue_head(&vb->done);
+               vb->magic = MAGIC_BUFFER;
+       }
+
+       return vb;
+}
+EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
+
+static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       unsigned long flags;
+       bool rc;
+
+       spin_lock_irqsave(q->irqlock, flags);
+       rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED;
+       spin_unlock_irqrestore(q->irqlock, flags);
+       return rc;
+};
+
+int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb,
+               int non_blocking, int intr)
+{
+       bool is_ext_locked;
+       int ret = 0;
+
+       MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+
+       if (non_blocking) {
+               if (is_state_active_or_queued(q, vb))
+                       return 0;
+               return -EAGAIN;
+       }
+
+       is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock);
+
+       /* Release vdev lock to prevent this wait from blocking outside access to
+          the device. */
+       if (is_ext_locked)
+               mutex_unlock(q->ext_lock);
+       if (intr)
+               ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb));
+       else
+               wait_event(vb->done, is_state_active_or_queued(q, vb));
+       /* Relock */
+       if (is_ext_locked)
+               mutex_lock(q->ext_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_waiton);
+
+int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
+                   struct v4l2_framebuffer *fbuf)
+{
+       MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       return CALL(q, iolock, q, vb, fbuf);
+}
+EXPORT_SYMBOL_GPL(videobuf_iolock);
+
+void *videobuf_queue_to_vaddr(struct videobuf_queue *q,
+                             struct videobuf_buffer *buf)
+{
+       if (q->int_ops->vaddr)
+               return q->int_ops->vaddr(buf);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr);
+
+/* --------------------------------------------------------------------- */
+
+
+void videobuf_queue_core_init(struct videobuf_queue *q,
+                        const struct videobuf_queue_ops *ops,
+                        struct device *dev,
+                        spinlock_t *irqlock,
+                        enum v4l2_buf_type type,
+                        enum v4l2_field field,
+                        unsigned int msize,
+                        void *priv,
+                        struct videobuf_qtype_ops *int_ops,
+                        struct mutex *ext_lock)
+{
+       BUG_ON(!q);
+       memset(q, 0, sizeof(*q));
+       q->irqlock   = irqlock;
+       q->ext_lock  = ext_lock;
+       q->dev       = dev;
+       q->type      = type;
+       q->field     = field;
+       q->msize     = msize;
+       q->ops       = ops;
+       q->priv_data = priv;
+       q->int_ops   = int_ops;
+
+       /* All buffer operations are mandatory */
+       BUG_ON(!q->ops->buf_setup);
+       BUG_ON(!q->ops->buf_prepare);
+       BUG_ON(!q->ops->buf_queue);
+       BUG_ON(!q->ops->buf_release);
+
+       /* Lock is mandatory for queue_cancel to work */
+       BUG_ON(!irqlock);
+
+       /* Having implementations for abstract methods are mandatory */
+       BUG_ON(!q->int_ops);
+
+       mutex_init(&q->vb_lock);
+       init_waitqueue_head(&q->wait);
+       INIT_LIST_HEAD(&q->stream);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
+
+/* Locking: Only usage in bttv unsafe find way to remove */
+int videobuf_queue_is_busy(struct videobuf_queue *q)
+{
+       int i;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       if (q->streaming) {
+               dprintk(1, "busy: streaming active\n");
+               return 1;
+       }
+       if (q->reading) {
+               dprintk(1, "busy: pending read #1\n");
+               return 1;
+       }
+       if (q->read_buf) {
+               dprintk(1, "busy: pending read #2\n");
+               return 1;
+       }
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               if (q->bufs[i]->map) {
+                       dprintk(1, "busy: buffer #%d mapped\n", i);
+                       return 1;
+               }
+               if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
+                       dprintk(1, "busy: buffer #%d queued\n", i);
+                       return 1;
+               }
+               if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {
+                       dprintk(1, "busy: buffer #%d avtive\n", i);
+                       return 1;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);
+
+/**
+ * __videobuf_free() - free all the buffers and their control structures
+ *
+ * This function can only be called if streaming/reading is off, i.e. no buffers
+ * are under control of the driver.
+ */
+/* Locking: Caller holds q->vb_lock */
+static int __videobuf_free(struct videobuf_queue *q)
+{
+       int i;
+
+       dprintk(1, "%s\n", __func__);
+       if (!q)
+               return 0;
+
+       if (q->streaming || q->reading) {
+               dprintk(1, "Cannot free buffers when streaming or reading\n");
+               return -EBUSY;
+       }
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       for (i = 0; i < VIDEO_MAX_FRAME; i++)
+               if (q->bufs[i] && q->bufs[i]->map) {
+                       dprintk(1, "Cannot free mmapped buffers\n");
+                       return -EBUSY;
+               }
+
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               q->ops->buf_release(q, q->bufs[i]);
+               kfree(q->bufs[i]);
+               q->bufs[i] = NULL;
+       }
+
+       return 0;
+}
+
+/* Locking: Caller holds q->vb_lock */
+void videobuf_queue_cancel(struct videobuf_queue *q)
+{
+       unsigned long flags = 0;
+       int i;
+
+       q->streaming = 0;
+       q->reading  = 0;
+       wake_up_interruptible_sync(&q->wait);
+
+       /* remove queued buffers from list */
+       spin_lock_irqsave(q->irqlock, flags);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
+                       list_del(&q->bufs[i]->queue);
+                       q->bufs[i]->state = VIDEOBUF_ERROR;
+                       wake_up_all(&q->bufs[i]->done);
+               }
+       }
+       spin_unlock_irqrestore(q->irqlock, flags);
+
+       /* free all buffers + clear queue */
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               q->ops->buf_release(q, q->bufs[i]);
+       }
+       INIT_LIST_HEAD(&q->stream);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
+
+/* --------------------------------------------------------------------- */
+
+/* Locking: Caller holds q->vb_lock */
+enum v4l2_field videobuf_next_field(struct videobuf_queue *q)
+{
+       enum v4l2_field field = q->field;
+
+       BUG_ON(V4L2_FIELD_ANY == field);
+
+       if (V4L2_FIELD_ALTERNATE == field) {
+               if (V4L2_FIELD_TOP == q->last) {
+                       field   = V4L2_FIELD_BOTTOM;
+                       q->last = V4L2_FIELD_BOTTOM;
+               } else {
+                       field   = V4L2_FIELD_TOP;
+                       q->last = V4L2_FIELD_TOP;
+               }
+       }
+       return field;
+}
+EXPORT_SYMBOL_GPL(videobuf_next_field);
+
+/* Locking: Caller holds q->vb_lock */
+static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
+                           struct videobuf_buffer *vb, enum v4l2_buf_type type)
+{
+       MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       b->index    = vb->i;
+       b->type     = type;
+
+       b->memory   = vb->memory;
+       switch (b->memory) {
+       case V4L2_MEMORY_MMAP:
+               b->m.offset  = vb->boff;
+               b->length    = vb->bsize;
+               break;
+       case V4L2_MEMORY_USERPTR:
+               b->m.userptr = vb->baddr;
+               b->length    = vb->bsize;
+               break;
+       case V4L2_MEMORY_OVERLAY:
+               b->m.offset  = vb->boff;
+               break;
+       }
+
+       b->flags    = 0;
+       if (vb->map)
+               b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+       switch (vb->state) {
+       case VIDEOBUF_PREPARED:
+       case VIDEOBUF_QUEUED:
+       case VIDEOBUF_ACTIVE:
+               b->flags |= V4L2_BUF_FLAG_QUEUED;
+               break;
+       case VIDEOBUF_ERROR:
+               b->flags |= V4L2_BUF_FLAG_ERROR;
+               /* fall through */
+       case VIDEOBUF_DONE:
+               b->flags |= V4L2_BUF_FLAG_DONE;
+               break;
+       case VIDEOBUF_NEEDS_INIT:
+       case VIDEOBUF_IDLE:
+               /* nothing */
+               break;
+       }
+
+       b->field     = vb->field;
+       b->timestamp = vb->ts;
+       b->bytesused = vb->size;
+       b->sequence  = vb->field_count >> 1;
+}
+
+int videobuf_mmap_free(struct videobuf_queue *q)
+{
+       int ret;
+       videobuf_queue_lock(q);
+       ret = __videobuf_free(q);
+       videobuf_queue_unlock(q);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_mmap_free);
+
+/* Locking: Caller holds q->vb_lock */
+int __videobuf_mmap_setup(struct videobuf_queue *q,
+                       unsigned int bcount, unsigned int bsize,
+                       enum v4l2_memory memory)
+{
+       unsigned int i;
+       int err;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       err = __videobuf_free(q);
+       if (0 != err)
+               return err;
+
+       /* Allocate and initialize buffers */
+       for (i = 0; i < bcount; i++) {
+               q->bufs[i] = videobuf_alloc_vb(q);
+
+               if (NULL == q->bufs[i])
+                       break;
+
+               q->bufs[i]->i      = i;
+               q->bufs[i]->memory = memory;
+               q->bufs[i]->bsize  = bsize;
+               switch (memory) {
+               case V4L2_MEMORY_MMAP:
+                       q->bufs[i]->boff = PAGE_ALIGN(bsize) * i;
+                       break;
+               case V4L2_MEMORY_USERPTR:
+               case V4L2_MEMORY_OVERLAY:
+                       /* nothing */
+                       break;
+               }
+       }
+
+       if (!i)
+               return -ENOMEM;
+
+       dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize);
+
+       return i;
+}
+EXPORT_SYMBOL_GPL(__videobuf_mmap_setup);
+
+int videobuf_mmap_setup(struct videobuf_queue *q,
+                       unsigned int bcount, unsigned int bsize,
+                       enum v4l2_memory memory)
+{
+       int ret;
+       videobuf_queue_lock(q);
+       ret = __videobuf_mmap_setup(q, bcount, bsize, memory);
+       videobuf_queue_unlock(q);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
+
+int videobuf_reqbufs(struct videobuf_queue *q,
+                struct v4l2_requestbuffers *req)
+{
+       unsigned int size, count;
+       int retval;
+
+       if (req->count < 1) {
+               dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
+               return -EINVAL;
+       }
+
+       if (req->memory != V4L2_MEMORY_MMAP     &&
+           req->memory != V4L2_MEMORY_USERPTR  &&
+           req->memory != V4L2_MEMORY_OVERLAY) {
+               dprintk(1, "reqbufs: memory type invalid\n");
+               return -EINVAL;
+       }
+
+       videobuf_queue_lock(q);
+       if (req->type != q->type) {
+               dprintk(1, "reqbufs: queue type invalid\n");
+               retval = -EINVAL;
+               goto done;
+       }
+
+       if (q->streaming) {
+               dprintk(1, "reqbufs: streaming already exists\n");
+               retval = -EBUSY;
+               goto done;
+       }
+       if (!list_empty(&q->stream)) {
+               dprintk(1, "reqbufs: stream running\n");
+               retval = -EBUSY;
+               goto done;
+       }
+
+       count = req->count;
+       if (count > VIDEO_MAX_FRAME)
+               count = VIDEO_MAX_FRAME;
+       size = 0;
+       q->ops->buf_setup(q, &count, &size);
+       dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n",
+               count, size,
+               (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT));
+
+       retval = __videobuf_mmap_setup(q, count, size, req->memory);
+       if (retval < 0) {
+               dprintk(1, "reqbufs: mmap setup returned %d\n", retval);
+               goto done;
+       }
+
+       req->count = retval;
+       retval = 0;
+
+ done:
+       videobuf_queue_unlock(q);
+       return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_reqbufs);
+
+int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
+{
+       int ret = -EINVAL;
+
+       videobuf_queue_lock(q);
+       if (unlikely(b->type != q->type)) {
+               dprintk(1, "querybuf: Wrong type.\n");
+               goto done;
+       }
+       if (unlikely(b->index >= VIDEO_MAX_FRAME)) {
+               dprintk(1, "querybuf: index out of range.\n");
+               goto done;
+       }
+       if (unlikely(NULL == q->bufs[b->index])) {
+               dprintk(1, "querybuf: buffer is null.\n");
+               goto done;
+       }
+
+       videobuf_status(q, b, q->bufs[b->index], q->type);
+
+       ret = 0;
+done:
+       videobuf_queue_unlock(q);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_querybuf);
+
+int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
+{
+       struct videobuf_buffer *buf;
+       enum v4l2_field field;
+       unsigned long flags = 0;
+       int retval;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       if (b->memory == V4L2_MEMORY_MMAP)
+               down_read(&current->mm->mmap_sem);
+
+       videobuf_queue_lock(q);
+       retval = -EBUSY;
+       if (q->reading) {
+               dprintk(1, "qbuf: Reading running...\n");
+               goto done;
+       }
+       retval = -EINVAL;
+       if (b->type != q->type) {
+               dprintk(1, "qbuf: Wrong type.\n");
+               goto done;
+       }
+       if (b->index >= VIDEO_MAX_FRAME) {
+               dprintk(1, "qbuf: index out of range.\n");
+               goto done;
+       }
+       buf = q->bufs[b->index];
+       if (NULL == buf) {
+               dprintk(1, "qbuf: buffer is null.\n");
+               goto done;
+       }
+       MAGIC_CHECK(buf->magic, MAGIC_BUFFER);
+       if (buf->memory != b->memory) {
+               dprintk(1, "qbuf: memory type is wrong.\n");
+               goto done;
+       }
+       if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) {
+               dprintk(1, "qbuf: buffer is already queued or active.\n");
+               goto done;
+       }
+
+       switch (b->memory) {
+       case V4L2_MEMORY_MMAP:
+               if (0 == buf->baddr) {
+                       dprintk(1, "qbuf: mmap requested "
+                                  "but buffer addr is zero!\n");
+                       goto done;
+               }
+               if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT
+                   || q->type == V4L2_BUF_TYPE_VBI_OUTPUT
+                   || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+                       buf->size = b->bytesused;
+                       buf->field = b->field;
+                       buf->ts = b->timestamp;
+               }
+               break;
+       case V4L2_MEMORY_USERPTR:
+               if (b->length < buf->bsize) {
+                       dprintk(1, "qbuf: buffer length is not enough\n");
+                       goto done;
+               }
+               if (VIDEOBUF_NEEDS_INIT != buf->state &&
+                   buf->baddr != b->m.userptr)
+                       q->ops->buf_release(q, buf);
+               buf->baddr = b->m.userptr;
+               break;
+       case V4L2_MEMORY_OVERLAY:
+               buf->boff = b->m.offset;
+               break;
+       default:
+               dprintk(1, "qbuf: wrong memory type\n");
+               goto done;
+       }
+
+       dprintk(1, "qbuf: requesting next field\n");
+       field = videobuf_next_field(q);
+       retval = q->ops->buf_prepare(q, buf, field);
+       if (0 != retval) {
+               dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);
+               goto done;
+       }
+
+       list_add_tail(&buf->stream, &q->stream);
+       if (q->streaming) {
+               spin_lock_irqsave(q->irqlock, flags);
+               q->ops->buf_queue(q, buf);
+               spin_unlock_irqrestore(q->irqlock, flags);
+       }
+       dprintk(1, "qbuf: succeeded\n");
+       retval = 0;
+       wake_up_interruptible_sync(&q->wait);
+
+done:
+       videobuf_queue_unlock(q);
+
+       if (b->memory == V4L2_MEMORY_MMAP)
+               up_read(&current->mm->mmap_sem);
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_qbuf);
+
+/* Locking: Caller holds q->vb_lock */
+static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
+{
+       int retval;
+
+checks:
+       if (!q->streaming) {
+               dprintk(1, "next_buffer: Not streaming\n");
+               retval = -EINVAL;
+               goto done;
+       }
+
+       if (list_empty(&q->stream)) {
+               if (noblock) {
+                       retval = -EAGAIN;
+                       dprintk(2, "next_buffer: no buffers to dequeue\n");
+                       goto done;
+               } else {
+                       dprintk(2, "next_buffer: waiting on buffer\n");
+
+                       /* Drop lock to avoid deadlock with qbuf */
+                       videobuf_queue_unlock(q);
+
+                       /* Checking list_empty and streaming is safe without
+                        * locks because we goto checks to validate while
+                        * holding locks before proceeding */
+                       retval = wait_event_interruptible(q->wait,
+                               !list_empty(&q->stream) || !q->streaming);
+                       videobuf_queue_lock(q);
+
+                       if (retval)
+                               goto done;
+
+                       goto checks;
+               }
+       }
+
+       retval = 0;
+
+done:
+       return retval;
+}
+
+/* Locking: Caller holds q->vb_lock */
+static int stream_next_buffer(struct videobuf_queue *q,
+                       struct videobuf_buffer **vb, int nonblocking)
+{
+       int retval;
+       struct videobuf_buffer *buf = NULL;
+
+       retval = stream_next_buffer_check_queue(q, nonblocking);
+       if (retval)
+               goto done;
+
+       buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
+       retval = videobuf_waiton(q, buf, nonblocking, 1);
+       if (retval < 0)
+               goto done;
+
+       *vb = buf;
+done:
+       return retval;
+}
+
+int videobuf_dqbuf(struct videobuf_queue *q,
+                  struct v4l2_buffer *b, int nonblocking)
+{
+       struct videobuf_buffer *buf = NULL;
+       int retval;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       memset(b, 0, sizeof(*b));
+       videobuf_queue_lock(q);
+
+       retval = stream_next_buffer(q, &buf, nonblocking);
+       if (retval < 0) {
+               dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
+               goto done;
+       }
+
+       switch (buf->state) {
+       case VIDEOBUF_ERROR:
+               dprintk(1, "dqbuf: state is error\n");
+               break;
+       case VIDEOBUF_DONE:
+               dprintk(1, "dqbuf: state is done\n");
+               break;
+       default:
+               dprintk(1, "dqbuf: state invalid\n");
+               retval = -EINVAL;
+               goto done;
+       }
+       CALL(q, sync, q, buf);
+       videobuf_status(q, b, buf, q->type);
+       list_del(&buf->stream);
+       buf->state = VIDEOBUF_IDLE;
+       b->flags &= ~V4L2_BUF_FLAG_DONE;
+done:
+       videobuf_queue_unlock(q);
+       return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_dqbuf);
+
+int videobuf_streamon(struct videobuf_queue *q)
+{
+       struct videobuf_buffer *buf;
+       unsigned long flags = 0;
+       int retval;
+
+       videobuf_queue_lock(q);
+       retval = -EBUSY;
+       if (q->reading)
+               goto done;
+       retval = 0;
+       if (q->streaming)
+               goto done;
+       q->streaming = 1;
+       spin_lock_irqsave(q->irqlock, flags);
+       list_for_each_entry(buf, &q->stream, stream)
+               if (buf->state == VIDEOBUF_PREPARED)
+                       q->ops->buf_queue(q, buf);
+       spin_unlock_irqrestore(q->irqlock, flags);
+
+       wake_up_interruptible_sync(&q->wait);
+done:
+       videobuf_queue_unlock(q);
+       return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_streamon);
+
+/* Locking: Caller holds q->vb_lock */
+static int __videobuf_streamoff(struct videobuf_queue *q)
+{
+       if (!q->streaming)
+               return -EINVAL;
+
+       videobuf_queue_cancel(q);
+
+       return 0;
+}
+
+int videobuf_streamoff(struct videobuf_queue *q)
+{
+       int retval;
+
+       videobuf_queue_lock(q);
+       retval = __videobuf_streamoff(q);
+       videobuf_queue_unlock(q);
+
+       return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_streamoff);
+
+/* Locking: Caller holds q->vb_lock */
+static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
+                                     char __user *data,
+                                     size_t count, loff_t *ppos)
+{
+       enum v4l2_field field;
+       unsigned long flags = 0;
+       int retval;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       /* setup stuff */
+       q->read_buf = videobuf_alloc_vb(q);
+       if (NULL == q->read_buf)
+               return -ENOMEM;
+
+       q->read_buf->memory = V4L2_MEMORY_USERPTR;
+       q->read_buf->baddr  = (unsigned long)data;
+       q->read_buf->bsize  = count;
+
+       field = videobuf_next_field(q);
+       retval = q->ops->buf_prepare(q, q->read_buf, field);
+       if (0 != retval)
+               goto done;
+
+       /* start capture & wait */
+       spin_lock_irqsave(q->irqlock, flags);
+       q->ops->buf_queue(q, q->read_buf);
+       spin_unlock_irqrestore(q->irqlock, flags);
+       retval = videobuf_waiton(q, q->read_buf, 0, 0);
+       if (0 == retval) {
+               CALL(q, sync, q, q->read_buf);
+               if (VIDEOBUF_ERROR == q->read_buf->state)
+                       retval = -EIO;
+               else
+                       retval = q->read_buf->size;
+       }
+
+done:
+       /* cleanup */
+       q->ops->buf_release(q, q->read_buf);
+       kfree(q->read_buf);
+       q->read_buf = NULL;
+       return retval;
+}
+
+static int __videobuf_copy_to_user(struct videobuf_queue *q,
+                                  struct videobuf_buffer *buf,
+                                  char __user *data, size_t count,
+                                  int nonblocking)
+{
+       void *vaddr = CALL(q, vaddr, buf);
+
+       /* copy to userspace */
+       if (count > buf->size - q->read_off)
+               count = buf->size - q->read_off;
+
+       if (copy_to_user(data, vaddr + q->read_off, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static int __videobuf_copy_stream(struct videobuf_queue *q,
+                                 struct videobuf_buffer *buf,
+                                 char __user *data, size_t count, size_t pos,
+                                 int vbihack, int nonblocking)
+{
+       unsigned int *fc = CALL(q, vaddr, buf);
+
+       if (vbihack) {
+               /* dirty, undocumented hack -- pass the frame counter
+                       * within the last four bytes of each vbi data block.
+                       * We need that one to maintain backward compatibility
+                       * to all vbi decoding software out there ... */
+               fc += (buf->size >> 2) - 1;
+               *fc = buf->field_count >> 1;
+               dprintk(1, "vbihack: %d\n", *fc);
+       }
+
+       /* copy stuff using the common method */
+       count = __videobuf_copy_to_user(q, buf, data, count, nonblocking);
+
+       if ((count == -EFAULT) && (pos == 0))
+               return -EFAULT;
+
+       return count;
+}
+
+ssize_t videobuf_read_one(struct videobuf_queue *q,
+                         char __user *data, size_t count, loff_t *ppos,
+                         int nonblocking)
+{
+       enum v4l2_field field;
+       unsigned long flags = 0;
+       unsigned size = 0, nbufs = 1;
+       int retval;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       videobuf_queue_lock(q);
+
+       q->ops->buf_setup(q, &nbufs, &size);
+
+       if (NULL == q->read_buf  &&
+           count >= size        &&
+           !nonblocking) {
+               retval = videobuf_read_zerocopy(q, data, count, ppos);
+               if (retval >= 0  ||  retval == -EIO)
+                       /* ok, all done */
+                       goto done;
+               /* fallback to kernel bounce buffer on failures */
+       }
+
+       if (NULL == q->read_buf) {
+               /* need to capture a new frame */
+               retval = -ENOMEM;
+               q->read_buf = videobuf_alloc_vb(q);
+
+               dprintk(1, "video alloc=0x%p\n", q->read_buf);
+               if (NULL == q->read_buf)
+                       goto done;
+               q->read_buf->memory = V4L2_MEMORY_USERPTR;
+               q->read_buf->bsize = count; /* preferred size */
+               field = videobuf_next_field(q);
+               retval = q->ops->buf_prepare(q, q->read_buf, field);
+
+               if (0 != retval) {
+                       kfree(q->read_buf);
+                       q->read_buf = NULL;
+                       goto done;
+               }
+
+               spin_lock_irqsave(q->irqlock, flags);
+               q->ops->buf_queue(q, q->read_buf);
+               spin_unlock_irqrestore(q->irqlock, flags);
+
+               q->read_off = 0;
+       }
+
+       /* wait until capture is done */
+       retval = videobuf_waiton(q, q->read_buf, nonblocking, 1);
+       if (0 != retval)
+               goto done;
+
+       CALL(q, sync, q, q->read_buf);
+
+       if (VIDEOBUF_ERROR == q->read_buf->state) {
+               /* catch I/O errors */
+               q->ops->buf_release(q, q->read_buf);
+               kfree(q->read_buf);
+               q->read_buf = NULL;
+               retval = -EIO;
+               goto done;
+       }
+
+       /* Copy to userspace */
+       retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking);
+       if (retval < 0)
+               goto done;
+
+       q->read_off += retval;
+       if (q->read_off == q->read_buf->size) {
+               /* all data copied, cleanup */
+               q->ops->buf_release(q, q->read_buf);
+               kfree(q->read_buf);
+               q->read_buf = NULL;
+       }
+
+done:
+       videobuf_queue_unlock(q);
+       return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_read_one);
+
+/* Locking: Caller holds q->vb_lock */
+static int __videobuf_read_start(struct videobuf_queue *q)
+{
+       enum v4l2_field field;
+       unsigned long flags = 0;
+       unsigned int count = 0, size = 0;
+       int err, i;
+
+       q->ops->buf_setup(q, &count, &size);
+       if (count < 2)
+               count = 2;
+       if (count > VIDEO_MAX_FRAME)
+               count = VIDEO_MAX_FRAME;
+       size = PAGE_ALIGN(size);
+
+       err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR);
+       if (err < 0)
+               return err;
+
+       count = err;
+
+       for (i = 0; i < count; i++) {
+               field = videobuf_next_field(q);
+               err = q->ops->buf_prepare(q, q->bufs[i], field);
+               if (err)
+                       return err;
+               list_add_tail(&q->bufs[i]->stream, &q->stream);
+       }
+       spin_lock_irqsave(q->irqlock, flags);
+       for (i = 0; i < count; i++)
+               q->ops->buf_queue(q, q->bufs[i]);
+       spin_unlock_irqrestore(q->irqlock, flags);
+       q->reading = 1;
+       return 0;
+}
+
+static void __videobuf_read_stop(struct videobuf_queue *q)
+{
+       int i;
+
+       videobuf_queue_cancel(q);
+       __videobuf_free(q);
+       INIT_LIST_HEAD(&q->stream);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               kfree(q->bufs[i]);
+               q->bufs[i] = NULL;
+       }
+       q->read_buf = NULL;
+}
+
+int videobuf_read_start(struct videobuf_queue *q)
+{
+       int rc;
+
+       videobuf_queue_lock(q);
+       rc = __videobuf_read_start(q);
+       videobuf_queue_unlock(q);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(videobuf_read_start);
+
+void videobuf_read_stop(struct videobuf_queue *q)
+{
+       videobuf_queue_lock(q);
+       __videobuf_read_stop(q);
+       videobuf_queue_unlock(q);
+}
+EXPORT_SYMBOL_GPL(videobuf_read_stop);
+
+void videobuf_stop(struct videobuf_queue *q)
+{
+       videobuf_queue_lock(q);
+
+       if (q->streaming)
+               __videobuf_streamoff(q);
+
+       if (q->reading)
+               __videobuf_read_stop(q);
+
+       videobuf_queue_unlock(q);
+}
+EXPORT_SYMBOL_GPL(videobuf_stop);
+
+ssize_t videobuf_read_stream(struct videobuf_queue *q,
+                            char __user *data, size_t count, loff_t *ppos,
+                            int vbihack, int nonblocking)
+{
+       int rc, retval;
+       unsigned long flags = 0;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       dprintk(2, "%s\n", __func__);
+       videobuf_queue_lock(q);
+       retval = -EBUSY;
+       if (q->streaming)
+               goto done;
+       if (!q->reading) {
+               retval = __videobuf_read_start(q);
+               if (retval < 0)
+                       goto done;
+       }
+
+       retval = 0;
+       while (count > 0) {
+               /* get / wait for data */
+               if (NULL == q->read_buf) {
+                       q->read_buf = list_entry(q->stream.next,
+                                                struct videobuf_buffer,
+                                                stream);
+                       list_del(&q->read_buf->stream);
+                       q->read_off = 0;
+               }
+               rc = videobuf_waiton(q, q->read_buf, nonblocking, 1);
+               if (rc < 0) {
+                       if (0 == retval)
+                               retval = rc;
+                       break;
+               }
+
+               if (q->read_buf->state == VIDEOBUF_DONE) {
+                       rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count,
+                                       retval, vbihack, nonblocking);
+                       if (rc < 0) {
+                               retval = rc;
+                               break;
+                       }
+                       retval      += rc;
+                       count       -= rc;
+                       q->read_off += rc;
+               } else {
+                       /* some error */
+                       q->read_off = q->read_buf->size;
+                       if (0 == retval)
+                               retval = -EIO;
+               }
+
+               /* requeue buffer when done with copying */
+               if (q->read_off == q->read_buf->size) {
+                       list_add_tail(&q->read_buf->stream,
+                                     &q->stream);
+                       spin_lock_irqsave(q->irqlock, flags);
+                       q->ops->buf_queue(q, q->read_buf);
+                       spin_unlock_irqrestore(q->irqlock, flags);
+                       q->read_buf = NULL;
+               }
+               if (retval < 0)
+                       break;
+       }
+
+done:
+       videobuf_queue_unlock(q);
+       return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_read_stream);
+
+unsigned int videobuf_poll_stream(struct file *file,
+                                 struct videobuf_queue *q,
+                                 poll_table *wait)
+{
+       unsigned long req_events = poll_requested_events(wait);
+       struct videobuf_buffer *buf = NULL;
+       unsigned int rc = 0;
+
+       videobuf_queue_lock(q);
+       if (q->streaming) {
+               if (!list_empty(&q->stream))
+                       buf = list_entry(q->stream.next,
+                                        struct videobuf_buffer, stream);
+       } else if (req_events & (POLLIN | POLLRDNORM)) {
+               if (!q->reading)
+                       __videobuf_read_start(q);
+               if (!q->reading) {
+                       rc = POLLERR;
+               } else if (NULL == q->read_buf) {
+                       q->read_buf = list_entry(q->stream.next,
+                                                struct videobuf_buffer,
+                                                stream);
+                       list_del(&q->read_buf->stream);
+                       q->read_off = 0;
+               }
+               buf = q->read_buf;
+       }
+       if (!buf)
+               rc = POLLERR;
+
+       if (0 == rc) {
+               poll_wait(file, &buf->done, wait);
+               if (buf->state == VIDEOBUF_DONE ||
+                   buf->state == VIDEOBUF_ERROR) {
+                       switch (q->type) {
+                       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+                       case V4L2_BUF_TYPE_VBI_OUTPUT:
+                       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+                               rc = POLLOUT | POLLWRNORM;
+                               break;
+                       default:
+                               rc = POLLIN | POLLRDNORM;
+                               break;
+                       }
+               }
+       }
+       videobuf_queue_unlock(q);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(videobuf_poll_stream);
+
+int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma)
+{
+       int rc = -EINVAL;
+       int i;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
+               dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n");
+               return -EINVAL;
+       }
+
+       videobuf_queue_lock(q);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               struct videobuf_buffer *buf = q->bufs[i];
+
+               if (buf && buf->memory == V4L2_MEMORY_MMAP &&
+                               buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) {
+                       rc = CALL(q, mmap_mapper, q, buf, vma);
+                       break;
+               }
+       }
+       videobuf_queue_unlock(q);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
new file mode 100644 (file)
index 0000000..3a43ba0
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * helper functions for physically contiguous capture buffers
+ *
+ * The functions support hardware lacking scatter gather support
+ * (i.e. the buffers must be linear in physical memory)
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * Based on videobuf-vmalloc.c,
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/videobuf-dma-contig.h>
+
+struct videobuf_dma_contig_memory {
+       u32 magic;
+       void *vaddr;
+       dma_addr_t dma_handle;
+       bool cached;
+       unsigned long size;
+};
+
+#define MAGIC_DC_MEM 0x0733ac61
+#define MAGIC_CHECK(is, should)                                                    \
+       if (unlikely((is) != (should))) {                                   \
+               pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
+               BUG();                                                      \
+       }
+
+static int __videobuf_dc_alloc(struct device *dev,
+                              struct videobuf_dma_contig_memory *mem,
+                              unsigned long size, gfp_t flags)
+{
+       mem->size = size;
+       if (mem->cached) {
+               mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA);
+               if (mem->vaddr) {
+                       int err;
+
+                       mem->dma_handle = dma_map_single(dev, mem->vaddr,
+                                                        mem->size,
+                                                        DMA_FROM_DEVICE);
+                       err = dma_mapping_error(dev, mem->dma_handle);
+                       if (err) {
+                               dev_err(dev, "dma_map_single failed\n");
+
+                               free_pages_exact(mem->vaddr, mem->size);
+                               mem->vaddr = NULL;
+                               return err;
+                       }
+               }
+       } else
+               mem->vaddr = dma_alloc_coherent(dev, mem->size,
+                                               &mem->dma_handle, flags);
+
+       if (!mem->vaddr) {
+               dev_err(dev, "memory alloc size %ld failed\n", mem->size);
+               return -ENOMEM;
+       }
+
+       dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
+
+       return 0;
+}
+
+static void __videobuf_dc_free(struct device *dev,
+                              struct videobuf_dma_contig_memory *mem)
+{
+       if (mem->cached) {
+               if (!mem->vaddr)
+                       return;
+               dma_unmap_single(dev, mem->dma_handle, mem->size,
+                                DMA_FROM_DEVICE);
+               free_pages_exact(mem->vaddr, mem->size);
+       } else
+               dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
+
+       mem->vaddr = NULL;
+}
+
+static void videobuf_vm_open(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+
+       dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+               map, map->count, vma->vm_start, vma->vm_end);
+
+       map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+       struct videobuf_queue *q = map->q;
+       int i;
+
+       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) {
+               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)
+                       videobuf_queue_cancel(q);
+
+               for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+                       if (NULL == q->bufs[i])
+                               continue;
+
+                       if (q->bufs[i]->map != map)
+                               continue;
+
+                       mem = q->bufs[i]->priv;
+                       if (mem) {
+                               /* This callback is called only if kernel has
+                                  allocated memory and this memory is mmapped.
+                                  In this case, memory should be freed,
+                                  in order to do memory unmap.
+                                */
+
+                               MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+                               /* vfree is not atomic - can't be
+                                  called with IRQ's disabled
+                                */
+                               dev_dbg(q->dev, "buf[%d] freeing %p\n",
+                                       i, mem->vaddr);
+
+                               __videobuf_dc_free(q->dev, mem);
+                               mem->vaddr = NULL;
+                       }
+
+                       q->bufs[i]->map = NULL;
+                       q->bufs[i]->baddr = 0;
+               }
+
+               kfree(map);
+
+               videobuf_queue_unlock(q);
+       }
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+       .open   = videobuf_vm_open,
+       .close  = videobuf_vm_close,
+};
+
+/**
+ * videobuf_dma_contig_user_put() - reset pointer to user space buffer
+ * @mem: per-buffer private videobuf-dma-contig data
+ *
+ * This function resets the user space pointer
+ */
+static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
+{
+       mem->dma_handle = 0;
+       mem->size = 0;
+}
+
+/**
+ * videobuf_dma_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-dma-contig data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
+                                       struct videobuf_buffer *vb)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned long prev_pfn, this_pfn;
+       unsigned long pages_done, user_address;
+       unsigned int offset;
+       int ret;
+
+       offset = vb->baddr & ~PAGE_MASK;
+       mem->size = PAGE_ALIGN(vb->size + offset);
+       ret = -EINVAL;
+
+       down_read(&mm->mmap_sem);
+
+       vma = find_vma(mm, vb->baddr);
+       if (!vma)
+               goto out_up;
+
+       if ((vb->baddr + mem->size) > vma->vm_end)
+               goto out_up;
+
+       pages_done = 0;
+       prev_pfn = 0; /* kill warning */
+       user_address = vb->baddr;
+
+       while (pages_done < (mem->size >> PAGE_SHIFT)) {
+               ret = follow_pfn(vma, user_address, &this_pfn);
+               if (ret)
+                       break;
+
+               if (pages_done == 0)
+                       mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
+               else if (this_pfn != (prev_pfn + 1))
+                       ret = -EFAULT;
+
+               if (ret)
+                       break;
+
+               prev_pfn = this_pfn;
+               user_address += PAGE_SIZE;
+               pages_done++;
+       }
+
+out_up:
+       up_read(&current->mm->mmap_sem);
+
+       return ret;
+}
+
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached)
+{
+       struct videobuf_dma_contig_memory *mem;
+       struct videobuf_buffer *vb;
+
+       vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+       if (vb) {
+               vb->priv = ((char *)vb) + size;
+               mem = vb->priv;
+               mem->magic = MAGIC_DC_MEM;
+               mem->cached = cached;
+       }
+
+       return vb;
+}
+
+static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size)
+{
+       return __videobuf_alloc_vb(size, false);
+}
+
+static struct videobuf_buffer *__videobuf_alloc_cached(size_t size)
+{
+       return __videobuf_alloc_vb(size, true);
+}
+
+static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       return mem->vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+                            struct videobuf_buffer *vb,
+                            struct v4l2_framebuffer *fbuf)
+{
+       struct videobuf_dma_contig_memory *mem = vb->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       switch (vb->memory) {
+       case V4L2_MEMORY_MMAP:
+               dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
+
+               /* All handling should be done by __videobuf_mmap_mapper() */
+               if (!mem->vaddr) {
+                       dev_err(q->dev, "memory is not alloced/mmapped.\n");
+                       return -EINVAL;
+               }
+               break;
+       case V4L2_MEMORY_USERPTR:
+               dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
+
+               /* handle pointer from user space */
+               if (vb->baddr)
+                       return videobuf_dma_contig_user_get(mem, vb);
+
+               /* allocate memory for the read() method */
+               if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size),
+                                       GFP_KERNEL))
+                       return -ENOMEM;
+               break;
+       case V4L2_MEMORY_OVERLAY:
+       default:
+               dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __videobuf_sync(struct videobuf_queue *q,
+                          struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size,
+                               DMA_FROM_DEVICE);
+
+       return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+                                 struct videobuf_buffer *buf,
+                                 struct vm_area_struct *vma)
+{
+       struct videobuf_dma_contig_memory *mem;
+       struct videobuf_mapping *map;
+       int retval;
+       unsigned long size;
+       unsigned long pos, start = vma->vm_start;
+       struct page *page;
+
+       dev_dbg(q->dev, "%s\n", __func__);
+
+       /* create mapping + update buffer list */
+       map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+       if (!map)
+               return -ENOMEM;
+
+       buf->map = map;
+       map->q = q;
+
+       buf->baddr = vma->vm_start;
+
+       mem = buf->priv;
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize),
+                               GFP_KERNEL | __GFP_COMP))
+               goto error;
+
+       /* Try to remap memory */
+
+       size = vma->vm_end - vma->vm_start;
+       size = (size < mem->size) ? size : mem->size;
+
+       if (!mem->cached) {
+               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);
+               if (retval) {
+                       dev_err(q->dev, "mmap: remap failed with error %d. ",
+                                                               retval);
+                       dma_free_coherent(q->dev, mem->size,
+                                       mem->vaddr, mem->dma_handle);
+                       goto error;
+               }
+       } else {
+               pos = (unsigned long)mem->vaddr;
+
+               while (size > 0) {
+                       page = virt_to_page((void *)pos);
+                       if (NULL == page) {
+                               dev_err(q->dev, "mmap: virt_to_page failed\n");
+                               __videobuf_dc_free(q->dev, mem);
+                               goto error;
+                       }
+                       retval = vm_insert_page(vma, start, page);
+                       if (retval) {
+                               dev_err(q->dev, "mmap: insert failed with error %d\n",
+                                       retval);
+                               __videobuf_dc_free(q->dev, mem);
+                               goto error;
+                       }
+                       start += PAGE_SIZE;
+                       pos += PAGE_SIZE;
+
+                       if (size > PAGE_SIZE)
+                               size -= PAGE_SIZE;
+                       else
+                               size = 0;
+               }
+       }
+
+       vma->vm_ops = &videobuf_vm_ops;
+       vma->vm_flags |= VM_DONTEXPAND;
+       vma->vm_private_data = map;
+
+       dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+               map, q, vma->vm_start, vma->vm_end,
+               (long int)buf->bsize, vma->vm_pgoff, buf->i);
+
+       videobuf_vm_open(vma);
+
+       return 0;
+
+error:
+       kfree(map);
+       return -ENOMEM;
+}
+
+static struct videobuf_qtype_ops qops = {
+       .magic          = MAGIC_QTYPE_OPS,
+       .alloc_vb       = __videobuf_alloc_uncached,
+       .iolock         = __videobuf_iolock,
+       .mmap_mapper    = __videobuf_mmap_mapper,
+       .vaddr          = __videobuf_to_vaddr,
+};
+
+static struct videobuf_qtype_ops qops_cached = {
+       .magic          = MAGIC_QTYPE_OPS,
+       .alloc_vb       = __videobuf_alloc_cached,
+       .iolock         = __videobuf_iolock,
+       .sync           = __videobuf_sync,
+       .mmap_mapper    = __videobuf_mmap_mapper,
+       .vaddr          = __videobuf_to_vaddr,
+};
+
+void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
+                                   const struct videobuf_queue_ops *ops,
+                                   struct device *dev,
+                                   spinlock_t *irqlock,
+                                   enum v4l2_buf_type type,
+                                   enum v4l2_field field,
+                                   unsigned int msize,
+                                   void *priv,
+                                   struct mutex *ext_lock)
+{
+       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+                                priv, &qops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
+
+void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q,
+                                          const struct videobuf_queue_ops *ops,
+                                          struct device *dev,
+                                          spinlock_t *irqlock,
+                                          enum v4l2_buf_type type,
+                                          enum v4l2_field field,
+                                          unsigned int msize,
+                                          void *priv, struct mutex *ext_lock)
+{
+       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+                                priv, &qops_cached, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached);
+
+dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       return mem->dma_handle;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
+
+void videobuf_dma_contig_free(struct videobuf_queue *q,
+                             struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_contig_memory *mem = buf->priv;
+
+       /* mmapped memory can't be freed here, otherwise mmapped region
+          would be released, while still needed. In this case, the memory
+          release should happen inside videobuf_vm_close().
+          So, it should free memory only if the memory were allocated for
+          read() operation.
+        */
+       if (buf->memory != V4L2_MEMORY_USERPTR)
+               return;
+
+       if (!mem)
+               return;
+
+       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+       /* handle user space pointer case */
+       if (buf->baddr) {
+               videobuf_dma_contig_user_put(mem);
+               return;
+       }
+
+       /* read() method */
+       if (mem->vaddr) {
+               __videobuf_dc_free(q->dev, mem);
+               mem->vaddr = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
+
+MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
new file mode 100644 (file)
index 0000000..f300dea
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * helper functions for SG DMA video4linux capture buffers
+ *
+ * The functions expect the hardware being able to scatter gather
+ * (i.e. the buffers are not linear in physical memory, but fragmented
+ * into PAGE_SIZE chunks).  They also assume the driver does not need
+ * to touch the video data.
+ *
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * Highly based on video-buf written originally by:
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ * (c) 2006 Ted Walther and John Sokol
+ *
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include <media/videobuf-dma-sg.h>
+
+#define MAGIC_DMABUF 0x19721112
+#define MAGIC_SG_MEM 0x17890714
+
+#define MAGIC_CHECK(is, should)                                                \
+       if (unlikely((is) != (should))) {                               \
+               printk(KERN_ERR "magic mismatch: %x (expected %x)\n",   \
+                               is, should);                            \
+               BUG();                                                  \
+       }
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, fmt, arg...)                                    \
+       if (debug >= level)                                             \
+               printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Return a scatterlist for some page-aligned vmalloc()'ed memory
+ * block (NULL on errors).  Memory for the scatterlist is allocated
+ * using kmalloc.  The caller must free the memory.
+ */
+static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt,
+                                                 int nr_pages)
+{
+       struct scatterlist *sglist;
+       struct page *pg;
+       int i;
+
+       sglist = vzalloc(nr_pages * sizeof(*sglist));
+       if (NULL == sglist)
+               return NULL;
+       sg_init_table(sglist, nr_pages);
+       for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
+               pg = vmalloc_to_page(virt);
+               if (NULL == pg)
+                       goto err;
+               BUG_ON(PageHighMem(pg));
+               sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
+       }
+       return sglist;
+
+err:
+       vfree(sglist);
+       return NULL;
+}
+
+/*
+ * Return a scatterlist for a an array of userpages (NULL on errors).
+ * Memory for the scatterlist is allocated using kmalloc.  The caller
+ * must free the memory.
+ */
+static struct scatterlist *videobuf_pages_to_sg(struct page **pages,
+                                       int nr_pages, int offset, size_t size)
+{
+       struct scatterlist *sglist;
+       int i;
+
+       if (NULL == pages[0])
+               return NULL;
+       sglist = vmalloc(nr_pages * sizeof(*sglist));
+       if (NULL == sglist)
+               return NULL;
+       sg_init_table(sglist, nr_pages);
+
+       if (PageHighMem(pages[0]))
+               /* DMA to highmem pages might not work */
+               goto highmem;
+       sg_set_page(&sglist[0], pages[0],
+                       min_t(size_t, PAGE_SIZE - offset, size), offset);
+       size -= min_t(size_t, PAGE_SIZE - offset, size);
+       for (i = 1; i < nr_pages; i++) {
+               if (NULL == pages[i])
+                       goto nopage;
+               if (PageHighMem(pages[i]))
+                       goto highmem;
+               sg_set_page(&sglist[i], pages[i], min_t(size_t, PAGE_SIZE, size), 0);
+               size -= min_t(size_t, PAGE_SIZE, size);
+       }
+       return sglist;
+
+nopage:
+       dprintk(2, "sgl: oops - no page\n");
+       vfree(sglist);
+       return NULL;
+
+highmem:
+       dprintk(2, "sgl: oops - highmem page\n");
+       vfree(sglist);
+       return NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_sg_memory *mem = buf->priv;
+       BUG_ON(!mem);
+
+       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+       return &mem->dma;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_dma);
+
+void videobuf_dma_init(struct videobuf_dmabuf *dma)
+{
+       memset(dma, 0, sizeof(*dma));
+       dma->magic = MAGIC_DMABUF;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init);
+
+static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
+                       int direction, unsigned long data, unsigned long size)
+{
+       unsigned long first, last;
+       int err, rw = 0;
+
+       dma->direction = direction;
+       switch (dma->direction) {
+       case DMA_FROM_DEVICE:
+               rw = READ;
+               break;
+       case DMA_TO_DEVICE:
+               rw = WRITE;
+               break;
+       default:
+               BUG();
+       }
+
+       first = (data          & PAGE_MASK) >> PAGE_SHIFT;
+       last  = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+       dma->offset = data & ~PAGE_MASK;
+       dma->size = size;
+       dma->nr_pages = last-first+1;
+       dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL);
+       if (NULL == dma->pages)
+               return -ENOMEM;
+
+       dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
+               data, size, dma->nr_pages);
+
+       err = get_user_pages(current, current->mm,
+                            data & PAGE_MASK, dma->nr_pages,
+                            rw == READ, 1, /* force */
+                            dma->pages, NULL);
+
+       if (err != dma->nr_pages) {
+               dma->nr_pages = (err >= 0) ? err : 0;
+               dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages);
+               return err < 0 ? err : -EINVAL;
+       }
+       return 0;
+}
+
+int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
+                          unsigned long data, unsigned long size)
+{
+       int ret;
+
+       down_read(&current->mm->mmap_sem);
+       ret = videobuf_dma_init_user_locked(dma, direction, data, size);
+       up_read(&current->mm->mmap_sem);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
+
+int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
+                            int nr_pages)
+{
+       dprintk(1, "init kernel [%d pages]\n", nr_pages);
+
+       dma->direction = direction;
+       dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
+       if (NULL == dma->vaddr) {
+               dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
+               return -ENOMEM;
+       }
+
+       dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
+                               (unsigned long)dma->vaddr,
+                               nr_pages << PAGE_SHIFT);
+
+       memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
+       dma->nr_pages = nr_pages;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
+
+int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
+                             dma_addr_t addr, int nr_pages)
+{
+       dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n",
+               nr_pages, (unsigned long)addr);
+       dma->direction = direction;
+
+       if (0 == addr)
+               return -EINVAL;
+
+       dma->bus_addr = addr;
+       dma->nr_pages = nr_pages;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay);
+
+int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
+{
+       MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
+       BUG_ON(0 == dma->nr_pages);
+
+       if (dma->pages) {
+               dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,
+                                                  dma->offset, dma->size);
+       }
+       if (dma->vaddr) {
+               dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr,
+                                                    dma->nr_pages);
+       }
+       if (dma->bus_addr) {
+               dma->sglist = vmalloc(sizeof(*dma->sglist));
+               if (NULL != dma->sglist) {
+                       dma->sglen = 1;
+                       sg_dma_address(&dma->sglist[0]) = dma->bus_addr
+                                                       & PAGE_MASK;
+                       dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK;
+                       sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE;
+               }
+       }
+       if (NULL == dma->sglist) {
+               dprintk(1, "scatterlist is NULL\n");
+               return -ENOMEM;
+       }
+       if (!dma->bus_addr) {
+               dma->sglen = dma_map_sg(dev, dma->sglist,
+                                       dma->nr_pages, dma->direction);
+               if (0 == dma->sglen) {
+                       printk(KERN_WARNING
+                              "%s: videobuf_map_sg failed\n", __func__);
+                       vfree(dma->sglist);
+                       dma->sglist = NULL;
+                       dma->sglen = 0;
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_map);
+
+int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
+{
+       MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
+
+       if (!dma->sglen)
+               return 0;
+
+       dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction);
+
+       vfree(dma->sglist);
+       dma->sglist = NULL;
+       dma->sglen = 0;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_unmap);
+
+int videobuf_dma_free(struct videobuf_dmabuf *dma)
+{
+       int i;
+       MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
+       BUG_ON(dma->sglen);
+
+       if (dma->pages) {
+               for (i = 0; i < dma->nr_pages; i++)
+                       page_cache_release(dma->pages[i]);
+               kfree(dma->pages);
+               dma->pages = NULL;
+       }
+
+       vfree(dma->vaddr);
+       dma->vaddr = NULL;
+
+       if (dma->bus_addr)
+               dma->bus_addr = 0;
+       dma->direction = DMA_NONE;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_free);
+
+/* --------------------------------------------------------------------- */
+
+static void videobuf_vm_open(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+
+       dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map,
+               map->count, vma->vm_start, vma->vm_end);
+
+       map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+       struct videobuf_queue *q = map->q;
+       struct videobuf_dma_sg_memory *mem;
+       int i;
+
+       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) {
+               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;
+                       mem = q->bufs[i]->priv;
+                       if (!mem)
+                               continue;
+
+                       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+                       if (q->bufs[i]->map != map)
+                               continue;
+                       q->bufs[i]->map   = NULL;
+                       q->bufs[i]->baddr = 0;
+                       q->ops->buf_release(q, q->bufs[i]);
+               }
+               videobuf_queue_unlock(q);
+               kfree(map);
+       }
+       return;
+}
+
+/*
+ * Get a anonymous page for the mapping.  Make sure we can DMA to that
+ * memory location with 32bit PCI devices (i.e. don't use highmem for
+ * now ...).  Bounce buffers don't work very well for the data rates
+ * video capture has.
+ */
+static int videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+       struct page *page;
+
+       dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n",
+               (unsigned long)vmf->virtual_address,
+               vma->vm_start, vma->vm_end);
+
+       page = alloc_page(GFP_USER | __GFP_DMA32);
+       if (!page)
+               return VM_FAULT_OOM;
+       clear_user_highpage(page, (unsigned long)vmf->virtual_address);
+       vmf->page = page;
+
+       return 0;
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+       .open   = videobuf_vm_open,
+       .close  = videobuf_vm_close,
+       .fault  = videobuf_vm_fault,
+};
+
+/* ---------------------------------------------------------------------
+ * SG handlers for the generic methods
+ */
+
+/* Allocated area consists on 3 parts:
+       struct video_buffer
+       struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
+       struct videobuf_dma_sg_memory
+ */
+
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
+{
+       struct videobuf_dma_sg_memory *mem;
+       struct videobuf_buffer *vb;
+
+       vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+       if (!vb)
+               return vb;
+
+       mem = vb->priv = ((char *)vb) + size;
+       mem->magic = MAGIC_SG_MEM;
+
+       videobuf_dma_init(&mem->dma);
+
+       dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
+               __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
+               mem, (long)sizeof(*mem));
+
+       return vb;
+}
+
+static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_sg_memory *mem = buf->priv;
+       BUG_ON(!mem);
+
+       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+       return mem->dma.vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+                            struct videobuf_buffer *vb,
+                            struct v4l2_framebuffer *fbuf)
+{
+       int err, pages;
+       dma_addr_t bus;
+       struct videobuf_dma_sg_memory *mem = vb->priv;
+       BUG_ON(!mem);
+
+       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+       switch (vb->memory) {
+       case V4L2_MEMORY_MMAP:
+       case V4L2_MEMORY_USERPTR:
+               if (0 == vb->baddr) {
+                       /* no userspace addr -- kernel bounce buffer */
+                       pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
+                       err = videobuf_dma_init_kernel(&mem->dma,
+                                                      DMA_FROM_DEVICE,
+                                                      pages);
+                       if (0 != err)
+                               return err;
+               } else if (vb->memory == V4L2_MEMORY_USERPTR) {
+                       /* dma directly to userspace */
+                       err = videobuf_dma_init_user(&mem->dma,
+                                                    DMA_FROM_DEVICE,
+                                                    vb->baddr, vb->bsize);
+                       if (0 != err)
+                               return err;
+               } else {
+                       /* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP
+                       buffers can only be called from videobuf_qbuf
+                       we take current->mm->mmap_sem there, to prevent
+                       locking inversion, so don't take it here */
+
+                       err = videobuf_dma_init_user_locked(&mem->dma,
+                                                     DMA_FROM_DEVICE,
+                                                     vb->baddr, vb->bsize);
+                       if (0 != err)
+                               return err;
+               }
+               break;
+       case V4L2_MEMORY_OVERLAY:
+               if (NULL == fbuf)
+                       return -EINVAL;
+               /* FIXME: need sanity checks for vb->boff */
+               /*
+                * Using a double cast to avoid compiler warnings when
+                * building for PAE. Compiler doesn't like direct casting
+                * of a 32 bit ptr to 64 bit integer.
+                */
+               bus   = (dma_addr_t)(unsigned long)fbuf->base + vb->boff;
+               pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
+               err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE,
+                                               bus, pages);
+               if (0 != err)
+                       return err;
+               break;
+       default:
+               BUG();
+       }
+       err = videobuf_dma_map(q->dev, &mem->dma);
+       if (0 != err)
+               return err;
+
+       return 0;
+}
+
+static int __videobuf_sync(struct videobuf_queue *q,
+                          struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_sg_memory *mem = buf->priv;
+       BUG_ON(!mem || !mem->dma.sglen);
+
+       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+       MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF);
+
+       dma_sync_sg_for_cpu(q->dev, mem->dma.sglist,
+                           mem->dma.sglen, mem->dma.direction);
+
+       return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+                                 struct videobuf_buffer *buf,
+                                 struct vm_area_struct *vma)
+{
+       struct videobuf_dma_sg_memory *mem = buf->priv;
+       struct videobuf_mapping *map;
+       unsigned int first, last, size = 0, i;
+       int retval;
+
+       retval = -EINVAL;
+
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+       /* look for first buffer to map */
+       for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+               if (buf == q->bufs[first]) {
+                       size = PAGE_ALIGN(q->bufs[first]->bsize);
+                       break;
+               }
+       }
+
+       /* paranoia, should never happen since buf is always valid. */
+       if (!size) {
+               dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n",
+                               (vma->vm_pgoff << PAGE_SHIFT));
+               goto done;
+       }
+
+       last = first;
+
+       /* create mapping + update buffer list */
+       retval = -ENOMEM;
+       map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+       if (NULL == map)
+               goto done;
+
+       size = 0;
+       for (i = first; i <= last; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               q->bufs[i]->map   = map;
+               q->bufs[i]->baddr = vma->vm_start + size;
+               size += PAGE_ALIGN(q->bufs[i]->bsize);
+       }
+
+       map->count    = 1;
+       map->q        = q;
+       vma->vm_ops   = &videobuf_vm_ops;
+       vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+       vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+       vma->vm_private_data = map;
+       dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n",
+               map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last);
+       retval = 0;
+
+done:
+       return retval;
+}
+
+static struct videobuf_qtype_ops sg_ops = {
+       .magic        = MAGIC_QTYPE_OPS,
+
+       .alloc_vb     = __videobuf_alloc_vb,
+       .iolock       = __videobuf_iolock,
+       .sync         = __videobuf_sync,
+       .mmap_mapper  = __videobuf_mmap_mapper,
+       .vaddr        = __videobuf_to_vaddr,
+};
+
+void *videobuf_sg_alloc(size_t size)
+{
+       struct videobuf_queue q;
+
+       /* Required to make generic handler to call __videobuf_alloc */
+       q.int_ops = &sg_ops;
+
+       q.msize = size;
+
+       return videobuf_alloc_vb(&q);
+}
+EXPORT_SYMBOL_GPL(videobuf_sg_alloc);
+
+void videobuf_queue_sg_init(struct videobuf_queue *q,
+                        const struct videobuf_queue_ops *ops,
+                        struct device *dev,
+                        spinlock_t *irqlock,
+                        enum v4l2_buf_type type,
+                        enum v4l2_field field,
+                        unsigned int msize,
+                        void *priv,
+                        struct mutex *ext_lock)
+{
+       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+                                priv, &sg_ops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);
+
diff --git a/drivers/media/v4l2-core/videobuf-dvb.c b/drivers/media/v4l2-core/videobuf-dvb.c
new file mode 100644 (file)
index 0000000..b7efa45
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ *
+ * some helper function for simple DVB cards which simply DMA the
+ * complete transport stream and let the computer sort everything else
+ * (i.e. we are using the software demux, ...).  Also uses the
+ * video-buf to manage DMA buffers.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+
+#include <linux/freezer.h>
+
+#include <media/videobuf-core.h>
+#include <media/videobuf-dvb.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...)   if (debug)                      \
+       printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int videobuf_dvb_thread(void *data)
+{
+       struct videobuf_dvb *dvb = data;
+       struct videobuf_buffer *buf;
+       unsigned long flags;
+       void *outp;
+
+       dprintk("dvb thread started\n");
+       set_freezable();
+       videobuf_read_start(&dvb->dvbq);
+
+       for (;;) {
+               /* fetch next buffer */
+               buf = list_entry(dvb->dvbq.stream.next,
+                                struct videobuf_buffer, stream);
+               list_del(&buf->stream);
+               videobuf_waiton(&dvb->dvbq, buf, 0, 1);
+
+               /* no more feeds left or stop_feed() asked us to quit */
+               if (0 == dvb->nfeeds)
+                       break;
+               if (kthread_should_stop())
+                       break;
+               try_to_freeze();
+
+               /* feed buffer data to demux */
+               outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf);
+
+               if (buf->state == VIDEOBUF_DONE)
+                       dvb_dmx_swfilter(&dvb->demux, outp,
+                                        buf->size);
+
+               /* requeue buffer */
+               list_add_tail(&buf->stream,&dvb->dvbq.stream);
+               spin_lock_irqsave(dvb->dvbq.irqlock,flags);
+               dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf);
+               spin_unlock_irqrestore(dvb->dvbq.irqlock,flags);
+       }
+
+       videobuf_read_stop(&dvb->dvbq);
+       dprintk("dvb thread stopped\n");
+
+       /* Hmm, linux becomes *very* unhappy without this ... */
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule();
+       }
+       return 0;
+}
+
+static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+       struct dvb_demux *demux  = feed->demux;
+       struct videobuf_dvb *dvb = demux->priv;
+       int rc;
+
+       if (!demux->dmx.frontend)
+               return -EINVAL;
+
+       mutex_lock(&dvb->lock);
+       dvb->nfeeds++;
+       rc = dvb->nfeeds;
+
+       if (NULL != dvb->thread)
+               goto out;
+       dvb->thread = kthread_run(videobuf_dvb_thread,
+                                 dvb, "%s dvb", dvb->name);
+       if (IS_ERR(dvb->thread)) {
+               rc = PTR_ERR(dvb->thread);
+               dvb->thread = NULL;
+       }
+
+out:
+       mutex_unlock(&dvb->lock);
+       return rc;
+}
+
+static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+       struct dvb_demux *demux  = feed->demux;
+       struct videobuf_dvb *dvb = demux->priv;
+       int err = 0;
+
+       mutex_lock(&dvb->lock);
+       dvb->nfeeds--;
+       if (0 == dvb->nfeeds  &&  NULL != dvb->thread) {
+               err = kthread_stop(dvb->thread);
+               dvb->thread = NULL;
+       }
+       mutex_unlock(&dvb->lock);
+       return err;
+}
+
+static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
+                         struct module *module,
+                         void *adapter_priv,
+                         struct device *device,
+                         char *adapter_name,
+                         short *adapter_nr,
+                         int mfe_shared)
+{
+       int result;
+
+       mutex_init(&fe->lock);
+
+       /* register adapter */
+       result = dvb_register_adapter(&fe->adapter, adapter_name, module,
+               device, adapter_nr);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+                      adapter_name, result);
+       }
+       fe->adapter.priv = adapter_priv;
+       fe->adapter.mfe_shared = mfe_shared;
+
+       return result;
+}
+
+static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter,
+       struct videobuf_dvb *dvb)
+{
+       int result;
+
+       /* register frontend */
+       result = dvb_register_frontend(adapter, dvb->frontend);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_frontend;
+       }
+
+       /* register demux stuff */
+       dvb->demux.dmx.capabilities =
+               DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+               DMX_MEMORY_BASED_FILTERING;
+       dvb->demux.priv       = dvb;
+       dvb->demux.filternum  = 256;
+       dvb->demux.feednum    = 256;
+       dvb->demux.start_feed = videobuf_dvb_start_feed;
+       dvb->demux.stop_feed  = videobuf_dvb_stop_feed;
+       result = dvb_dmx_init(&dvb->demux);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_dmx;
+       }
+
+       dvb->dmxdev.filternum    = 256;
+       dvb->dmxdev.demux        = &dvb->demux.dmx;
+       dvb->dmxdev.capabilities = 0;
+       result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
+
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_dmxdev;
+       }
+
+       dvb->fe_hw.source = DMX_FRONTEND_0;
+       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_hw;
+       }
+
+       dvb->fe_mem.source = DMX_MEMORY_FE;
+       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_mem;
+       }
+
+       result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_conn;
+       }
+
+       /* register network adapter */
+       result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_conn;
+       }
+       return 0;
+
+fail_fe_conn:
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+       dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+       dvb_dmx_release(&dvb->demux);
+fail_dmx:
+       dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+       dvb_frontend_detach(dvb->frontend);
+       dvb->frontend = NULL;
+
+       return result;
+}
+
+/* ------------------------------------------------------------------ */
+/* Register a single adapter and one or more frontends */
+int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
+                         struct module *module,
+                         void *adapter_priv,
+                         struct device *device,
+                         short *adapter_nr,
+                         int mfe_shared)
+{
+       struct list_head *list, *q;
+       struct videobuf_dvb_frontend *fe;
+       int res;
+
+       fe = videobuf_dvb_get_frontend(f, 1);
+       if (!fe) {
+               printk(KERN_WARNING "Unable to register the adapter which has no frontends\n");
+               return -EINVAL;
+       }
+
+       /* Bring up the adapter */
+       res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
+               fe->dvb.name, adapter_nr, mfe_shared);
+       if (res < 0) {
+               printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
+               return res;
+       }
+
+       /* Attach all of the frontends to the adapter */
+       mutex_lock(&f->lock);
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+               res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb);
+               if (res < 0) {
+                       printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n",
+                               fe->dvb.name, res);
+                       goto err;
+               }
+       }
+       mutex_unlock(&f->lock);
+       return 0;
+
+err:
+       mutex_unlock(&f->lock);
+       videobuf_dvb_unregister_bus(f);
+       return res;
+}
+EXPORT_SYMBOL(videobuf_dvb_register_bus);
+
+void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f)
+{
+       videobuf_dvb_dealloc_frontends(f);
+
+       dvb_unregister_adapter(&f->adapter);
+}
+EXPORT_SYMBOL(videobuf_dvb_unregister_bus);
+
+struct videobuf_dvb_frontend *videobuf_dvb_get_frontend(
+       struct videobuf_dvb_frontends *f, int id)
+{
+       struct list_head *list, *q;
+       struct videobuf_dvb_frontend *fe, *ret = NULL;
+
+       mutex_lock(&f->lock);
+
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+               if (fe->id == id) {
+                       ret = fe;
+                       break;
+               }
+       }
+
+       mutex_unlock(&f->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(videobuf_dvb_get_frontend);
+
+int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f,
+       struct dvb_frontend *p)
+{
+       struct list_head *list, *q;
+       struct videobuf_dvb_frontend *fe = NULL;
+       int ret = 0;
+
+       mutex_lock(&f->lock);
+
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+               if (fe->dvb.frontend == p) {
+                       ret = fe->id;
+                       break;
+               }
+       }
+
+       mutex_unlock(&f->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(videobuf_dvb_find_frontend);
+
+struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend(
+       struct videobuf_dvb_frontends *f, int id)
+{
+       struct videobuf_dvb_frontend *fe;
+
+       fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL);
+       if (fe == NULL)
+               goto fail_alloc;
+
+       fe->id = id;
+       mutex_init(&fe->dvb.lock);
+
+       mutex_lock(&f->lock);
+       list_add_tail(&fe->felist, &f->felist);
+       mutex_unlock(&f->lock);
+
+fail_alloc:
+       return fe;
+}
+EXPORT_SYMBOL(videobuf_dvb_alloc_frontend);
+
+void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f)
+{
+       struct list_head *list, *q;
+       struct videobuf_dvb_frontend *fe;
+
+       mutex_lock(&f->lock);
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+               if (fe->dvb.net.dvbdev) {
+                       dvb_net_release(&fe->dvb.net);
+                       fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+                               &fe->dvb.fe_mem);
+                       fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+                               &fe->dvb.fe_hw);
+                       dvb_dmxdev_release(&fe->dvb.dmxdev);
+                       dvb_dmx_release(&fe->dvb.demux);
+                       dvb_unregister_frontend(fe->dvb.frontend);
+               }
+               if (fe->dvb.frontend)
+                       /* always allocated, may have been reset */
+                       dvb_frontend_detach(fe->dvb.frontend);
+               list_del(list); /* remove list entry */
+               kfree(fe);      /* free frontend allocation */
+       }
+       mutex_unlock(&f->lock);
+}
+EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends);
diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c
new file mode 100644 (file)
index 0000000..df14258
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * helper functions for vmalloc video4linux capture buffers
+ *
+ * The functions expect the hardware being able to scatter gather
+ * (i.e. the buffers are not linear in physical memory, but fragmented
+ * into PAGE_SIZE chunks).  They also assume the driver does not need
+ * to touch the video data.
+ *
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include <media/videobuf-vmalloc.h>
+
+#define MAGIC_DMABUF   0x17760309
+#define MAGIC_VMAL_MEM 0x18221223
+
+#define MAGIC_CHECK(is, should)                                                \
+       if (unlikely((is) != (should))) {                               \
+               printk(KERN_ERR "magic mismatch: %x (expected %x)\n",   \
+                               is, should);                            \
+               BUG();                                                  \
+       }
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, fmt, arg...)                                    \
+       if (debug >= level)                                             \
+               printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
+
+
+/***************************************************************************/
+
+static void videobuf_vm_open(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+
+       dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
+               map->count, vma->vm_start, vma->vm_end);
+
+       map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+       struct videobuf_mapping *map = vma->vm_private_data;
+       struct videobuf_queue *q = map->q;
+       int i;
+
+       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) {
+               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)
+                       videobuf_queue_cancel(q);
+
+               for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+                       if (NULL == q->bufs[i])
+                               continue;
+
+                       if (q->bufs[i]->map != map)
+                               continue;
+
+                       mem = q->bufs[i]->priv;
+                       if (mem) {
+                               /* This callback is called only if kernel has
+                                  allocated memory and this memory is mmapped.
+                                  In this case, memory should be freed,
+                                  in order to do memory unmap.
+                                */
+
+                               MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+                               /* vfree is not atomic - can't be
+                                  called with IRQ's disabled
+                                */
+                               dprintk(1, "%s: buf[%d] freeing (%p)\n",
+                                       __func__, i, mem->vaddr);
+
+                               vfree(mem->vaddr);
+                               mem->vaddr = NULL;
+                       }
+
+                       q->bufs[i]->map   = NULL;
+                       q->bufs[i]->baddr = 0;
+               }
+
+               kfree(map);
+
+               videobuf_queue_unlock(q);
+       }
+
+       return;
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+       .open     = videobuf_vm_open,
+       .close    = videobuf_vm_close,
+};
+
+/* ---------------------------------------------------------------------
+ * vmalloc handlers for the generic methods
+ */
+
+/* Allocated area consists on 3 parts:
+       struct video_buffer
+       struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
+       struct videobuf_dma_sg_memory
+ */
+
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
+{
+       struct videobuf_vmalloc_memory *mem;
+       struct videobuf_buffer *vb;
+
+       vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+       if (!vb)
+               return vb;
+
+       mem = vb->priv = ((char *)vb) + size;
+       mem->magic = MAGIC_VMAL_MEM;
+
+       dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
+               __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
+               mem, (long)sizeof(*mem));
+
+       return vb;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+                            struct videobuf_buffer *vb,
+                            struct v4l2_framebuffer *fbuf)
+{
+       struct videobuf_vmalloc_memory *mem = vb->priv;
+       int pages;
+
+       BUG_ON(!mem);
+
+       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+       switch (vb->memory) {
+       case V4L2_MEMORY_MMAP:
+               dprintk(1, "%s memory method MMAP\n", __func__);
+
+               /* All handling should be done by __videobuf_mmap_mapper() */
+               if (!mem->vaddr) {
+                       printk(KERN_ERR "memory is not alloced/mmapped.\n");
+                       return -EINVAL;
+               }
+               break;
+       case V4L2_MEMORY_USERPTR:
+               pages = PAGE_ALIGN(vb->size);
+
+               dprintk(1, "%s memory method USERPTR\n", __func__);
+
+               if (vb->baddr) {
+                       printk(KERN_ERR "USERPTR is currently not supported\n");
+                       return -EINVAL;
+               }
+
+               /* The only USERPTR currently supported is the one needed for
+                * read() method.
+                */
+
+               mem->vaddr = vmalloc_user(pages);
+               if (!mem->vaddr) {
+                       printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
+                       return -ENOMEM;
+               }
+               dprintk(1, "vmalloc is at addr %p (%d pages)\n",
+                       mem->vaddr, pages);
+
+#if 0
+               int rc;
+               /* Kernel userptr is used also by read() method. In this case,
+                  there's no need to remap, since data will be copied to user
+                */
+               if (!vb->baddr)
+                       return 0;
+
+               /* FIXME: to properly support USERPTR, remap should occur.
+                  The code below won't work, since mem->vma = NULL
+                */
+               /* Try to remap memory */
+               rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0);
+               if (rc < 0) {
+                       printk(KERN_ERR "mmap: remap failed with error %d", rc);
+                       return -ENOMEM;
+               }
+#endif
+
+               break;
+       case V4L2_MEMORY_OVERLAY:
+       default:
+               dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
+
+               /* Currently, doesn't support V4L2_MEMORY_OVERLAY */
+               printk(KERN_ERR "Memory method currently unsupported.\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+                                 struct videobuf_buffer *buf,
+                                 struct vm_area_struct *vma)
+{
+       struct videobuf_vmalloc_memory *mem;
+       struct videobuf_mapping *map;
+       int retval, pages;
+
+       dprintk(1, "%s\n", __func__);
+
+       /* create mapping + update buffer list */
+       map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+       if (NULL == map)
+               return -ENOMEM;
+
+       buf->map = map;
+       map->q     = q;
+
+       buf->baddr = vma->vm_start;
+
+       mem = buf->priv;
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+       pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
+       mem->vaddr = vmalloc_user(pages);
+       if (!mem->vaddr) {
+               printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
+               goto error;
+       }
+       dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages);
+
+       /* Try to remap memory */
+       retval = remap_vmalloc_range(vma, mem->vaddr, 0);
+       if (retval < 0) {
+               printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
+               vfree(mem->vaddr);
+               goto error;
+       }
+
+       vma->vm_ops          = &videobuf_vm_ops;
+       vma->vm_flags       |= VM_DONTEXPAND | VM_RESERVED;
+       vma->vm_private_data = map;
+
+       dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+               map, q, vma->vm_start, vma->vm_end,
+               (long int)buf->bsize,
+               vma->vm_pgoff, buf->i);
+
+       videobuf_vm_open(vma);
+
+       return 0;
+
+error:
+       mem = NULL;
+       kfree(map);
+       return -ENOMEM;
+}
+
+static struct videobuf_qtype_ops qops = {
+       .magic        = MAGIC_QTYPE_OPS,
+
+       .alloc_vb     = __videobuf_alloc_vb,
+       .iolock       = __videobuf_iolock,
+       .mmap_mapper  = __videobuf_mmap_mapper,
+       .vaddr        = videobuf_to_vmalloc,
+};
+
+void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
+                        const struct videobuf_queue_ops *ops,
+                        struct device *dev,
+                        spinlock_t *irqlock,
+                        enum v4l2_buf_type type,
+                        enum v4l2_field field,
+                        unsigned int msize,
+                        void *priv,
+                        struct mutex *ext_lock)
+{
+       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+                                priv, &qops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
+
+void *videobuf_to_vmalloc(struct videobuf_buffer *buf)
+{
+       struct videobuf_vmalloc_memory *mem = buf->priv;
+       BUG_ON(!mem);
+       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+       return mem->vaddr;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
+
+void videobuf_vmalloc_free(struct videobuf_buffer *buf)
+{
+       struct videobuf_vmalloc_memory *mem = buf->priv;
+
+       /* mmapped memory can't be freed here, otherwise mmapped region
+          would be released, while still needed. In this case, the memory
+          release should happen inside videobuf_vm_close().
+          So, it should free memory only if the memory were allocated for
+          read() operation.
+        */
+       if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
+               return;
+
+       if (!mem)
+               return;
+
+       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+       vfree(mem->vaddr);
+       mem->vaddr = NULL;
+
+       return;
+}
+EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);
+
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
new file mode 100644 (file)
index 0000000..4da3df6
--- /dev/null
@@ -0,0 +1,2380 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *        Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-core.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...)                                    \
+       do {                                                            \
+               if (debug >= level)                                     \
+                       printk(KERN_DEBUG "vb2: " fmt, ## arg);         \
+       } while (0)
+
+#define call_memop(q, op, args...)                                     \
+       (((q)->mem_ops->op) ?                                           \
+               ((q)->mem_ops->op(args)) : 0)
+
+#define call_qop(q, op, args...)                                       \
+       (((q)->ops->op) ? ((q)->ops->op(args)) : 0)
+
+#define V4L2_BUFFER_STATE_FLAGS        (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
+                                V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
+                                V4L2_BUF_FLAG_PREPARED)
+
+/**
+ * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
+ */
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       void *mem_priv;
+       int plane;
+
+       /* Allocate memory for all planes in this buffer */
+       for (plane = 0; plane < vb->num_planes; ++plane) {
+               mem_priv = call_memop(q, alloc, q->alloc_ctx[plane],
+                                     q->plane_sizes[plane]);
+               if (IS_ERR_OR_NULL(mem_priv))
+                       goto free;
+
+               /* Associate allocator private data with this plane */
+               vb->planes[plane].mem_priv = mem_priv;
+               vb->v4l2_planes[plane].length = q->plane_sizes[plane];
+       }
+
+       return 0;
+free:
+       /* Free already allocated memory if one of the allocations failed */
+       for (; plane > 0; --plane) {
+               call_memop(q, put, vb->planes[plane - 1].mem_priv);
+               vb->planes[plane - 1].mem_priv = NULL;
+       }
+
+       return -ENOMEM;
+}
+
+/**
+ * __vb2_buf_mem_free() - free memory of the given buffer
+ */
+static void __vb2_buf_mem_free(struct vb2_buffer *vb)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       unsigned int plane;
+
+       for (plane = 0; plane < vb->num_planes; ++plane) {
+               call_memop(q, put, vb->planes[plane].mem_priv);
+               vb->planes[plane].mem_priv = NULL;
+               dprintk(3, "Freed plane %d of buffer %d\n", plane,
+                       vb->v4l2_buf.index);
+       }
+}
+
+/**
+ * __vb2_buf_userptr_put() - release userspace memory associated with
+ * a USERPTR buffer
+ */
+static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       unsigned int plane;
+
+       for (plane = 0; plane < vb->num_planes; ++plane) {
+               if (vb->planes[plane].mem_priv)
+                       call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+               vb->planes[plane].mem_priv = NULL;
+       }
+}
+
+/**
+ * __setup_offsets() - setup unique offsets ("cookies") for every plane in
+ * every buffer on the queue
+ */
+static void __setup_offsets(struct vb2_queue *q, unsigned int n)
+{
+       unsigned int buffer, plane;
+       struct vb2_buffer *vb;
+       unsigned long off;
+
+       if (q->num_buffers) {
+               struct v4l2_plane *p;
+               vb = q->bufs[q->num_buffers - 1];
+               p = &vb->v4l2_planes[vb->num_planes - 1];
+               off = PAGE_ALIGN(p->m.mem_offset + p->length);
+       } else {
+               off = 0;
+       }
+
+       for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
+               vb = q->bufs[buffer];
+               if (!vb)
+                       continue;
+
+               for (plane = 0; plane < vb->num_planes; ++plane) {
+                       vb->v4l2_planes[plane].length = q->plane_sizes[plane];
+                       vb->v4l2_planes[plane].m.mem_offset = off;
+
+                       dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
+                                       buffer, plane, off);
+
+                       off += vb->v4l2_planes[plane].length;
+                       off = PAGE_ALIGN(off);
+               }
+       }
+}
+
+/**
+ * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
+ * video buffer memory for all buffers/planes on the queue and initializes the
+ * queue
+ *
+ * Returns the number of buffers successfully allocated.
+ */
+static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
+                            unsigned int num_buffers, unsigned int num_planes)
+{
+       unsigned int buffer;
+       struct vb2_buffer *vb;
+       int ret;
+
+       for (buffer = 0; buffer < num_buffers; ++buffer) {
+               /* Allocate videobuf buffer structures */
+               vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
+               if (!vb) {
+                       dprintk(1, "Memory alloc for buffer struct failed\n");
+                       break;
+               }
+
+               /* Length stores number of planes for multiplanar buffers */
+               if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
+                       vb->v4l2_buf.length = num_planes;
+
+               vb->state = VB2_BUF_STATE_DEQUEUED;
+               vb->vb2_queue = q;
+               vb->num_planes = num_planes;
+               vb->v4l2_buf.index = q->num_buffers + buffer;
+               vb->v4l2_buf.type = q->type;
+               vb->v4l2_buf.memory = memory;
+
+               /* Allocate video buffer memory for the MMAP type */
+               if (memory == V4L2_MEMORY_MMAP) {
+                       ret = __vb2_buf_mem_alloc(vb);
+                       if (ret) {
+                               dprintk(1, "Failed allocating memory for "
+                                               "buffer %d\n", buffer);
+                               kfree(vb);
+                               break;
+                       }
+                       /*
+                        * Call the driver-provided buffer initialization
+                        * callback, if given. An error in initialization
+                        * results in queue setup failure.
+                        */
+                       ret = call_qop(q, buf_init, vb);
+                       if (ret) {
+                               dprintk(1, "Buffer %d %p initialization"
+                                       " failed\n", buffer, vb);
+                               __vb2_buf_mem_free(vb);
+                               kfree(vb);
+                               break;
+                       }
+               }
+
+               q->bufs[q->num_buffers + buffer] = vb;
+       }
+
+       __setup_offsets(q, buffer);
+
+       dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
+                       buffer, num_planes);
+
+       return buffer;
+}
+
+/**
+ * __vb2_free_mem() - release all video buffer memory for a given queue
+ */
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
+{
+       unsigned int buffer;
+       struct vb2_buffer *vb;
+
+       for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+            ++buffer) {
+               vb = q->bufs[buffer];
+               if (!vb)
+                       continue;
+
+               /* Free MMAP buffers or release USERPTR buffers */
+               if (q->memory == V4L2_MEMORY_MMAP)
+                       __vb2_buf_mem_free(vb);
+               else
+                       __vb2_buf_userptr_put(vb);
+       }
+}
+
+/**
+ * __vb2_queue_free() - free buffers at the end of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been freed.
+ */
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
+{
+       unsigned int buffer;
+
+       /* Call driver-provided cleanup function for each buffer, if provided */
+       if (q->ops->buf_cleanup) {
+               for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+                    ++buffer) {
+                       if (NULL == q->bufs[buffer])
+                               continue;
+                       q->ops->buf_cleanup(q->bufs[buffer]);
+               }
+       }
+
+       /* Release video buffer memory */
+       __vb2_free_mem(q, buffers);
+
+       /* Free videobuf buffers */
+       for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+            ++buffer) {
+               kfree(q->bufs[buffer]);
+               q->bufs[buffer] = NULL;
+       }
+
+       q->num_buffers -= buffers;
+       if (!q->num_buffers)
+               q->memory = 0;
+       INIT_LIST_HEAD(&q->queued_list);
+}
+
+/**
+ * __verify_planes_array() - verify that the planes array passed in struct
+ * v4l2_buffer from userspace can be safely used
+ */
+static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+       /* Is memory for copying plane information present? */
+       if (NULL == b->m.planes) {
+               dprintk(1, "Multi-planar buffer passed but "
+                          "planes array not provided\n");
+               return -EINVAL;
+       }
+
+       if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
+               dprintk(1, "Incorrect planes array length, "
+                          "expected %d, got %d\n", vb->num_planes, b->length);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * __buffer_in_use() - return true if the buffer is in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+       unsigned int plane;
+       for (plane = 0; plane < vb->num_planes; ++plane) {
+               void *mem_priv = vb->planes[plane].mem_priv;
+               /*
+                * If num_users() has not been provided, call_memop
+                * will return 0, apparently nobody cares about this
+                * case anyway. If num_users() returns more than 1,
+                * we are not the only user of the plane's memory.
+                */
+               if (mem_priv && call_memop(q, num_users, mem_priv) > 1)
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * __buffers_in_use() - return true if any buffers on the queue are in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffers_in_use(struct vb2_queue *q)
+{
+       unsigned int buffer;
+       for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+               if (__buffer_in_use(q, q->bufs[buffer]))
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
+ * returned to userspace
+ */
+static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       int ret;
+
+       /* Copy back data such as timestamp, flags, etc. */
+       memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
+       b->reserved2 = vb->v4l2_buf.reserved2;
+       b->reserved = vb->v4l2_buf.reserved;
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
+               ret = __verify_planes_array(vb, b);
+               if (ret)
+                       return ret;
+
+               /*
+                * Fill in plane-related data if userspace provided an array
+                * for it. The memory and size is verified above.
+                */
+               memcpy(b->m.planes, vb->v4l2_planes,
+                       b->length * sizeof(struct v4l2_plane));
+       } else {
+               /*
+                * We use length and offset in v4l2_planes array even for
+                * single-planar buffers, but userspace does not.
+                */
+               b->length = vb->v4l2_planes[0].length;
+               b->bytesused = vb->v4l2_planes[0].bytesused;
+               if (q->memory == V4L2_MEMORY_MMAP)
+                       b->m.offset = vb->v4l2_planes[0].m.mem_offset;
+               else if (q->memory == V4L2_MEMORY_USERPTR)
+                       b->m.userptr = vb->v4l2_planes[0].m.userptr;
+       }
+
+       /*
+        * Clear any buffer state related flags.
+        */
+       b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
+
+       switch (vb->state) {
+       case VB2_BUF_STATE_QUEUED:
+       case VB2_BUF_STATE_ACTIVE:
+               b->flags |= V4L2_BUF_FLAG_QUEUED;
+               break;
+       case VB2_BUF_STATE_ERROR:
+               b->flags |= V4L2_BUF_FLAG_ERROR;
+               /* fall through */
+       case VB2_BUF_STATE_DONE:
+               b->flags |= V4L2_BUF_FLAG_DONE;
+               break;
+       case VB2_BUF_STATE_PREPARED:
+               b->flags |= V4L2_BUF_FLAG_PREPARED;
+               break;
+       case VB2_BUF_STATE_DEQUEUED:
+               /* nothing */
+               break;
+       }
+
+       if (__buffer_in_use(q, vb))
+               b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+       return 0;
+}
+
+/**
+ * vb2_querybuf() - query video buffer information
+ * @q:         videobuf queue
+ * @b:         buffer struct passed from userspace to vidioc_querybuf handler
+ *             in driver
+ *
+ * Should be called from vidioc_querybuf ioctl handler in driver.
+ * This function will verify the passed v4l2_buffer structure and fill the
+ * relevant information for the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_querybuf handler in driver.
+ */
+int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+       struct vb2_buffer *vb;
+
+       if (b->type != q->type) {
+               dprintk(1, "querybuf: wrong buffer type\n");
+               return -EINVAL;
+       }
+
+       if (b->index >= q->num_buffers) {
+               dprintk(1, "querybuf: buffer index out of range\n");
+               return -EINVAL;
+       }
+       vb = q->bufs[b->index];
+
+       return __fill_v4l2_buffer(vb, b);
+}
+EXPORT_SYMBOL(vb2_querybuf);
+
+/**
+ * __verify_userptr_ops() - verify that all memory operations required for
+ * USERPTR queue type have been provided
+ */
+static int __verify_userptr_ops(struct vb2_queue *q)
+{
+       if (!(q->io_modes & VB2_USERPTR) || !q->mem_ops->get_userptr ||
+           !q->mem_ops->put_userptr)
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * __verify_mmap_ops() - verify that all memory operations required for
+ * MMAP queue type have been provided
+ */
+static int __verify_mmap_ops(struct vb2_queue *q)
+{
+       if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc ||
+           !q->mem_ops->put || !q->mem_ops->mmap)
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * __verify_memory_type() - Check whether the memory type and buffer type
+ * passed to a buffer operation are compatible with the queue.
+ */
+static int __verify_memory_type(struct vb2_queue *q,
+               enum v4l2_memory memory, enum v4l2_buf_type type)
+{
+       if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) {
+               dprintk(1, "reqbufs: unsupported memory type\n");
+               return -EINVAL;
+       }
+
+       if (type != q->type) {
+               dprintk(1, "reqbufs: requested type is incorrect\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Make sure all the required memory ops for given memory type
+        * are available.
+        */
+       if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+               dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+               return -EINVAL;
+       }
+
+       if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+               dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Place the busy tests at the end: -EBUSY can be ignored when
+        * create_bufs is called with count == 0, but count == 0 should still
+        * do the memory and type validation.
+        */
+       if (q->fileio) {
+               dprintk(1, "reqbufs: file io in progress\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+/**
+ * __reqbufs() - Initiate streaming
+ * @q:         videobuf2 queue
+ * @req:       struct passed from userspace to vidioc_reqbufs handler in driver
+ *
+ * Should be called from vidioc_reqbufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies streaming parameters passed from the userspace,
+ * 2) sets up the queue,
+ * 3) negotiates number of buffers and planes per buffer with the driver
+ *    to be used during streaming,
+ * 4) allocates internal buffer structures (struct vb2_buffer), according to
+ *    the agreed parameters,
+ * 5) for MMAP memory type, allocates actual video memory, using the
+ *    memory handling/allocation routines provided during queue initialization
+ *
+ * If req->count is 0, all the memory will be freed instead.
+ * If the queue has been allocated previously (by a previous vb2_reqbufs) call
+ * and the queue is not busy, memory will be reallocated.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_reqbufs handler in driver.
+ */
+static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+       unsigned int num_buffers, allocated_buffers, num_planes = 0;
+       int ret;
+
+       if (q->streaming) {
+               dprintk(1, "reqbufs: streaming active\n");
+               return -EBUSY;
+       }
+
+       if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
+               /*
+                * We already have buffers allocated, so first check if they
+                * are not in use and can be freed.
+                */
+               if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
+                       dprintk(1, "reqbufs: memory in use, cannot free\n");
+                       return -EBUSY;
+               }
+
+               __vb2_queue_free(q, q->num_buffers);
+
+               /*
+                * In case of REQBUFS(0) return immediately without calling
+                * driver's queue_setup() callback and allocating resources.
+                */
+               if (req->count == 0)
+                       return 0;
+       }
+
+       /*
+        * Make sure the requested values and current defaults are sane.
+        */
+       num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
+       memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+       memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+       q->memory = req->memory;
+
+       /*
+        * Ask the driver how many buffers and planes per buffer it requires.
+        * Driver also sets the size and allocator context for each plane.
+        */
+       ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
+                      q->plane_sizes, q->alloc_ctx);
+       if (ret)
+               return ret;
+
+       /* Finally, allocate buffers and video memory */
+       ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
+       if (ret == 0) {
+               dprintk(1, "Memory allocation failed\n");
+               return -ENOMEM;
+       }
+
+       allocated_buffers = ret;
+
+       /*
+        * Check if driver can handle the allocated number of buffers.
+        */
+       if (allocated_buffers < num_buffers) {
+               num_buffers = allocated_buffers;
+
+               ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
+                              &num_planes, q->plane_sizes, q->alloc_ctx);
+
+               if (!ret && allocated_buffers < num_buffers)
+                       ret = -ENOMEM;
+
+               /*
+                * Either the driver has accepted a smaller number of buffers,
+                * or .queue_setup() returned an error
+                */
+       }
+
+       q->num_buffers = allocated_buffers;
+
+       if (ret < 0) {
+               __vb2_queue_free(q, allocated_buffers);
+               return ret;
+       }
+
+       /*
+        * Return the number of successfully allocated buffers
+        * to the userspace.
+        */
+       req->count = allocated_buffers;
+
+       return 0;
+}
+
+/**
+ * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and
+ * type values.
+ * @q:         videobuf2 queue
+ * @req:       struct passed from userspace to vidioc_reqbufs handler in driver
+ */
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+       int ret = __verify_memory_type(q, req->memory, req->type);
+
+       return ret ? ret : __reqbufs(q, req);
+}
+EXPORT_SYMBOL_GPL(vb2_reqbufs);
+
+/**
+ * __create_bufs() - Allocate buffers and any required auxiliary structs
+ * @q:         videobuf2 queue
+ * @create:    creation parameters, passed from userspace to vidioc_create_bufs
+ *             handler in driver
+ *
+ * Should be called from vidioc_create_bufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies parameter sanity
+ * 2) calls the .queue_setup() queue operation
+ * 3) performs any necessary memory allocations
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_create_bufs handler in driver.
+ */
+static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+       unsigned int num_planes = 0, num_buffers, allocated_buffers;
+       int ret;
+
+       if (q->num_buffers == VIDEO_MAX_FRAME) {
+               dprintk(1, "%s(): maximum number of buffers already allocated\n",
+                       __func__);
+               return -ENOBUFS;
+       }
+
+       if (!q->num_buffers) {
+               memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+               memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+               q->memory = create->memory;
+       }
+
+       num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
+
+       /*
+        * Ask the driver, whether the requested number of buffers, planes per
+        * buffer and their sizes are acceptable
+        */
+       ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+                      &num_planes, q->plane_sizes, q->alloc_ctx);
+       if (ret)
+               return ret;
+
+       /* Finally, allocate buffers and video memory */
+       ret = __vb2_queue_alloc(q, create->memory, num_buffers,
+                               num_planes);
+       if (ret == 0) {
+               dprintk(1, "Memory allocation failed\n");
+               return -ENOMEM;
+       }
+
+       allocated_buffers = ret;
+
+       /*
+        * Check if driver can handle the so far allocated number of buffers.
+        */
+       if (ret < num_buffers) {
+               num_buffers = ret;
+
+               /*
+                * q->num_buffers contains the total number of buffers, that the
+                * queue driver has set up
+                */
+               ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+                              &num_planes, q->plane_sizes, q->alloc_ctx);
+
+               if (!ret && allocated_buffers < num_buffers)
+                       ret = -ENOMEM;
+
+               /*
+                * Either the driver has accepted a smaller number of buffers,
+                * or .queue_setup() returned an error
+                */
+       }
+
+       q->num_buffers += allocated_buffers;
+
+       if (ret < 0) {
+               __vb2_queue_free(q, allocated_buffers);
+               return -ENOMEM;
+       }
+
+       /*
+        * Return the number of successfully allocated buffers
+        * to the userspace.
+        */
+       create->count = allocated_buffers;
+
+       return 0;
+}
+
+/**
+ * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the
+ * memory and type values.
+ * @q:         videobuf2 queue
+ * @create:    creation parameters, passed from userspace to vidioc_create_bufs
+ *             handler in driver
+ */
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+       int ret = __verify_memory_type(q, create->memory, create->format.type);
+
+       create->index = q->num_buffers;
+       if (create->count == 0)
+               return ret != -EBUSY ? ret : 0;
+       return ret ? ret : __create_bufs(q, create);
+}
+EXPORT_SYMBOL_GPL(vb2_create_bufs);
+
+/**
+ * vb2_plane_vaddr() - Return a kernel virtual address of a given plane
+ * @vb:                vb2_buffer to which the plane in question belongs to
+ * @plane_no:  plane number for which the address is to be returned
+ *
+ * This function returns a kernel virtual address of a given plane if
+ * such a mapping exist, NULL otherwise.
+ */
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+
+       if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
+               return NULL;
+
+       return call_memop(q, vaddr, vb->planes[plane_no].mem_priv);
+
+}
+EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
+
+/**
+ * vb2_plane_cookie() - Return allocator specific cookie for the given plane
+ * @vb:                vb2_buffer to which the plane in question belongs to
+ * @plane_no:  plane number for which the cookie is to be returned
+ *
+ * This function returns an allocator specific cookie for a given plane if
+ * available, NULL otherwise. The allocator should provide some simple static
+ * inline function, which would convert this cookie to the allocator specific
+ * type that can be used directly by the driver to access the buffer. This can
+ * be for example physical address, pointer to scatter list or IOMMU mapping.
+ */
+void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+
+       if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
+               return NULL;
+
+       return call_memop(q, cookie, vb->planes[plane_no].mem_priv);
+}
+EXPORT_SYMBOL_GPL(vb2_plane_cookie);
+
+/**
+ * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
+ * @vb:                vb2_buffer returned from the driver
+ * @state:     either VB2_BUF_STATE_DONE if the operation finished successfully
+ *             or VB2_BUF_STATE_ERROR if the operation finished with an error
+ *
+ * This function should be called by the driver after a hardware operation on
+ * a buffer is finished and the buffer may be returned to userspace. The driver
+ * cannot use this buffer anymore until it is queued back to it by videobuf
+ * by the means of buf_queue callback. Only buffers previously queued to the
+ * driver by buf_queue can be passed to this function.
+ */
+void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       unsigned long flags;
+
+       if (vb->state != VB2_BUF_STATE_ACTIVE)
+               return;
+
+       if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
+               return;
+
+       dprintk(4, "Done processing on buffer %d, state: %d\n",
+                       vb->v4l2_buf.index, vb->state);
+
+       /* Add the buffer to the done buffers list */
+       spin_lock_irqsave(&q->done_lock, flags);
+       vb->state = state;
+       list_add_tail(&vb->done_entry, &q->done_list);
+       atomic_dec(&q->queued_count);
+       spin_unlock_irqrestore(&q->done_lock, flags);
+
+       /* Inform any processes that may be waiting for buffers */
+       wake_up(&q->done_wq);
+}
+EXPORT_SYMBOL_GPL(vb2_buffer_done);
+
+/**
+ * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
+ * a v4l2_buffer by the userspace
+ */
+static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
+                               struct v4l2_plane *v4l2_planes)
+{
+       unsigned int plane;
+       int ret;
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+               /*
+                * Verify that the userspace gave us a valid array for
+                * plane information.
+                */
+               ret = __verify_planes_array(vb, b);
+               if (ret)
+                       return ret;
+
+               /* Fill in driver-provided information for OUTPUT types */
+               if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+                       /*
+                        * Will have to go up to b->length when API starts
+                        * accepting variable number of planes.
+                        */
+                       for (plane = 0; plane < vb->num_planes; ++plane) {
+                               v4l2_planes[plane].bytesused =
+                                       b->m.planes[plane].bytesused;
+                               v4l2_planes[plane].data_offset =
+                                       b->m.planes[plane].data_offset;
+                       }
+               }
+
+               if (b->memory == V4L2_MEMORY_USERPTR) {
+                       for (plane = 0; plane < vb->num_planes; ++plane) {
+                               v4l2_planes[plane].m.userptr =
+                                       b->m.planes[plane].m.userptr;
+                               v4l2_planes[plane].length =
+                                       b->m.planes[plane].length;
+                       }
+               }
+       } else {
+               /*
+                * Single-planar buffers do not use planes array,
+                * so fill in relevant v4l2_buffer struct fields instead.
+                * In videobuf we use our internal V4l2_planes struct for
+                * single-planar buffers as well, for simplicity.
+                */
+               if (V4L2_TYPE_IS_OUTPUT(b->type))
+                       v4l2_planes[0].bytesused = b->bytesused;
+
+               if (b->memory == V4L2_MEMORY_USERPTR) {
+                       v4l2_planes[0].m.userptr = b->m.userptr;
+                       v4l2_planes[0].length = b->length;
+               }
+       }
+
+       vb->v4l2_buf.field = b->field;
+       vb->v4l2_buf.timestamp = b->timestamp;
+       vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
+
+       return 0;
+}
+
+/**
+ * __qbuf_userptr() - handle qbuf of a USERPTR buffer
+ */
+static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+       struct v4l2_plane planes[VIDEO_MAX_PLANES];
+       struct vb2_queue *q = vb->vb2_queue;
+       void *mem_priv;
+       unsigned int plane;
+       int ret;
+       int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+
+       /* Verify and copy relevant information provided by the userspace */
+       ret = __fill_vb2_buffer(vb, b, planes);
+       if (ret)
+               return ret;
+
+       for (plane = 0; plane < vb->num_planes; ++plane) {
+               /* Skip the plane if already verified */
+               if (vb->v4l2_planes[plane].m.userptr &&
+                   vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
+                   && vb->v4l2_planes[plane].length == planes[plane].length)
+                       continue;
+
+               dprintk(3, "qbuf: userspace address for plane %d changed, "
+                               "reacquiring memory\n", plane);
+
+               /* Check if the provided plane buffer is large enough */
+               if (planes[plane].length < q->plane_sizes[plane]) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               /* Release previously acquired memory if present */
+               if (vb->planes[plane].mem_priv)
+                       call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+
+               vb->planes[plane].mem_priv = NULL;
+               vb->v4l2_planes[plane].m.userptr = 0;
+               vb->v4l2_planes[plane].length = 0;
+
+               /* Acquire each plane's memory */
+               mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
+                                     planes[plane].m.userptr,
+                                     planes[plane].length, write);
+               if (IS_ERR_OR_NULL(mem_priv)) {
+                       dprintk(1, "qbuf: failed acquiring userspace "
+                                               "memory for plane %d\n", plane);
+                       ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
+                       goto err;
+               }
+               vb->planes[plane].mem_priv = mem_priv;
+       }
+
+       /*
+        * Call driver-specific initialization on the newly acquired buffer,
+        * if provided.
+        */
+       ret = call_qop(q, buf_init, vb);
+       if (ret) {
+               dprintk(1, "qbuf: buffer initialization failed\n");
+               goto err;
+       }
+
+       /*
+        * Now that everything is in order, copy relevant information
+        * provided by userspace.
+        */
+       for (plane = 0; plane < vb->num_planes; ++plane)
+               vb->v4l2_planes[plane] = planes[plane];
+
+       return 0;
+err:
+       /* In case of errors, release planes that were already acquired */
+       for (plane = 0; plane < vb->num_planes; ++plane) {
+               if (vb->planes[plane].mem_priv)
+                       call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+               vb->planes[plane].mem_priv = NULL;
+               vb->v4l2_planes[plane].m.userptr = 0;
+               vb->v4l2_planes[plane].length = 0;
+       }
+
+       return ret;
+}
+
+/**
+ * __qbuf_mmap() - handle qbuf of an MMAP buffer
+ */
+static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+       return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
+}
+
+/**
+ * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
+ */
+static void __enqueue_in_driver(struct vb2_buffer *vb)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+
+       vb->state = VB2_BUF_STATE_ACTIVE;
+       atomic_inc(&q->queued_count);
+       q->ops->buf_queue(vb);
+}
+
+static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+       struct vb2_queue *q = vb->vb2_queue;
+       int ret;
+
+       switch (q->memory) {
+       case V4L2_MEMORY_MMAP:
+               ret = __qbuf_mmap(vb, b);
+               break;
+       case V4L2_MEMORY_USERPTR:
+               ret = __qbuf_userptr(vb, b);
+               break;
+       default:
+               WARN(1, "Invalid queue type\n");
+               ret = -EINVAL;
+       }
+
+       if (!ret)
+               ret = call_qop(q, buf_prepare, vb);
+       if (ret)
+               dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
+       else
+               vb->state = VB2_BUF_STATE_PREPARED;
+
+       return ret;
+}
+
+/**
+ * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
+ * @q:         videobuf2 queue
+ * @b:         buffer structure passed from userspace to vidioc_prepare_buf
+ *             handler in driver
+ *
+ * Should be called from vidioc_prepare_buf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_prepare callback in the driver (if provided), in which
+ *    driver-specific buffer initialization can be performed,
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_prepare_buf handler in driver.
+ */
+int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+       struct vb2_buffer *vb;
+       int ret;
+
+       if (q->fileio) {
+               dprintk(1, "%s(): file io in progress\n", __func__);
+               return -EBUSY;
+       }
+
+       if (b->type != q->type) {
+               dprintk(1, "%s(): invalid buffer type\n", __func__);
+               return -EINVAL;
+       }
+
+       if (b->index >= q->num_buffers) {
+               dprintk(1, "%s(): buffer index out of range\n", __func__);
+               return -EINVAL;
+       }
+
+       vb = q->bufs[b->index];
+       if (NULL == vb) {
+               /* Should never happen */
+               dprintk(1, "%s(): buffer is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       if (b->memory != q->memory) {
+               dprintk(1, "%s(): invalid memory type\n", __func__);
+               return -EINVAL;
+       }
+
+       if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+               dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
+               return -EINVAL;
+       }
+
+       ret = __buf_prepare(vb, b);
+       if (ret < 0)
+               return ret;
+
+       __fill_v4l2_buffer(vb, b);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_prepare_buf);
+
+/**
+ * vb2_qbuf() - Queue a buffer from userspace
+ * @q:         videobuf2 queue
+ * @b:         buffer structure passed from userspace to vidioc_qbuf handler
+ *             in driver
+ *
+ * Should be called from vidioc_qbuf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) if necessary, calls buf_prepare callback in the driver (if provided), in
+ *    which driver-specific buffer initialization can be performed,
+ * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
+ *    callback for processing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_qbuf handler in driver.
+ */
+int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+       struct rw_semaphore *mmap_sem = NULL;
+       struct vb2_buffer *vb;
+       int ret = 0;
+
+       /*
+        * In case of user pointer buffers vb2 allocator needs to get direct
+        * access to userspace pages. This requires getting read access on
+        * mmap semaphore in the current process structure. The same
+        * semaphore is taken before calling mmap operation, while both mmap
+        * and qbuf are called by the driver or v4l2 core with driver's lock
+        * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in
+        * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core
+        * release driver's lock, takes mmap_sem and then takes again driver's
+        * lock.
+        *
+        * To avoid race with other vb2 calls, which might be called after
+        * releasing driver's lock, this operation is performed at the
+        * beggining of qbuf processing. This way the queue status is
+        * consistent after getting driver's lock back.
+        */
+       if (q->memory == V4L2_MEMORY_USERPTR) {
+               mmap_sem = &current->mm->mmap_sem;
+               call_qop(q, wait_prepare, q);
+               down_read(mmap_sem);
+               call_qop(q, wait_finish, q);
+       }
+
+       if (q->fileio) {
+               dprintk(1, "qbuf: file io in progress\n");
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       if (b->type != q->type) {
+               dprintk(1, "qbuf: invalid buffer type\n");
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       if (b->index >= q->num_buffers) {
+               dprintk(1, "qbuf: buffer index out of range\n");
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       vb = q->bufs[b->index];
+       if (NULL == vb) {
+               /* Should never happen */
+               dprintk(1, "qbuf: buffer is NULL\n");
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       if (b->memory != q->memory) {
+               dprintk(1, "qbuf: invalid memory type\n");
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       switch (vb->state) {
+       case VB2_BUF_STATE_DEQUEUED:
+               ret = __buf_prepare(vb, b);
+               if (ret)
+                       goto unlock;
+       case VB2_BUF_STATE_PREPARED:
+               break;
+       default:
+               dprintk(1, "qbuf: buffer already in use\n");
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       /*
+        * Add to the queued buffers list, a buffer will stay on it until
+        * dequeued in dqbuf.
+        */
+       list_add_tail(&vb->queued_entry, &q->queued_list);
+       vb->state = VB2_BUF_STATE_QUEUED;
+
+       /*
+        * If already streaming, give the buffer to driver for processing.
+        * If not, the buffer will be given to driver on next streamon.
+        */
+       if (q->streaming)
+               __enqueue_in_driver(vb);
+
+       /* Fill buffer information for the userspace */
+       __fill_v4l2_buffer(vb, b);
+
+       dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
+unlock:
+       if (mmap_sem)
+               up_read(mmap_sem);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_qbuf);
+
+/**
+ * __vb2_wait_for_done_vb() - wait for a buffer to become available
+ * for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
+{
+       /*
+        * All operations on vb_done_list are performed under done_lock
+        * spinlock protection. However, buffers may be removed from
+        * it and returned to userspace only while holding both driver's
+        * lock and the done_lock spinlock. Thus we can be sure that as
+        * long as we hold the driver's lock, the list will remain not
+        * empty if list_empty() check succeeds.
+        */
+
+       for (;;) {
+               int ret;
+
+               if (!q->streaming) {
+                       dprintk(1, "Streaming off, will not wait for buffers\n");
+                       return -EINVAL;
+               }
+
+               if (!list_empty(&q->done_list)) {
+                       /*
+                        * Found a buffer that we were waiting for.
+                        */
+                       break;
+               }
+
+               if (nonblocking) {
+                       dprintk(1, "Nonblocking and no buffers to dequeue, "
+                                                               "will not wait\n");
+                       return -EAGAIN;
+               }
+
+               /*
+                * We are streaming and blocking, wait for another buffer to
+                * become ready or for streamoff. Driver's lock is released to
+                * allow streamoff or qbuf to be called while waiting.
+                */
+               call_qop(q, wait_prepare, q);
+
+               /*
+                * All locks have been released, it is safe to sleep now.
+                */
+               dprintk(3, "Will sleep waiting for buffers\n");
+               ret = wait_event_interruptible(q->done_wq,
+                               !list_empty(&q->done_list) || !q->streaming);
+
+               /*
+                * We need to reevaluate both conditions again after reacquiring
+                * the locks or return an error if one occurred.
+                */
+               call_qop(q, wait_finish, q);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+/**
+ * __vb2_get_done_vb() - get a buffer ready for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
+                               int nonblocking)
+{
+       unsigned long flags;
+       int ret;
+
+       /*
+        * Wait for at least one buffer to become available on the done_list.
+        */
+       ret = __vb2_wait_for_done_vb(q, nonblocking);
+       if (ret)
+               return ret;
+
+       /*
+        * Driver's lock has been held since we last verified that done_list
+        * is not empty, so no need for another list_empty(done_list) check.
+        */
+       spin_lock_irqsave(&q->done_lock, flags);
+       *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
+       list_del(&(*vb)->done_entry);
+       spin_unlock_irqrestore(&q->done_lock, flags);
+
+       return 0;
+}
+
+/**
+ * vb2_wait_for_all_buffers() - wait until all buffers are given back to vb2
+ * @q:         videobuf2 queue
+ *
+ * This function will wait until all buffers that have been given to the driver
+ * by buf_queue() are given back to vb2 with vb2_buffer_done(). It doesn't call
+ * wait_prepare, wait_finish pair. It is intended to be called with all locks
+ * taken, for example from stop_streaming() callback.
+ */
+int vb2_wait_for_all_buffers(struct vb2_queue *q)
+{
+       if (!q->streaming) {
+               dprintk(1, "Streaming off, will not wait for buffers\n");
+               return -EINVAL;
+       }
+
+       wait_event(q->done_wq, !atomic_read(&q->queued_count));
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
+
+/**
+ * vb2_dqbuf() - Dequeue a buffer to the userspace
+ * @q:         videobuf2 queue
+ * @b:         buffer structure passed from userspace to vidioc_dqbuf handler
+ *             in driver
+ * @nonblocking: if true, this call will not sleep waiting for a buffer if no
+ *              buffers ready for dequeuing are present. Normally the driver
+ *              would be passing (file->f_flags & O_NONBLOCK) here
+ *
+ * Should be called from vidioc_dqbuf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_finish callback in the driver (if provided), in which
+ *    driver can perform any additional operations that may be required before
+ *    returning the buffer to userspace, such as cache sync,
+ * 3) the buffer struct members are filled with relevant information for
+ *    the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_dqbuf handler in driver.
+ */
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
+{
+       struct vb2_buffer *vb = NULL;
+       int ret;
+
+       if (q->fileio) {
+               dprintk(1, "dqbuf: file io in progress\n");
+               return -EBUSY;
+       }
+
+       if (b->type != q->type) {
+               dprintk(1, "dqbuf: invalid buffer type\n");
+               return -EINVAL;
+       }
+
+       ret = __vb2_get_done_vb(q, &vb, nonblocking);
+       if (ret < 0) {
+               dprintk(1, "dqbuf: error getting next done buffer\n");
+               return ret;
+       }
+
+       ret = call_qop(q, buf_finish, vb);
+       if (ret) {
+               dprintk(1, "dqbuf: buffer finish failed\n");
+               return ret;
+       }
+
+       switch (vb->state) {
+       case VB2_BUF_STATE_DONE:
+               dprintk(3, "dqbuf: Returning done buffer\n");
+               break;
+       case VB2_BUF_STATE_ERROR:
+               dprintk(3, "dqbuf: Returning done buffer with errors\n");
+               break;
+       default:
+               dprintk(1, "dqbuf: Invalid buffer state\n");
+               return -EINVAL;
+       }
+
+       /* Fill buffer information for the userspace */
+       __fill_v4l2_buffer(vb, b);
+       /* Remove from videobuf queue */
+       list_del(&vb->queued_entry);
+
+       dprintk(1, "dqbuf of buffer %d, with state %d\n",
+                       vb->v4l2_buf.index, vb->state);
+
+       vb->state = VB2_BUF_STATE_DEQUEUED;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_dqbuf);
+
+/**
+ * __vb2_queue_cancel() - cancel and stop (pause) streaming
+ *
+ * Removes all queued buffers from driver's queue and all buffers queued by
+ * userspace from videobuf's queue. Returns to state after reqbufs.
+ */
+static void __vb2_queue_cancel(struct vb2_queue *q)
+{
+       unsigned int i;
+
+       /*
+        * Tell driver to stop all transactions and release all queued
+        * buffers.
+        */
+       if (q->streaming)
+               call_qop(q, stop_streaming, q);
+       q->streaming = 0;
+
+       /*
+        * Remove all buffers from videobuf's list...
+        */
+       INIT_LIST_HEAD(&q->queued_list);
+       /*
+        * ...and done list; userspace will not receive any buffers it
+        * has not already dequeued before initiating cancel.
+        */
+       INIT_LIST_HEAD(&q->done_list);
+       atomic_set(&q->queued_count, 0);
+       wake_up_all(&q->done_wq);
+
+       /*
+        * Reinitialize all buffers for next use.
+        */
+       for (i = 0; i < q->num_buffers; ++i)
+               q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+}
+
+/**
+ * vb2_streamon - start streaming
+ * @q:         videobuf2 queue
+ * @type:      type argument passed from userspace to vidioc_streamon handler
+ *
+ * Should be called from vidioc_streamon handler of a driver.
+ * This function:
+ * 1) verifies current state
+ * 2) passes any previously queued buffers to the driver and starts streaming
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamon handler in the driver.
+ */
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+       struct vb2_buffer *vb;
+       int ret;
+
+       if (q->fileio) {
+               dprintk(1, "streamon: file io in progress\n");
+               return -EBUSY;
+       }
+
+       if (type != q->type) {
+               dprintk(1, "streamon: invalid stream type\n");
+               return -EINVAL;
+       }
+
+       if (q->streaming) {
+               dprintk(1, "streamon: already streaming\n");
+               return -EBUSY;
+       }
+
+       /*
+        * If any buffers were queued before streamon,
+        * we can now pass them to driver for processing.
+        */
+       list_for_each_entry(vb, &q->queued_list, queued_entry)
+               __enqueue_in_driver(vb);
+
+       /*
+        * Let driver notice that streaming state has been enabled.
+        */
+       ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
+       if (ret) {
+               dprintk(1, "streamon: driver refused to start streaming\n");
+               __vb2_queue_cancel(q);
+               return ret;
+       }
+
+       q->streaming = 1;
+
+       dprintk(3, "Streamon successful\n");
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_streamon);
+
+
+/**
+ * vb2_streamoff - stop streaming
+ * @q:         videobuf2 queue
+ * @type:      type argument passed from userspace to vidioc_streamoff handler
+ *
+ * Should be called from vidioc_streamoff handler of a driver.
+ * This function:
+ * 1) verifies current state,
+ * 2) stop streaming and dequeues any queued buffers, including those previously
+ *    passed to the driver (after waiting for the driver to finish).
+ *
+ * This call can be used for pausing playback.
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamoff handler in the driver
+ */
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+       if (q->fileio) {
+               dprintk(1, "streamoff: file io in progress\n");
+               return -EBUSY;
+       }
+
+       if (type != q->type) {
+               dprintk(1, "streamoff: invalid stream type\n");
+               return -EINVAL;
+       }
+
+       if (!q->streaming) {
+               dprintk(1, "streamoff: not streaming\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Cancel will pause streaming and remove all buffers from the driver
+        * and videobuf, effectively returning control over them to userspace.
+        */
+       __vb2_queue_cancel(q);
+
+       dprintk(3, "Streamoff successful\n");
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_streamoff);
+
+/**
+ * __find_plane_by_offset() - find plane associated with the given offset off
+ */
+static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
+                       unsigned int *_buffer, unsigned int *_plane)
+{
+       struct vb2_buffer *vb;
+       unsigned int buffer, plane;
+
+       /*
+        * Go over all buffers and their planes, comparing the given offset
+        * with an offset assigned to each plane. If a match is found,
+        * return its buffer and plane numbers.
+        */
+       for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+               vb = q->bufs[buffer];
+
+               for (plane = 0; plane < vb->num_planes; ++plane) {
+                       if (vb->v4l2_planes[plane].m.mem_offset == off) {
+                               *_buffer = buffer;
+                               *_plane = plane;
+                               return 0;
+                       }
+               }
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * vb2_mmap() - map video buffers into application address space
+ * @q:         videobuf2 queue
+ * @vma:       vma passed to the mmap file operation handler in the driver
+ *
+ * Should be called from mmap file operation handler of a driver.
+ * This function maps one plane of one of the available video buffers to
+ * userspace. To map whole video memory allocated on reqbufs, this function
+ * has to be called once per each plane per each buffer previously allocated.
+ *
+ * When the userspace application calls mmap, it passes to it an offset returned
+ * to it earlier by the means of vidioc_querybuf handler. That offset acts as
+ * a "cookie", which is then used to identify the plane to be mapped.
+ * This function finds a plane with a matching offset and a mapping is performed
+ * by the means of a provided memory operation.
+ *
+ * The return values from this function are intended to be directly returned
+ * from the mmap handler in driver.
+ */
+int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
+{
+       unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+       struct vb2_buffer *vb;
+       unsigned int buffer, plane;
+       int ret;
+
+       if (q->memory != V4L2_MEMORY_MMAP) {
+               dprintk(1, "Queue is not currently set up for mmap\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Check memory area access mode.
+        */
+       if (!(vma->vm_flags & VM_SHARED)) {
+               dprintk(1, "Invalid vma flags, VM_SHARED needed\n");
+               return -EINVAL;
+       }
+       if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+               if (!(vma->vm_flags & VM_WRITE)) {
+                       dprintk(1, "Invalid vma flags, VM_WRITE needed\n");
+                       return -EINVAL;
+               }
+       } else {
+               if (!(vma->vm_flags & VM_READ)) {
+                       dprintk(1, "Invalid vma flags, VM_READ needed\n");
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * Find the plane corresponding to the offset passed by userspace.
+        */
+       ret = __find_plane_by_offset(q, off, &buffer, &plane);
+       if (ret)
+               return ret;
+
+       vb = q->bufs[buffer];
+
+       ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma);
+       if (ret)
+               return ret;
+
+       dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+                                   unsigned long addr,
+                                   unsigned long len,
+                                   unsigned long pgoff,
+                                   unsigned long flags)
+{
+       unsigned long off = pgoff << PAGE_SHIFT;
+       struct vb2_buffer *vb;
+       unsigned int buffer, plane;
+       int ret;
+
+       if (q->memory != V4L2_MEMORY_MMAP) {
+               dprintk(1, "Queue is not currently set up for mmap\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Find the plane corresponding to the offset passed by userspace.
+        */
+       ret = __find_plane_by_offset(q, off, &buffer, &plane);
+       if (ret)
+               return ret;
+
+       vb = q->bufs[buffer];
+
+       return (unsigned long)vb2_plane_vaddr(vb, plane);
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
+static int __vb2_init_fileio(struct vb2_queue *q, int read);
+static int __vb2_cleanup_fileio(struct vb2_queue *q);
+
+/**
+ * vb2_poll() - implements poll userspace operation
+ * @q:         videobuf2 queue
+ * @file:      file argument passed to the poll file operation handler
+ * @wait:      wait argument passed to the poll file operation handler
+ *
+ * This function implements poll file operation handler for a driver.
+ * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will
+ * be informed that the file descriptor of a video device is available for
+ * reading.
+ * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
+ * will be reported as available for writing.
+ *
+ * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any
+ * pending events.
+ *
+ * The return values from this function are intended to be directly returned
+ * from poll handler in driver.
+ */
+unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
+{
+       struct video_device *vfd = video_devdata(file);
+       unsigned long req_events = poll_requested_events(wait);
+       struct vb2_buffer *vb = NULL;
+       unsigned int res = 0;
+       unsigned long flags;
+
+       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+               struct v4l2_fh *fh = file->private_data;
+
+               if (v4l2_event_pending(fh))
+                       res = POLLPRI;
+               else if (req_events & POLLPRI)
+                       poll_wait(file, &fh->wait, wait);
+       }
+
+       /*
+        * Start file I/O emulator only if streaming API has not been used yet.
+        */
+       if (q->num_buffers == 0 && q->fileio == NULL) {
+               if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
+                               (req_events & (POLLIN | POLLRDNORM))) {
+                       if (__vb2_init_fileio(q, 1))
+                               return res | POLLERR;
+               }
+               if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
+                               (req_events & (POLLOUT | POLLWRNORM))) {
+                       if (__vb2_init_fileio(q, 0))
+                               return res | POLLERR;
+                       /*
+                        * Write to OUTPUT queue can be done immediately.
+                        */
+                       return res | POLLOUT | POLLWRNORM;
+               }
+       }
+
+       /*
+        * There is nothing to wait for if no buffers have already been queued.
+        */
+       if (list_empty(&q->queued_list))
+               return res | POLLERR;
+
+       poll_wait(file, &q->done_wq, wait);
+
+       /*
+        * Take first buffer available for dequeuing.
+        */
+       spin_lock_irqsave(&q->done_lock, flags);
+       if (!list_empty(&q->done_list))
+               vb = list_first_entry(&q->done_list, struct vb2_buffer,
+                                       done_entry);
+       spin_unlock_irqrestore(&q->done_lock, flags);
+
+       if (vb && (vb->state == VB2_BUF_STATE_DONE
+                       || vb->state == VB2_BUF_STATE_ERROR)) {
+               return (V4L2_TYPE_IS_OUTPUT(q->type)) ?
+                               res | POLLOUT | POLLWRNORM :
+                               res | POLLIN | POLLRDNORM;
+       }
+       return res;
+}
+EXPORT_SYMBOL_GPL(vb2_poll);
+
+/**
+ * vb2_queue_init() - initialize a videobuf2 queue
+ * @q:         videobuf2 queue; this structure should be allocated in driver
+ *
+ * The vb2_queue structure should be allocated by the driver. The driver is
+ * responsible of clearing it's content and setting initial values for some
+ * required entries before calling this function.
+ * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
+ * to the struct vb2_queue description in include/media/videobuf2-core.h
+ * for more information.
+ */
+int vb2_queue_init(struct vb2_queue *q)
+{
+       BUG_ON(!q);
+       BUG_ON(!q->ops);
+       BUG_ON(!q->mem_ops);
+       BUG_ON(!q->type);
+       BUG_ON(!q->io_modes);
+
+       BUG_ON(!q->ops->queue_setup);
+       BUG_ON(!q->ops->buf_queue);
+
+       INIT_LIST_HEAD(&q->queued_list);
+       INIT_LIST_HEAD(&q->done_list);
+       spin_lock_init(&q->done_lock);
+       init_waitqueue_head(&q->done_wq);
+
+       if (q->buf_struct_size == 0)
+               q->buf_struct_size = sizeof(struct vb2_buffer);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_queue_init);
+
+/**
+ * vb2_queue_release() - stop streaming, release the queue and free memory
+ * @q:         videobuf2 queue
+ *
+ * This function stops streaming and performs necessary clean ups, including
+ * freeing video buffer memory. The driver is responsible for freeing
+ * the vb2_queue structure itself.
+ */
+void vb2_queue_release(struct vb2_queue *q)
+{
+       __vb2_cleanup_fileio(q);
+       __vb2_queue_cancel(q);
+       __vb2_queue_free(q, q->num_buffers);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_release);
+
+/**
+ * struct vb2_fileio_buf - buffer context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. This structure is used for
+ * tracking context related to the buffers.
+ */
+struct vb2_fileio_buf {
+       void *vaddr;
+       unsigned int size;
+       unsigned int pos;
+       unsigned int queued:1;
+};
+
+/**
+ * struct vb2_fileio_data - queue context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. For proper operation it required
+ * this structure to save the driver state between each call of the read
+ * or write function.
+ */
+struct vb2_fileio_data {
+       struct v4l2_requestbuffers req;
+       struct v4l2_buffer b;
+       struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
+       unsigned int index;
+       unsigned int q_count;
+       unsigned int dq_count;
+       unsigned int flags;
+};
+
+/**
+ * __vb2_init_fileio() - initialize file io emulator
+ * @q:         videobuf2 queue
+ * @read:      mode selector (1 means read, 0 means write)
+ */
+static int __vb2_init_fileio(struct vb2_queue *q, int read)
+{
+       struct vb2_fileio_data *fileio;
+       int i, ret;
+       unsigned int count = 0;
+
+       /*
+        * Sanity check
+        */
+       if ((read && !(q->io_modes & VB2_READ)) ||
+          (!read && !(q->io_modes & VB2_WRITE)))
+               BUG();
+
+       /*
+        * Check if device supports mapping buffers to kernel virtual space.
+        */
+       if (!q->mem_ops->vaddr)
+               return -EBUSY;
+
+       /*
+        * Check if streaming api has not been already activated.
+        */
+       if (q->streaming || q->num_buffers > 0)
+               return -EBUSY;
+
+       /*
+        * Start with count 1, driver can increase it in queue_setup()
+        */
+       count = 1;
+
+       dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n",
+               (read) ? "read" : "write", count, q->io_flags);
+
+       fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
+       if (fileio == NULL)
+               return -ENOMEM;
+
+       fileio->flags = q->io_flags;
+
+       /*
+        * Request buffers and use MMAP type to force driver
+        * to allocate buffers by itself.
+        */
+       fileio->req.count = count;
+       fileio->req.memory = V4L2_MEMORY_MMAP;
+       fileio->req.type = q->type;
+       ret = vb2_reqbufs(q, &fileio->req);
+       if (ret)
+               goto err_kfree;
+
+       /*
+        * Check if plane_count is correct
+        * (multiplane buffers are not supported).
+        */
+       if (q->bufs[0]->num_planes != 1) {
+               ret = -EBUSY;
+               goto err_reqbufs;
+       }
+
+       /*
+        * Get kernel address of each buffer.
+        */
+       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)
+                       goto err_reqbufs;
+               fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
+       }
+
+       /*
+        * Read mode requires pre queuing of all buffers.
+        */
+       if (read) {
+               /*
+                * Queue all buffers.
+                */
+               for (i = 0; i < q->num_buffers; i++) {
+                       struct v4l2_buffer *b = &fileio->b;
+                       memset(b, 0, sizeof(*b));
+                       b->type = q->type;
+                       b->memory = q->memory;
+                       b->index = i;
+                       ret = vb2_qbuf(q, b);
+                       if (ret)
+                               goto err_reqbufs;
+                       fileio->bufs[i].queued = 1;
+               }
+
+               /*
+                * Start streaming.
+                */
+               ret = vb2_streamon(q, q->type);
+               if (ret)
+                       goto err_reqbufs;
+       }
+
+       q->fileio = fileio;
+
+       return ret;
+
+err_reqbufs:
+       fileio->req.count = 0;
+       vb2_reqbufs(q, &fileio->req);
+
+err_kfree:
+       kfree(fileio);
+       return ret;
+}
+
+/**
+ * __vb2_cleanup_fileio() - free resourced used by file io emulator
+ * @q:         videobuf2 queue
+ */
+static int __vb2_cleanup_fileio(struct vb2_queue *q)
+{
+       struct vb2_fileio_data *fileio = q->fileio;
+
+       if (fileio) {
+               /*
+                * Hack fileio context to enable direct calls to vb2 ioctl
+                * interface.
+                */
+               q->fileio = NULL;
+
+               vb2_streamoff(q, q->type);
+               fileio->req.count = 0;
+               vb2_reqbufs(q, &fileio->req);
+               kfree(fileio);
+               dprintk(3, "file io emulator closed\n");
+       }
+       return 0;
+}
+
+/**
+ * __vb2_perform_fileio() - perform a single file io (read or write) operation
+ * @q:         videobuf2 queue
+ * @data:      pointed to target userspace buffer
+ * @count:     number of bytes to read or write
+ * @ppos:      file handle position tracking pointer
+ * @nonblock:  mode selector (1 means blocking calls, 0 means nonblocking)
+ * @read:      access mode selector (1 means read, 0 means write)
+ */
+static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+               loff_t *ppos, int nonblock, int read)
+{
+       struct vb2_fileio_data *fileio;
+       struct vb2_fileio_buf *buf;
+       int ret, index;
+
+       dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n",
+               read ? "read" : "write", (long)*ppos, count,
+               nonblock ? "non" : "");
+
+       if (!data)
+               return -EINVAL;
+
+       /*
+        * Initialize emulator on first call.
+        */
+       if (!q->fileio) {
+               ret = __vb2_init_fileio(q, read);
+               dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+               if (ret)
+                       return ret;
+       }
+       fileio = q->fileio;
+
+       /*
+        * Hack fileio context to enable direct calls to vb2 ioctl interface.
+        * The pointer will be restored before returning from this function.
+        */
+       q->fileio = NULL;
+
+       index = fileio->index;
+       buf = &fileio->bufs[index];
+
+       /*
+        * Check if we need to dequeue the buffer.
+        */
+       if (buf->queued) {
+               struct vb2_buffer *vb;
+
+               /*
+                * Call vb2_dqbuf to get buffer back.
+                */
+               memset(&fileio->b, 0, sizeof(fileio->b));
+               fileio->b.type = q->type;
+               fileio->b.memory = q->memory;
+               fileio->b.index = index;
+               ret = vb2_dqbuf(q, &fileio->b, nonblock);
+               dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+               if (ret)
+                       goto end;
+               fileio->dq_count += 1;
+
+               /*
+                * Get number of bytes filled by the driver
+                */
+               vb = q->bufs[index];
+               buf->size = vb2_get_plane_payload(vb, 0);
+               buf->queued = 0;
+       }
+
+       /*
+        * Limit count on last few bytes of the buffer.
+        */
+       if (buf->pos + count > buf->size) {
+               count = buf->size - buf->pos;
+               dprintk(5, "reducing read count: %zd\n", count);
+       }
+
+       /*
+        * Transfer data to userspace.
+        */
+       dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n",
+               count, index, buf->pos);
+       if (read)
+               ret = copy_to_user(data, buf->vaddr + buf->pos, count);
+       else
+               ret = copy_from_user(buf->vaddr + buf->pos, data, count);
+       if (ret) {
+               dprintk(3, "file io: error copying data\n");
+               ret = -EFAULT;
+               goto end;
+       }
+
+       /*
+        * Update counters.
+        */
+       buf->pos += count;
+       *ppos += count;
+
+       /*
+        * Queue next buffer if required.
+        */
+       if (buf->pos == buf->size ||
+          (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATELY))) {
+               /*
+                * Check if this is the last buffer to read.
+                */
+               if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
+                   fileio->dq_count == 1) {
+                       dprintk(3, "file io: read limit reached\n");
+                       /*
+                        * Restore fileio pointer and release the context.
+                        */
+                       q->fileio = fileio;
+                       return __vb2_cleanup_fileio(q);
+               }
+
+               /*
+                * Call vb2_qbuf and give buffer to the driver.
+                */
+               memset(&fileio->b, 0, sizeof(fileio->b));
+               fileio->b.type = q->type;
+               fileio->b.memory = q->memory;
+               fileio->b.index = index;
+               fileio->b.bytesused = buf->pos;
+               ret = vb2_qbuf(q, &fileio->b);
+               dprintk(5, "file io: vb2_dbuf result: %d\n", ret);
+               if (ret)
+                       goto end;
+
+               /*
+                * Buffer has been queued, update the status
+                */
+               buf->pos = 0;
+               buf->queued = 1;
+               buf->size = q->bufs[0]->v4l2_planes[0].length;
+               fileio->q_count += 1;
+
+               /*
+                * Switch to the next buffer
+                */
+               fileio->index = (index + 1) % q->num_buffers;
+
+               /*
+                * Start streaming if required.
+                */
+               if (!read && !q->streaming) {
+                       ret = vb2_streamon(q, q->type);
+                       if (ret)
+                               goto end;
+               }
+       }
+
+       /*
+        * Return proper number of bytes processed.
+        */
+       if (ret == 0)
+               ret = count;
+end:
+       /*
+        * Restore the fileio context and block vb2 ioctl interface.
+        */
+       q->fileio = fileio;
+       return ret;
+}
+
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+               loff_t *ppos, int nonblocking)
+{
+       return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
+}
+EXPORT_SYMBOL_GPL(vb2_read);
+
+size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+               loff_t *ppos, int nonblocking)
+{
+       return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
+}
+EXPORT_SYMBOL_GPL(vb2_write);
+
+
+/*
+ * The following functions are not part of the vb2 core API, but are helper
+ * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
+ * and struct vb2_ops.
+ * They contain boilerplate code that most if not all drivers have to do
+ * and so they simplify the driver code.
+ */
+
+/* The queue is busy if there is a owner and you are not that owner. */
+static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file)
+{
+       return vdev->queue->owner && vdev->queue->owner != file->private_data;
+}
+
+/* vb2 ioctl helpers */
+
+int vb2_ioctl_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *p)
+{
+       struct video_device *vdev = video_devdata(file);
+       int res = __verify_memory_type(vdev->queue, p->memory, p->type);
+
+       if (res)
+               return res;
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       res = __reqbufs(vdev->queue, p);
+       /* If count == 0, then the owner has released all buffers and he
+          is no longer owner of the queue. Otherwise we have a new owner. */
+       if (res == 0)
+               vdev->queue->owner = p->count ? file->private_data : NULL;
+       return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
+
+int vb2_ioctl_create_bufs(struct file *file, void *priv,
+                         struct v4l2_create_buffers *p)
+{
+       struct video_device *vdev = video_devdata(file);
+       int res = __verify_memory_type(vdev->queue, p->memory, p->format.type);
+
+       p->index = vdev->queue->num_buffers;
+       /* If count == 0, then just check if memory and type are valid.
+          Any -EBUSY result from __verify_memory_type can be mapped to 0. */
+       if (p->count == 0)
+               return res != -EBUSY ? res : 0;
+       if (res)
+               return res;
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       res = __create_bufs(vdev->queue, p);
+       if (res == 0)
+               vdev->queue->owner = file->private_data;
+       return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs);
+
+int vb2_ioctl_prepare_buf(struct file *file, void *priv,
+                         struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_prepare_buf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
+
+int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       /* No need to call vb2_queue_is_busy(), anyone can query buffers. */
+       return vb2_querybuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf);
+
+int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_qbuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
+
+int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
+
+int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_streamon(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamon);
+
+int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (vb2_queue_is_busy(vdev, file))
+               return -EBUSY;
+       return vb2_streamoff(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
+
+/* v4l2_file_operations helpers */
+
+int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       return vb2_mmap(vdev->queue, vma);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_mmap);
+
+int vb2_fop_release(struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       if (file->private_data == vdev->queue->owner) {
+               vb2_queue_release(vdev->queue);
+               vdev->queue->owner = NULL;
+       }
+       return v4l2_fh_release(file);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_release);
+
+ssize_t vb2_fop_write(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+       int err = -EBUSY;
+
+       if (lock && mutex_lock_interruptible(lock))
+               return -ERESTARTSYS;
+       if (vb2_queue_is_busy(vdev, file))
+               goto exit;
+       err = vb2_write(vdev->queue, buf, count, ppos,
+                      file->f_flags & O_NONBLOCK);
+       if (err >= 0)
+               vdev->queue->owner = file->private_data;
+exit:
+       if (lock)
+               mutex_unlock(lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_write);
+
+ssize_t vb2_fop_read(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+       int err = -EBUSY;
+
+       if (lock && mutex_lock_interruptible(lock))
+               return -ERESTARTSYS;
+       if (vb2_queue_is_busy(vdev, file))
+               goto exit;
+       err = vb2_read(vdev->queue, buf, count, ppos,
+                      file->f_flags & O_NONBLOCK);
+       if (err >= 0)
+               vdev->queue->owner = file->private_data;
+exit:
+       if (lock)
+               mutex_unlock(lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_read);
+
+unsigned int vb2_fop_poll(struct file *file, poll_table *wait)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct vb2_queue *q = vdev->queue;
+       struct mutex *lock = q->lock ? q->lock : vdev->lock;
+       unsigned long req_events = poll_requested_events(wait);
+       unsigned res;
+       void *fileio;
+       bool must_lock = false;
+
+       /* Try to be smart: only lock if polling might start fileio,
+          otherwise locking will only introduce unwanted delays. */
+       if (q->num_buffers == 0 && q->fileio == NULL) {
+               if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
+                               (req_events & (POLLIN | POLLRDNORM)))
+                       must_lock = true;
+               else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
+                               (req_events & (POLLOUT | POLLWRNORM)))
+                       must_lock = true;
+       }
+
+       /* If locking is needed, but this helper doesn't know how, then you
+          shouldn't be using this helper but you should write your own. */
+       WARN_ON(must_lock && !lock);
+
+       if (must_lock && lock && mutex_lock_interruptible(lock))
+               return POLLERR;
+
+       fileio = q->fileio;
+
+       res = vb2_poll(vdev->queue, file, wait);
+
+       /* If fileio was started, then we have a new queue owner. */
+       if (must_lock && !fileio && q->fileio)
+               q->owner = file->private_data;
+       if (must_lock && lock)
+               mutex_unlock(lock);
+       return res;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_poll);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
+               unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+       struct video_device *vdev = video_devdata(file);
+
+       return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
+#endif
+
+/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
+
+void vb2_ops_wait_prepare(struct vb2_queue *vq)
+{
+       mutex_unlock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
+
+void vb2_ops_wait_finish(struct vb2_queue *vq)
+{
+       mutex_lock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
+
+MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
new file mode 100644 (file)
index 0000000..4b71326
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_dc_conf {
+       struct device           *dev;
+};
+
+struct vb2_dc_buf {
+       struct vb2_dc_conf              *conf;
+       void                            *vaddr;
+       dma_addr_t                      dma_addr;
+       unsigned long                   size;
+       struct vm_area_struct           *vma;
+       atomic_t                        refcount;
+       struct vb2_vmarea_handler       handler;
+};
+
+static void vb2_dma_contig_put(void *buf_priv);
+
+static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
+{
+       struct vb2_dc_conf *conf = alloc_ctx;
+       struct vb2_dc_buf *buf;
+
+       buf = kzalloc(sizeof *buf, GFP_KERNEL);
+       if (!buf)
+               return ERR_PTR(-ENOMEM);
+
+       buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr,
+                                       GFP_KERNEL);
+       if (!buf->vaddr) {
+               dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
+                       size);
+               kfree(buf);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       buf->conf = conf;
+       buf->size = size;
+
+       buf->handler.refcount = &buf->refcount;
+       buf->handler.put = vb2_dma_contig_put;
+       buf->handler.arg = buf;
+
+       atomic_inc(&buf->refcount);
+
+       return buf;
+}
+
+static void vb2_dma_contig_put(void *buf_priv)
+{
+       struct vb2_dc_buf *buf = buf_priv;
+
+       if (atomic_dec_and_test(&buf->refcount)) {
+               dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
+                                 buf->dma_addr);
+               kfree(buf);
+       }
+}
+
+static void *vb2_dma_contig_cookie(void *buf_priv)
+{
+       struct vb2_dc_buf *buf = buf_priv;
+
+       return &buf->dma_addr;
+}
+
+static void *vb2_dma_contig_vaddr(void *buf_priv)
+{
+       struct vb2_dc_buf *buf = buf_priv;
+       if (!buf)
+               return NULL;
+
+       return buf->vaddr;
+}
+
+static unsigned int vb2_dma_contig_num_users(void *buf_priv)
+{
+       struct vb2_dc_buf *buf = buf_priv;
+
+       return atomic_read(&buf->refcount);
+}
+
+static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+       struct vb2_dc_buf *buf = buf_priv;
+
+       if (!buf) {
+               printk(KERN_ERR "No buffer to map\n");
+               return -EINVAL;
+       }
+
+       return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size,
+                                 &vb2_common_vm_ops, &buf->handler);
+}
+
+static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
+                                       unsigned long size, int write)
+{
+       struct vb2_dc_buf *buf;
+       struct vm_area_struct *vma;
+       dma_addr_t dma_addr = 0;
+       int ret;
+
+       buf = kzalloc(sizeof *buf, GFP_KERNEL);
+       if (!buf)
+               return ERR_PTR(-ENOMEM);
+
+       ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr);
+       if (ret) {
+               printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
+                               vaddr);
+               kfree(buf);
+               return ERR_PTR(ret);
+       }
+
+       buf->size = size;
+       buf->dma_addr = dma_addr;
+       buf->vma = vma;
+
+       return buf;
+}
+
+static void vb2_dma_contig_put_userptr(void *mem_priv)
+{
+       struct vb2_dc_buf *buf = mem_priv;
+
+       if (!buf)
+               return;
+
+       vb2_put_vma(buf->vma);
+       kfree(buf);
+}
+
+const struct vb2_mem_ops vb2_dma_contig_memops = {
+       .alloc          = vb2_dma_contig_alloc,
+       .put            = vb2_dma_contig_put,
+       .cookie         = vb2_dma_contig_cookie,
+       .vaddr          = vb2_dma_contig_vaddr,
+       .mmap           = vb2_dma_contig_mmap,
+       .get_userptr    = vb2_dma_contig_get_userptr,
+       .put_userptr    = vb2_dma_contig_put_userptr,
+       .num_users      = vb2_dma_contig_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
+
+void *vb2_dma_contig_init_ctx(struct device *dev)
+{
+       struct vb2_dc_conf *conf;
+
+       conf = kzalloc(sizeof *conf, GFP_KERNEL);
+       if (!conf)
+               return ERR_PTR(-ENOMEM);
+
+       conf->dev = dev;
+
+       return conf;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
+
+void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
+{
+       kfree(alloc_ctx);
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
+
+MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
new file mode 100644 (file)
index 0000000..25c3b36
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+#include <media/videobuf2-dma-sg.h>
+
+struct vb2_dma_sg_buf {
+       void                            *vaddr;
+       struct page                     **pages;
+       int                             write;
+       int                             offset;
+       struct vb2_dma_sg_desc          sg_desc;
+       atomic_t                        refcount;
+       struct vb2_vmarea_handler       handler;
+};
+
+static void vb2_dma_sg_put(void *buf_priv);
+
+static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size)
+{
+       struct vb2_dma_sg_buf *buf;
+       int i;
+
+       buf = kzalloc(sizeof *buf, GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->vaddr = NULL;
+       buf->write = 0;
+       buf->offset = 0;
+       buf->sg_desc.size = size;
+       buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+       buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages *
+                                     sizeof(*buf->sg_desc.sglist));
+       if (!buf->sg_desc.sglist)
+               goto fail_sglist_alloc;
+       sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
+
+       buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+                            GFP_KERNEL);
+       if (!buf->pages)
+               goto fail_pages_array_alloc;
+
+       for (i = 0; i < buf->sg_desc.num_pages; ++i) {
+               buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
+               if (NULL == buf->pages[i])
+                       goto fail_pages_alloc;
+               sg_set_page(&buf->sg_desc.sglist[i],
+                           buf->pages[i], PAGE_SIZE, 0);
+       }
+
+       buf->handler.refcount = &buf->refcount;
+       buf->handler.put = vb2_dma_sg_put;
+       buf->handler.arg = buf;
+
+       atomic_inc(&buf->refcount);
+
+       printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n",
+               __func__, buf->sg_desc.num_pages);
+       return buf;
+
+fail_pages_alloc:
+       while (--i >= 0)
+               __free_page(buf->pages[i]);
+       kfree(buf->pages);
+
+fail_pages_array_alloc:
+       vfree(buf->sg_desc.sglist);
+
+fail_sglist_alloc:
+       kfree(buf);
+       return NULL;
+}
+
+static void vb2_dma_sg_put(void *buf_priv)
+{
+       struct vb2_dma_sg_buf *buf = buf_priv;
+       int i = buf->sg_desc.num_pages;
+
+       if (atomic_dec_and_test(&buf->refcount)) {
+               printk(KERN_DEBUG "%s: Freeing buffer of %d pages\n", __func__,
+                       buf->sg_desc.num_pages);
+               if (buf->vaddr)
+                       vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+               vfree(buf->sg_desc.sglist);
+               while (--i >= 0)
+                       __free_page(buf->pages[i]);
+               kfree(buf->pages);
+               kfree(buf);
+       }
+}
+
+static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
+                                   unsigned long size, int write)
+{
+       struct vb2_dma_sg_buf *buf;
+       unsigned long first, last;
+       int num_pages_from_user, i;
+
+       buf = kzalloc(sizeof *buf, GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->vaddr = NULL;
+       buf->write = write;
+       buf->offset = vaddr & ~PAGE_MASK;
+       buf->sg_desc.size = size;
+
+       first = (vaddr           & PAGE_MASK) >> PAGE_SHIFT;
+       last  = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
+       buf->sg_desc.num_pages = last - first + 1;
+
+       buf->sg_desc.sglist = vzalloc(
+               buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
+       if (!buf->sg_desc.sglist)
+               goto userptr_fail_sglist_alloc;
+
+       sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
+
+       buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+                            GFP_KERNEL);
+       if (!buf->pages)
+               goto userptr_fail_pages_array_alloc;
+
+       num_pages_from_user = get_user_pages(current, current->mm,
+                                            vaddr & PAGE_MASK,
+                                            buf->sg_desc.num_pages,
+                                            write,
+                                            1, /* force */
+                                            buf->pages,
+                                            NULL);
+
+       if (num_pages_from_user != buf->sg_desc.num_pages)
+               goto userptr_fail_get_user_pages;
+
+       sg_set_page(&buf->sg_desc.sglist[0], buf->pages[0],
+                   PAGE_SIZE - buf->offset, buf->offset);
+       size -= PAGE_SIZE - buf->offset;
+       for (i = 1; i < buf->sg_desc.num_pages; ++i) {
+               sg_set_page(&buf->sg_desc.sglist[i], buf->pages[i],
+                           min_t(size_t, PAGE_SIZE, size), 0);
+               size -= min_t(size_t, PAGE_SIZE, size);
+       }
+       return buf;
+
+userptr_fail_get_user_pages:
+       printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n",
+              num_pages_from_user, buf->sg_desc.num_pages);
+       while (--num_pages_from_user >= 0)
+               put_page(buf->pages[num_pages_from_user]);
+       kfree(buf->pages);
+
+userptr_fail_pages_array_alloc:
+       vfree(buf->sg_desc.sglist);
+
+userptr_fail_sglist_alloc:
+       kfree(buf);
+       return NULL;
+}
+
+/*
+ * @put_userptr: inform the allocator that a USERPTR buffer will no longer
+ *              be used
+ */
+static void vb2_dma_sg_put_userptr(void *buf_priv)
+{
+       struct vb2_dma_sg_buf *buf = buf_priv;
+       int i = buf->sg_desc.num_pages;
+
+       printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n",
+              __func__, buf->sg_desc.num_pages);
+       if (buf->vaddr)
+               vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+       while (--i >= 0) {
+               if (buf->write)
+                       set_page_dirty_lock(buf->pages[i]);
+               put_page(buf->pages[i]);
+       }
+       vfree(buf->sg_desc.sglist);
+       kfree(buf->pages);
+       kfree(buf);
+}
+
+static void *vb2_dma_sg_vaddr(void *buf_priv)
+{
+       struct vb2_dma_sg_buf *buf = buf_priv;
+
+       BUG_ON(!buf);
+
+       if (!buf->vaddr)
+               buf->vaddr = vm_map_ram(buf->pages,
+                                       buf->sg_desc.num_pages,
+                                       -1,
+                                       PAGE_KERNEL);
+
+       /* add offset in case userptr is not page-aligned */
+       return buf->vaddr + buf->offset;
+}
+
+static unsigned int vb2_dma_sg_num_users(void *buf_priv)
+{
+       struct vb2_dma_sg_buf *buf = buf_priv;
+
+       return atomic_read(&buf->refcount);
+}
+
+static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+       struct vb2_dma_sg_buf *buf = buf_priv;
+       unsigned long uaddr = vma->vm_start;
+       unsigned long usize = vma->vm_end - vma->vm_start;
+       int i = 0;
+
+       if (!buf) {
+               printk(KERN_ERR "No memory to map\n");
+               return -EINVAL;
+       }
+
+       do {
+               int ret;
+
+               ret = vm_insert_page(vma, uaddr, buf->pages[i++]);
+               if (ret) {
+                       printk(KERN_ERR "Remapping memory, error: %d\n", ret);
+                       return ret;
+               }
+
+               uaddr += PAGE_SIZE;
+               usize -= PAGE_SIZE;
+       } while (usize > 0);
+
+
+       /*
+        * Use common vm_area operations to track buffer refcount.
+        */
+       vma->vm_private_data    = &buf->handler;
+       vma->vm_ops             = &vb2_common_vm_ops;
+
+       vma->vm_ops->open(vma);
+
+       return 0;
+}
+
+static void *vb2_dma_sg_cookie(void *buf_priv)
+{
+       struct vb2_dma_sg_buf *buf = buf_priv;
+
+       return &buf->sg_desc;
+}
+
+const struct vb2_mem_ops vb2_dma_sg_memops = {
+       .alloc          = vb2_dma_sg_alloc,
+       .put            = vb2_dma_sg_put,
+       .get_userptr    = vb2_dma_sg_get_userptr,
+       .put_userptr    = vb2_dma_sg_put_userptr,
+       .vaddr          = vb2_dma_sg_vaddr,
+       .mmap           = vb2_dma_sg_mmap,
+       .num_users      = vb2_dma_sg_num_users,
+       .cookie         = vb2_dma_sg_cookie,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
+
+MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
+MODULE_AUTHOR("Andrzej Pietrasiewicz");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c
new file mode 100644 (file)
index 0000000..504cd4c
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * videobuf2-memops.c - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *        Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+/**
+ * vb2_get_vma() - acquire and lock the virtual memory area
+ * @vma:       given virtual memory area
+ *
+ * This function attempts to acquire an area mapped in the userspace for
+ * the duration of a hardware operation. The area is "locked" by performing
+ * the same set of operation that are done when process calls fork() and
+ * memory areas are duplicated.
+ *
+ * Returns a copy of a virtual memory region on success or NULL.
+ */
+struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma)
+{
+       struct vm_area_struct *vma_copy;
+
+       vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
+       if (vma_copy == NULL)
+               return NULL;
+
+       if (vma->vm_ops && vma->vm_ops->open)
+               vma->vm_ops->open(vma);
+
+       if (vma->vm_file)
+               get_file(vma->vm_file);
+
+       memcpy(vma_copy, vma, sizeof(*vma));
+
+       vma_copy->vm_mm = NULL;
+       vma_copy->vm_next = NULL;
+       vma_copy->vm_prev = NULL;
+
+       return vma_copy;
+}
+EXPORT_SYMBOL_GPL(vb2_get_vma);
+
+/**
+ * vb2_put_userptr() - release a userspace virtual memory area
+ * @vma:       virtual memory region associated with the area to be released
+ *
+ * This function releases the previously acquired memory area after a hardware
+ * operation.
+ */
+void vb2_put_vma(struct vm_area_struct *vma)
+{
+       if (!vma)
+               return;
+
+       if (vma->vm_ops && vma->vm_ops->close)
+               vma->vm_ops->close(vma);
+
+       if (vma->vm_file)
+               fput(vma->vm_file);
+
+       kfree(vma);
+}
+EXPORT_SYMBOL_GPL(vb2_put_vma);
+
+/**
+ * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory
+ * @vaddr:     starting virtual address of the area to be verified
+ * @size:      size of the area
+ * @res_paddr: will return physical address for the given vaddr
+ * @res_vma:   will return locked copy of struct vm_area for the given area
+ *
+ * This function will go through memory area of size @size mapped at @vaddr and
+ * verify that the underlying physical pages are contiguous. If they are
+ * contiguous the virtual memory area is locked and a @res_vma is filled with
+ * the copy and @res_pa set to the physical address of the buffer.
+ *
+ * Returns 0 on success.
+ */
+int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
+                          struct vm_area_struct **res_vma, dma_addr_t *res_pa)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned long offset, start, end;
+       unsigned long this_pfn, prev_pfn;
+       dma_addr_t pa = 0;
+
+       start = vaddr;
+       offset = start & ~PAGE_MASK;
+       end = start + size;
+
+       vma = find_vma(mm, start);
+
+       if (vma == NULL || vma->vm_end < end)
+               return -EFAULT;
+
+       for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
+               int ret = follow_pfn(vma, start, &this_pfn);
+               if (ret)
+                       return ret;
+
+               if (prev_pfn == 0)
+                       pa = this_pfn << PAGE_SHIFT;
+               else if (this_pfn != prev_pfn + 1)
+                       return -EFAULT;
+
+               prev_pfn = this_pfn;
+       }
+
+       /*
+        * Memory is contigous, lock vma and return to the caller
+        */
+       *res_vma = vb2_get_vma(vma);
+       if (*res_vma == NULL)
+               return -ENOMEM;
+
+       *res_pa = pa + offset;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
+
+/**
+ * vb2_mmap_pfn_range() - map physical pages to userspace
+ * @vma:       virtual memory region for the mapping
+ * @paddr:     starting physical address of the memory to be mapped
+ * @size:      size of the memory to be mapped
+ * @vm_ops:    vm operations to be assigned to the created area
+ * @priv:      private data to be associated with the area
+ *
+ * Returns 0 on success.
+ */
+int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
+                               unsigned long size,
+                               const struct vm_operations_struct *vm_ops,
+                               void *priv)
+{
+       int ret;
+
+       size = min_t(unsigned long, vma->vm_end - vma->vm_start, size);
+
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
+                               size, vma->vm_page_prot);
+       if (ret) {
+               printk(KERN_ERR "Remapping memory failed, error: %d\n", ret);
+               return ret;
+       }
+
+       vma->vm_flags           |= VM_DONTEXPAND | VM_RESERVED;
+       vma->vm_private_data    = priv;
+       vma->vm_ops             = vm_ops;
+
+       vma->vm_ops->open(vma);
+
+       pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
+                       __func__, paddr, vma->vm_start, size);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range);
+
+/**
+ * vb2_common_vm_open() - increase refcount of the vma
+ * @vma:       virtual memory region for the mapping
+ *
+ * This function adds another user to the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_open(struct vm_area_struct *vma)
+{
+       struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+       pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+              __func__, h, atomic_read(h->refcount), vma->vm_start,
+              vma->vm_end);
+
+       atomic_inc(h->refcount);
+}
+
+/**
+ * vb2_common_vm_close() - decrease refcount of the vma
+ * @vma:       virtual memory region for the mapping
+ *
+ * This function releases the user from the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_close(struct vm_area_struct *vma)
+{
+       struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+       pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+              __func__, h, atomic_read(h->refcount), vma->vm_start,
+              vma->vm_end);
+
+       h->put(h->arg);
+}
+
+/**
+ * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped
+ * video buffers
+ */
+const struct vm_operations_struct vb2_common_vm_ops = {
+       .open = vb2_common_vm_open,
+       .close = vb2_common_vm_close,
+};
+EXPORT_SYMBOL_GPL(vb2_common_vm_ops);
+
+MODULE_DESCRIPTION("common memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
new file mode 100644 (file)
index 0000000..94efa04
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_vmalloc_buf {
+       void                            *vaddr;
+       struct page                     **pages;
+       struct vm_area_struct           *vma;
+       int                             write;
+       unsigned long                   size;
+       unsigned int                    n_pages;
+       atomic_t                        refcount;
+       struct vb2_vmarea_handler       handler;
+};
+
+static void vb2_vmalloc_put(void *buf_priv);
+
+static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
+{
+       struct vb2_vmalloc_buf *buf;
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->size = size;
+       buf->vaddr = vmalloc_user(buf->size);
+       buf->handler.refcount = &buf->refcount;
+       buf->handler.put = vb2_vmalloc_put;
+       buf->handler.arg = buf;
+
+       if (!buf->vaddr) {
+               pr_debug("vmalloc of size %ld failed\n", buf->size);
+               kfree(buf);
+               return NULL;
+       }
+
+       atomic_inc(&buf->refcount);
+       return buf;
+}
+
+static void vb2_vmalloc_put(void *buf_priv)
+{
+       struct vb2_vmalloc_buf *buf = buf_priv;
+
+       if (atomic_dec_and_test(&buf->refcount)) {
+               vfree(buf->vaddr);
+               kfree(buf);
+       }
+}
+
+static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
+                                    unsigned long size, int write)
+{
+       struct vb2_vmalloc_buf *buf;
+       unsigned long first, last;
+       int n_pages, offset;
+       struct vm_area_struct *vma;
+       dma_addr_t physp;
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->write = write;
+       offset = vaddr & ~PAGE_MASK;
+       buf->size = size;
+
+
+       vma = find_vma(current->mm, vaddr);
+       if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
+               if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
+                       goto fail_pages_array_alloc;
+               buf->vma = vma;
+               buf->vaddr = ioremap_nocache(physp, size);
+               if (!buf->vaddr)
+                       goto fail_pages_array_alloc;
+       } else {
+               first = vaddr >> PAGE_SHIFT;
+               last  = (vaddr + size - 1) >> PAGE_SHIFT;
+               buf->n_pages = last - first + 1;
+               buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
+                                    GFP_KERNEL);
+               if (!buf->pages)
+                       goto fail_pages_array_alloc;
+
+               /* current->mm->mmap_sem is taken by videobuf2 core */
+               n_pages = get_user_pages(current, current->mm,
+                                        vaddr & PAGE_MASK, buf->n_pages,
+                                        write, 1, /* force */
+                                        buf->pages, NULL);
+               if (n_pages != buf->n_pages)
+                       goto fail_get_user_pages;
+
+               buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+                                       PAGE_KERNEL);
+               if (!buf->vaddr)
+                       goto fail_get_user_pages;
+       }
+
+       buf->vaddr += offset;
+       return buf;
+
+fail_get_user_pages:
+       pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
+                buf->n_pages);
+       while (--n_pages >= 0)
+               put_page(buf->pages[n_pages]);
+       kfree(buf->pages);
+
+fail_pages_array_alloc:
+       kfree(buf);
+
+       return NULL;
+}
+
+static void vb2_vmalloc_put_userptr(void *buf_priv)
+{
+       struct vb2_vmalloc_buf *buf = buf_priv;
+       unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
+       unsigned int i;
+
+       if (buf->pages) {
+               if (vaddr)
+                       vm_unmap_ram((void *)vaddr, buf->n_pages);
+               for (i = 0; i < buf->n_pages; ++i) {
+                       if (buf->write)
+                               set_page_dirty_lock(buf->pages[i]);
+                       put_page(buf->pages[i]);
+               }
+               kfree(buf->pages);
+       } else {
+               if (buf->vma)
+                       vb2_put_vma(buf->vma);
+               iounmap(buf->vaddr);
+       }
+       kfree(buf);
+}
+
+static void *vb2_vmalloc_vaddr(void *buf_priv)
+{
+       struct vb2_vmalloc_buf *buf = buf_priv;
+
+       if (!buf->vaddr) {
+               pr_err("Address of an unallocated plane requested "
+                      "or cannot map user pointer\n");
+               return NULL;
+       }
+
+       return buf->vaddr;
+}
+
+static unsigned int vb2_vmalloc_num_users(void *buf_priv)
+{
+       struct vb2_vmalloc_buf *buf = buf_priv;
+       return atomic_read(&buf->refcount);
+}
+
+static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+       struct vb2_vmalloc_buf *buf = buf_priv;
+       int ret;
+
+       if (!buf) {
+               pr_err("No memory to map\n");
+               return -EINVAL;
+       }
+
+       ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+       if (ret) {
+               pr_err("Remapping vmalloc memory, error: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Make sure that vm_areas for 2 buffers won't be merged together
+        */
+       vma->vm_flags           |= VM_DONTEXPAND;
+
+       /*
+        * Use common vm_area operations to track buffer refcount.
+        */
+       vma->vm_private_data    = &buf->handler;
+       vma->vm_ops             = &vb2_common_vm_ops;
+
+       vma->vm_ops->open(vma);
+
+       return 0;
+}
+
+const struct vb2_mem_ops vb2_vmalloc_memops = {
+       .alloc          = vb2_vmalloc_alloc,
+       .put            = vb2_vmalloc_put,
+       .get_userptr    = vb2_vmalloc_get_userptr,
+       .put_userptr    = vb2_vmalloc_put_userptr,
+       .vaddr          = vb2_vmalloc_vaddr,
+       .mmap           = vb2_vmalloc_mmap,
+       .num_users      = vb2_vmalloc_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
+
+MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
index a5e3bd044085cb481c8574fcaf3b1a3371d16f8e..068e8daa6b7b0354c8f32c210cda549b697fa872 100644 (file)
@@ -2,32 +2,6 @@
 # Generic video config states
 #
 
-config VIDEO_V4L2
-       tristate
-       depends on VIDEO_DEV && VIDEO_V4L2_COMMON
-       default y
-
-config VIDEOBUF_GEN
-       tristate
-
-config VIDEOBUF_DMA_SG
-       depends on HAS_DMA
-       select VIDEOBUF_GEN
-       tristate
-
-config VIDEOBUF_VMALLOC
-       select VIDEOBUF_GEN
-       tristate
-
-config VIDEOBUF_DMA_CONTIG
-       depends on HAS_DMA
-       select VIDEOBUF_GEN
-       tristate
-
-config VIDEOBUF_DVB
-       tristate
-       select VIDEOBUF_GEN
-
 config VIDEO_BTCX
        depends on PCI
        tristate
@@ -36,36 +10,6 @@ config VIDEO_TVEEPROM
        tristate
        depends on I2C
 
-config VIDEO_TUNER
-       tristate
-       depends on MEDIA_TUNER
-
-config V4L2_MEM2MEM_DEV
-       tristate
-       depends on VIDEOBUF2_CORE
-
-config VIDEOBUF2_CORE
-       tristate
-
-config VIDEOBUF2_MEMOPS
-       tristate
-
-config VIDEOBUF2_DMA_CONTIG
-       select VIDEOBUF2_CORE
-       select VIDEOBUF2_MEMOPS
-       tristate
-
-config VIDEOBUF2_VMALLOC
-       select VIDEOBUF2_CORE
-       select VIDEOBUF2_MEMOPS
-       tristate
-
-
-config VIDEOBUF2_DMA_SG
-       #depends on HAS_DMA
-       select VIDEOBUF2_CORE
-       select VIDEOBUF2_MEMOPS
-       tristate
 #
 # Multimedia Video device configuration
 #
index 12cad1206148e82da4b8354e542e50a1c7fccfa2..839e2c982376571571d9347a1136a97baf79e52c 100644 (file)
@@ -2,32 +2,18 @@
 # Makefile for the video capture/playback device drivers.
 #
 
-tuner-objs     :=      tuner-core.o
-
 msp3400-objs   :=      msp3400-driver.o msp3400-kthreads.o
 
 stkwebcam-objs :=      stk-webcam.o stk-sensor.o
 
 omap2cam-objs  :=      omap24xxcam.o omap24xxcam-dma.o
 
-videodev-objs  :=      v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
-                       v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
-ifeq ($(CONFIG_COMPAT),y)
-  videodev-objs += v4l2-compat-ioctl32.o
-endif
-
-# V4L2 core modules
-
-obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
-obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
-
 # Helper modules
 
 obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
 
 # All i2c modules must come first:
 
-obj-$(CONFIG_VIDEO_TUNER) += tuner.o
 obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
 obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
 obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
@@ -128,21 +114,8 @@ obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o
 obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
 obj-$(CONFIG_VIDEO_STK1160) += stk1160/
 
-obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
-obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
-obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
-obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
-obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
 obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
 
-obj-$(CONFIG_VIDEOBUF2_CORE)           += videobuf2-core.o
-obj-$(CONFIG_VIDEOBUF2_MEMOPS)         += videobuf2-memops.o
-obj-$(CONFIG_VIDEOBUF2_VMALLOC)                += videobuf2-vmalloc.o
-obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG)     += videobuf2-dma-contig.o
-obj-$(CONFIG_VIDEOBUF2_DMA_SG)         += videobuf2-dma-sg.o
-
-obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
-
 obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
 
 obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
deleted file mode 100644 (file)
index b5a819a..0000000
+++ /dev/null
@@ -1,1354 +0,0 @@
-/*
- * i2c tv tuner chip device driver
- * core core, i.e. kernel interfaces, registering and so on
- *
- * Copyright(c) by Ralph Metzler, Gerd Knorr, Gunther Mayer
- *
- * Copyright(c) 2005-2011 by Mauro Carvalho Chehab
- *     - Added support for a separate Radio tuner
- *     - Major rework and cleanups at the code
- *
- * This driver supports many devices and the idea is to let the driver
- * detect which device is present. So rather than listing all supported
- * devices here, we pretend to support a single, fake device type that will
- * handle both radio and analog TV tuning.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/i2c.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/videodev2.h>
-#include <media/tuner.h>
-#include <media/tuner-types.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include "mt20xx.h"
-#include "tda8290.h"
-#include "tea5761.h"
-#include "tea5767.h"
-#include "tuner-xc2028.h"
-#include "tuner-simple.h"
-#include "tda9887.h"
-#include "xc5000.h"
-#include "tda18271.h"
-#include "xc4000.h"
-
-#define UNSET (-1U)
-
-#define PREFIX (t->i2c->driver->driver.name)
-
-/*
- * Driver modprobe parameters
- */
-
-/* insmod options used at init time => read/only */
-static unsigned int addr;
-static unsigned int no_autodetect;
-static unsigned int show_i2c;
-
-module_param(addr, int, 0444);
-module_param(no_autodetect, int, 0444);
-module_param(show_i2c, int, 0444);
-
-/* insmod options used at runtime => read/write */
-static int tuner_debug;
-static unsigned int tv_range[2] = { 44, 958 };
-static unsigned int radio_range[2] = { 65, 108 };
-static char pal[] = "--";
-static char secam[] = "--";
-static char ntsc[] = "-";
-
-module_param_named(debug, tuner_debug, int, 0644);
-module_param_array(tv_range, int, NULL, 0644);
-module_param_array(radio_range, int, NULL, 0644);
-module_param_string(pal, pal, sizeof(pal), 0644);
-module_param_string(secam, secam, sizeof(secam), 0644);
-module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
-
-/*
- * Static vars
- */
-
-static LIST_HEAD(tuner_list);
-static const struct v4l2_subdev_ops tuner_ops;
-
-/*
- * Debug macros
- */
-
-#define tuner_warn(fmt, arg...) do {                   \
-       printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
-              i2c_adapter_id(t->i2c->adapter),         \
-              t->i2c->addr, ##arg);                    \
-        } while (0)
-
-#define tuner_info(fmt, arg...) do {                   \
-       printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX,    \
-              i2c_adapter_id(t->i2c->adapter),         \
-              t->i2c->addr, ##arg);                    \
-        } while (0)
-
-#define tuner_err(fmt, arg...) do {                    \
-       printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX,     \
-              i2c_adapter_id(t->i2c->adapter),         \
-              t->i2c->addr, ##arg);                    \
-        } while (0)
-
-#define tuner_dbg(fmt, arg...) do {                            \
-       if (tuner_debug)                                        \
-               printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX,   \
-                      i2c_adapter_id(t->i2c->adapter),         \
-                      t->i2c->addr, ##arg);                    \
-        } while (0)
-
-/*
- * Internal struct used inside the driver
- */
-
-struct tuner {
-       /* device */
-       struct dvb_frontend fe;
-       struct i2c_client   *i2c;
-       struct v4l2_subdev  sd;
-       struct list_head    list;
-
-       /* keep track of the current settings */
-       v4l2_std_id         std;
-       unsigned int        tv_freq;
-       unsigned int        radio_freq;
-       unsigned int        audmode;
-
-       enum v4l2_tuner_type mode;
-       unsigned int        mode_mask; /* Combination of allowable modes */
-
-       bool                standby;    /* Standby mode */
-
-       unsigned int        type; /* chip type id */
-       unsigned int        config;
-       const char          *name;
-};
-
-/*
- * Function prototypes
- */
-
-static void set_tv_freq(struct i2c_client *c, unsigned int freq);
-static void set_radio_freq(struct i2c_client *c, unsigned int freq);
-
-/*
- * tuner attach/detach logic
- */
-
-/* This macro allows us to probe dynamically, avoiding static links */
-#ifdef CONFIG_MEDIA_ATTACH
-#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
-       int __r = -EINVAL; \
-       typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
-       if (__a) { \
-               __r = (int) __a(ARGS); \
-               symbol_put(FUNCTION); \
-       } else { \
-               printk(KERN_ERR "TUNER: Unable to find " \
-                               "symbol "#FUNCTION"()\n"); \
-       } \
-       __r; \
-})
-
-static void tuner_detach(struct dvb_frontend *fe)
-{
-       if (fe->ops.tuner_ops.release) {
-               fe->ops.tuner_ops.release(fe);
-               symbol_put_addr(fe->ops.tuner_ops.release);
-       }
-       if (fe->ops.analog_ops.release) {
-               fe->ops.analog_ops.release(fe);
-               symbol_put_addr(fe->ops.analog_ops.release);
-       }
-}
-#else
-#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
-       FUNCTION(ARGS); \
-})
-
-static void tuner_detach(struct dvb_frontend *fe)
-{
-       if (fe->ops.tuner_ops.release)
-               fe->ops.tuner_ops.release(fe);
-       if (fe->ops.analog_ops.release)
-               fe->ops.analog_ops.release(fe);
-}
-#endif
-
-
-static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
-{
-       return container_of(sd, struct tuner, sd);
-}
-
-/*
- * struct analog_demod_ops callbacks
- */
-
-static void fe_set_params(struct dvb_frontend *fe,
-                         struct analog_parameters *params)
-{
-       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
-       struct tuner *t = fe->analog_demod_priv;
-
-       if (NULL == fe_tuner_ops->set_analog_params) {
-               tuner_warn("Tuner frontend module has no way to set freq\n");
-               return;
-       }
-       fe_tuner_ops->set_analog_params(fe, params);
-}
-
-static void fe_standby(struct dvb_frontend *fe)
-{
-       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
-
-       if (fe_tuner_ops->sleep)
-               fe_tuner_ops->sleep(fe);
-}
-
-static int fe_has_signal(struct dvb_frontend *fe)
-{
-       u16 strength = 0;
-
-       if (fe->ops.tuner_ops.get_rf_strength)
-               fe->ops.tuner_ops.get_rf_strength(fe, &strength);
-
-       return strength;
-}
-
-static int fe_get_afc(struct dvb_frontend *fe)
-{
-       s32 afc = 0;
-
-       if (fe->ops.tuner_ops.get_afc)
-               fe->ops.tuner_ops.get_afc(fe, &afc);
-
-       return 0;
-}
-
-static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
-       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
-       struct tuner *t = fe->analog_demod_priv;
-
-       if (fe_tuner_ops->set_config)
-               return fe_tuner_ops->set_config(fe, priv_cfg);
-
-       tuner_warn("Tuner frontend module has no way to set config\n");
-
-       return 0;
-}
-
-static void tuner_status(struct dvb_frontend *fe);
-
-static struct analog_demod_ops tuner_analog_ops = {
-       .set_params     = fe_set_params,
-       .standby        = fe_standby,
-       .has_signal     = fe_has_signal,
-       .get_afc        = fe_get_afc,
-       .set_config     = fe_set_config,
-       .tuner_status   = tuner_status
-};
-
-/*
- * Functions to select between radio and TV and tuner probe/remove functions
- */
-
-/**
- * set_type - Sets the tuner type for a given device
- *
- * @c:                 i2c_client descriptoy
- * @type:              type of the tuner (e. g. tuner number)
- * @new_mode_mask:     Indicates if tuner supports TV and/or Radio
- * @new_config:                an optional parameter ranging from 0-255 used by
-                       a few tuners to adjust an internal parameter,
-                       like LNA mode
- * @tuner_callback:    an optional function to be called when switching
- *                     to analog mode
- *
- * This function applys the tuner config to tuner specified
- * by tun_setup structure. It contains several per-tuner initialization "magic"
- */
-static void set_type(struct i2c_client *c, unsigned int type,
-                    unsigned int new_mode_mask, unsigned int new_config,
-                    int (*tuner_callback) (void *dev, int component, int cmd, int arg))
-{
-       struct tuner *t = to_tuner(i2c_get_clientdata(c));
-       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-       unsigned char buffer[4];
-       int tune_now = 1;
-
-       if (type == UNSET || type == TUNER_ABSENT) {
-               tuner_dbg("tuner 0x%02x: Tuner type absent\n", c->addr);
-               return;
-       }
-
-       t->type = type;
-       /* prevent invalid config values */
-       t->config = new_config < 256 ? new_config : 0;
-       if (tuner_callback != NULL) {
-               tuner_dbg("defining GPIO callback\n");
-               t->fe.callback = tuner_callback;
-       }
-
-       /* discard private data, in case set_type() was previously called */
-       tuner_detach(&t->fe);
-       t->fe.analog_demod_priv = NULL;
-
-       switch (t->type) {
-       case TUNER_MT2032:
-               if (!dvb_attach(microtune_attach,
-                          &t->fe, t->i2c->adapter, t->i2c->addr))
-                       goto attach_failed;
-               break;
-       case TUNER_PHILIPS_TDA8290:
-       {
-               struct tda829x_config cfg = {
-                       .lna_cfg        = t->config,
-               };
-               if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter,
-                               t->i2c->addr, &cfg))
-                       goto attach_failed;
-               break;
-       }
-       case TUNER_TEA5767:
-               if (!dvb_attach(tea5767_attach, &t->fe,
-                               t->i2c->adapter, t->i2c->addr))
-                       goto attach_failed;
-               t->mode_mask = T_RADIO;
-               break;
-       case TUNER_TEA5761:
-               if (!dvb_attach(tea5761_attach, &t->fe,
-                               t->i2c->adapter, t->i2c->addr))
-                       goto attach_failed;
-               t->mode_mask = T_RADIO;
-               break;
-       case TUNER_PHILIPS_FMD1216ME_MK3:
-       case TUNER_PHILIPS_FMD1216MEX_MK3:
-               buffer[0] = 0x0b;
-               buffer[1] = 0xdc;
-               buffer[2] = 0x9c;
-               buffer[3] = 0x60;
-               i2c_master_send(c, buffer, 4);
-               mdelay(1);
-               buffer[2] = 0x86;
-               buffer[3] = 0x54;
-               i2c_master_send(c, buffer, 4);
-               if (!dvb_attach(simple_tuner_attach, &t->fe,
-                               t->i2c->adapter, t->i2c->addr, t->type))
-                       goto attach_failed;
-               break;
-       case TUNER_PHILIPS_TD1316:
-               buffer[0] = 0x0b;
-               buffer[1] = 0xdc;
-               buffer[2] = 0x86;
-               buffer[3] = 0xa4;
-               i2c_master_send(c, buffer, 4);
-               if (!dvb_attach(simple_tuner_attach, &t->fe,
-                               t->i2c->adapter, t->i2c->addr, t->type))
-                       goto attach_failed;
-               break;
-       case TUNER_XC2028:
-       {
-               struct xc2028_config cfg = {
-                       .i2c_adap  = t->i2c->adapter,
-                       .i2c_addr  = t->i2c->addr,
-               };
-               if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
-                       goto attach_failed;
-               tune_now = 0;
-               break;
-       }
-       case TUNER_TDA9887:
-               if (!dvb_attach(tda9887_attach,
-                          &t->fe, t->i2c->adapter, t->i2c->addr))
-                       goto attach_failed;
-               break;
-       case TUNER_XC5000:
-       {
-               struct xc5000_config xc5000_cfg = {
-                       .i2c_address = t->i2c->addr,
-                       /* if_khz will be set at dvb_attach() */
-                       .if_khz   = 0,
-               };
-
-               if (!dvb_attach(xc5000_attach,
-                               &t->fe, t->i2c->adapter, &xc5000_cfg))
-                       goto attach_failed;
-               tune_now = 0;
-               break;
-       }
-       case TUNER_XC5000C:
-       {
-               struct xc5000_config xc5000c_cfg = {
-                       .i2c_address = t->i2c->addr,
-                       /* if_khz will be set at dvb_attach() */
-                       .if_khz   = 0,
-                       .chip_id  = XC5000C,
-               };
-
-               if (!dvb_attach(xc5000_attach,
-                               &t->fe, t->i2c->adapter, &xc5000c_cfg))
-                       goto attach_failed;
-               tune_now = 0;
-               break;
-       }
-       case TUNER_NXP_TDA18271:
-       {
-               struct tda18271_config cfg = {
-                       .config = t->config,
-                       .small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
-               };
-
-               if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr,
-                               t->i2c->adapter, &cfg))
-                       goto attach_failed;
-               tune_now = 0;
-               break;
-       }
-       case TUNER_XC4000:
-       {
-               struct xc4000_config xc4000_cfg = {
-                       .i2c_address      = t->i2c->addr,
-                       /* FIXME: the correct parameters will be set */
-                       /* only when the digital dvb_attach() occurs */
-                       .default_pm       = 0,
-                       .dvb_amplitude    = 0,
-                       .set_smoothedcvbs = 0,
-                       .if_khz           = 0
-               };
-               if (!dvb_attach(xc4000_attach,
-                               &t->fe, t->i2c->adapter, &xc4000_cfg))
-                       goto attach_failed;
-               tune_now = 0;
-               break;
-       }
-       default:
-               if (!dvb_attach(simple_tuner_attach, &t->fe,
-                               t->i2c->adapter, t->i2c->addr, t->type))
-                       goto attach_failed;
-
-               break;
-       }
-
-       if ((NULL == analog_ops->set_params) &&
-           (fe_tuner_ops->set_analog_params)) {
-
-               t->name = fe_tuner_ops->info.name;
-
-               t->fe.analog_demod_priv = t;
-               memcpy(analog_ops, &tuner_analog_ops,
-                      sizeof(struct analog_demod_ops));
-
-       } else {
-               t->name = analog_ops->info.name;
-       }
-
-       tuner_dbg("type set to %s\n", t->name);
-
-       t->mode_mask = new_mode_mask;
-
-       /* Some tuners require more initialization setup before use,
-          such as firmware download or device calibration.
-          trying to set a frequency here will just fail
-          FIXME: better to move set_freq to the tuner code. This is needed
-          on analog tuners for PLL to properly work
-        */
-       if (tune_now) {
-               if (V4L2_TUNER_RADIO == t->mode)
-                       set_radio_freq(c, t->radio_freq);
-               else
-                       set_tv_freq(c, t->tv_freq);
-       }
-
-       tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
-                 c->adapter->name, c->driver->driver.name, c->addr << 1, type,
-                 t->mode_mask);
-       return;
-
-attach_failed:
-       tuner_dbg("Tuner attach for type = %d failed.\n", t->type);
-       t->type = TUNER_ABSENT;
-
-       return;
-}
-
-/**
- * tuner_s_type_addr - Sets the tuner type for a device
- *
- * @sd:                subdev descriptor
- * @tun_setup: type to be associated to a given tuner i2c address
- *
- * This function applys the tuner config to tuner specified
- * by tun_setup structure.
- * If tuner I2C address is UNSET, then it will only set the device
- * if the tuner supports the mode specified in the call.
- * If the address is specified, the change will be applied only if
- * tuner I2C address matches.
- * The call can change the tuner number and the tuner mode.
- */
-static int tuner_s_type_addr(struct v4l2_subdev *sd,
-                            struct tuner_setup *tun_setup)
-{
-       struct tuner *t = to_tuner(sd);
-       struct i2c_client *c = v4l2_get_subdevdata(sd);
-
-       tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
-                       tun_setup->type,
-                       tun_setup->addr,
-                       tun_setup->mode_mask,
-                       tun_setup->config);
-
-       if ((t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
-           (t->mode_mask & tun_setup->mode_mask))) ||
-           (tun_setup->addr == c->addr)) {
-               set_type(c, tun_setup->type, tun_setup->mode_mask,
-                        tun_setup->config, tun_setup->tuner_callback);
-       } else
-               tuner_dbg("set addr discarded for type %i, mask %x. "
-                         "Asked to change tuner at addr 0x%02x, with mask %x\n",
-                         t->type, t->mode_mask,
-                         tun_setup->addr, tun_setup->mode_mask);
-
-       return 0;
-}
-
-/**
- * tuner_s_config - Sets tuner configuration
- *
- * @sd:                subdev descriptor
- * @cfg:       tuner configuration
- *
- * Calls tuner set_config() private function to set some tuner-internal
- * parameters
- */
-static int tuner_s_config(struct v4l2_subdev *sd,
-                         const struct v4l2_priv_tun_config *cfg)
-{
-       struct tuner *t = to_tuner(sd);
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
-       if (t->type != cfg->tuner)
-               return 0;
-
-       if (analog_ops->set_config) {
-               analog_ops->set_config(&t->fe, cfg->priv);
-               return 0;
-       }
-
-       tuner_dbg("Tuner frontend module has no way to set config\n");
-       return 0;
-}
-
-/**
- * tuner_lookup - Seek for tuner adapters
- *
- * @adap:      i2c_adapter struct
- * @radio:     pointer to be filled if the adapter is radio
- * @tv:                pointer to be filled if the adapter is TV
- *
- * Search for existing radio and/or TV tuners on the given I2C adapter,
- * discarding demod-only adapters (tda9887).
- *
- * Note that when this function is called from tuner_probe you can be
- * certain no other devices will be added/deleted at the same time, I2C
- * core protects against that.
- */
-static void tuner_lookup(struct i2c_adapter *adap,
-               struct tuner **radio, struct tuner **tv)
-{
-       struct tuner *pos;
-
-       *radio = NULL;
-       *tv = NULL;
-
-       list_for_each_entry(pos, &tuner_list, list) {
-               int mode_mask;
-
-               if (pos->i2c->adapter != adap ||
-                   strcmp(pos->i2c->driver->driver.name, "tuner"))
-                       continue;
-
-               mode_mask = pos->mode_mask;
-               if (*radio == NULL && mode_mask == T_RADIO)
-                       *radio = pos;
-               /* Note: currently TDA9887 is the only demod-only
-                  device. If other devices appear then we need to
-                  make this test more general. */
-               else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
-                        (pos->mode_mask & T_ANALOG_TV))
-                       *tv = pos;
-       }
-}
-
-/**
- *tuner_probe - Probes the existing tuners on an I2C bus
- *
- * @client:    i2c_client descriptor
- * @id:                not used
- *
- * This routine probes for tuners at the expected I2C addresses. On most
- * cases, if a device answers to a given I2C address, it assumes that the
- * device is a tuner. On a few cases, however, an additional logic is needed
- * to double check if the device is really a tuner, or to identify the tuner
- * type, like on tea5767/5761 devices.
- *
- * During client attach, set_type is called by adapter's attach_inform callback.
- * set_type must then be completed by tuner_probe.
- */
-static int tuner_probe(struct i2c_client *client,
-                      const struct i2c_device_id *id)
-{
-       struct tuner *t;
-       struct tuner *radio;
-       struct tuner *tv;
-
-       t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
-       if (NULL == t)
-               return -ENOMEM;
-       v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
-       t->i2c = client;
-       t->name = "(tuner unset)";
-       t->type = UNSET;
-       t->audmode = V4L2_TUNER_MODE_STEREO;
-       t->standby = 1;
-       t->radio_freq = 87.5 * 16000;   /* Initial freq range */
-       t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */
-
-       if (show_i2c) {
-               unsigned char buffer[16];
-               int i, rc;
-
-               memset(buffer, 0, sizeof(buffer));
-               rc = i2c_master_recv(client, buffer, sizeof(buffer));
-               tuner_info("I2C RECV = ");
-               for (i = 0; i < rc; i++)
-                       printk(KERN_CONT "%02x ", buffer[i]);
-               printk("\n");
-       }
-
-       /* autodetection code based on the i2c addr */
-       if (!no_autodetect) {
-               switch (client->addr) {
-               case 0x10:
-                       if (tuner_symbol_probe(tea5761_autodetection,
-                                              t->i2c->adapter,
-                                              t->i2c->addr) >= 0) {
-                               t->type = TUNER_TEA5761;
-                               t->mode_mask = T_RADIO;
-                               tuner_lookup(t->i2c->adapter, &radio, &tv);
-                               if (tv)
-                                       tv->mode_mask &= ~T_RADIO;
-
-                               goto register_client;
-                       }
-                       kfree(t);
-                       return -ENODEV;
-               case 0x42:
-               case 0x43:
-               case 0x4a:
-               case 0x4b:
-                       /* If chip is not tda8290, don't register.
-                          since it can be tda9887*/
-                       if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
-                                              t->i2c->addr) >= 0) {
-                               tuner_dbg("tda829x detected\n");
-                       } else {
-                               /* Default is being tda9887 */
-                               t->type = TUNER_TDA9887;
-                               t->mode_mask = T_RADIO | T_ANALOG_TV;
-                               goto register_client;
-                       }
-                       break;
-               case 0x60:
-                       if (tuner_symbol_probe(tea5767_autodetection,
-                                              t->i2c->adapter, t->i2c->addr)
-                                       >= 0) {
-                               t->type = TUNER_TEA5767;
-                               t->mode_mask = T_RADIO;
-                               /* Sets freq to FM range */
-                               tuner_lookup(t->i2c->adapter, &radio, &tv);
-                               if (tv)
-                                       tv->mode_mask &= ~T_RADIO;
-
-                               goto register_client;
-                       }
-                       break;
-               }
-       }
-
-       /* Initializes only the first TV tuner on this adapter. Why only the
-          first? Because there are some devices (notably the ones with TI
-          tuners) that have more than one i2c address for the *same* device.
-          Experience shows that, except for just one case, the first
-          address is the right one. The exception is a Russian tuner
-          (ACORP_Y878F). So, the desired behavior is just to enable the
-          first found TV tuner. */
-       tuner_lookup(t->i2c->adapter, &radio, &tv);
-       if (tv == NULL) {
-               t->mode_mask = T_ANALOG_TV;
-               if (radio == NULL)
-                       t->mode_mask |= T_RADIO;
-               tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
-       }
-
-       /* Should be just before return */
-register_client:
-       /* Sets a default mode */
-       if (t->mode_mask & T_ANALOG_TV)
-               t->mode = V4L2_TUNER_ANALOG_TV;
-       else
-               t->mode = V4L2_TUNER_RADIO;
-       set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
-       list_add_tail(&t->list, &tuner_list);
-
-       tuner_info("Tuner %d found with type(s)%s%s.\n",
-                  t->type,
-                  t->mode_mask & T_RADIO ? " Radio" : "",
-                  t->mode_mask & T_ANALOG_TV ? " TV" : "");
-       return 0;
-}
-
-/**
- * tuner_remove - detaches a tuner
- *
- * @client:    i2c_client descriptor
- */
-
-static int tuner_remove(struct i2c_client *client)
-{
-       struct tuner *t = to_tuner(i2c_get_clientdata(client));
-
-       v4l2_device_unregister_subdev(&t->sd);
-       tuner_detach(&t->fe);
-       t->fe.analog_demod_priv = NULL;
-
-       list_del(&t->list);
-       kfree(t);
-       return 0;
-}
-
-/*
- * Functions to switch between Radio and TV
- *
- * A few cards have a separate I2C tuner for radio. Those routines
- * take care of switching between TV/Radio mode, filtering only the
- * commands that apply to the Radio or TV tuner.
- */
-
-/**
- * check_mode - Verify if tuner supports the requested mode
- * @t: a pointer to the module's internal struct_tuner
- *
- * This function checks if the tuner is capable of tuning analog TV,
- * digital TV or radio, depending on what the caller wants. If the
- * tuner can't support that mode, it returns -EINVAL. Otherwise, it
- * returns 0.
- * This function is needed for boards that have a separate tuner for
- * radio (like devices with tea5767).
- * NOTE: mt20xx uses V4L2_TUNER_DIGITAL_TV and calls set_tv_freq to
- *       select a TV frequency. So, t_mode = T_ANALOG_TV could actually
- *      be used to represent a Digital TV too.
- */
-static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode)
-{
-       int t_mode;
-       if (mode == V4L2_TUNER_RADIO)
-               t_mode = T_RADIO;
-       else
-               t_mode = T_ANALOG_TV;
-
-       if ((t_mode & t->mode_mask) == 0)
-               return -EINVAL;
-
-       return 0;
-}
-
-/**
- * set_mode - Switch tuner to other mode.
- * @t:         a pointer to the module's internal struct_tuner
- * @mode:      enum v4l2_type (radio or TV)
- *
- * If tuner doesn't support the needed mode (radio or TV), prints a
- * debug message and returns -EINVAL, changing its state to standby.
- * Otherwise, changes the mode and returns 0.
- */
-static int set_mode(struct tuner *t, enum v4l2_tuner_type mode)
-{
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
-       if (mode != t->mode) {
-               if (check_mode(t, mode) == -EINVAL) {
-                       tuner_dbg("Tuner doesn't support mode %d. "
-                                 "Putting tuner to sleep\n", mode);
-                       t->standby = true;
-                       if (analog_ops->standby)
-                               analog_ops->standby(&t->fe);
-                       return -EINVAL;
-               }
-               t->mode = mode;
-               tuner_dbg("Changing to mode %d\n", mode);
-       }
-       return 0;
-}
-
-/**
- * set_freq - Set the tuner to the desired frequency.
- * @t:         a pointer to the module's internal struct_tuner
- * @freq:      frequency to set (0 means to use the current frequency)
- */
-static void set_freq(struct tuner *t, unsigned int freq)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
-
-       if (t->mode == V4L2_TUNER_RADIO) {
-               if (!freq)
-                       freq = t->radio_freq;
-               set_radio_freq(client, freq);
-       } else {
-               if (!freq)
-                       freq = t->tv_freq;
-               set_tv_freq(client, freq);
-       }
-}
-
-/*
- * Functions that are specific for TV mode
- */
-
-/**
- * set_tv_freq - Set tuner frequency,  freq in Units of 62.5 kHz = 1/16MHz
- *
- * @c: i2c_client descriptor
- * @freq: frequency
- */
-static void set_tv_freq(struct i2c_client *c, unsigned int freq)
-{
-       struct tuner *t = to_tuner(i2c_get_clientdata(c));
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
-       struct analog_parameters params = {
-               .mode      = t->mode,
-               .audmode   = t->audmode,
-               .std       = t->std
-       };
-
-       if (t->type == UNSET) {
-               tuner_warn("tuner type not set\n");
-               return;
-       }
-       if (NULL == analog_ops->set_params) {
-               tuner_warn("Tuner has no way to set tv freq\n");
-               return;
-       }
-       if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
-               tuner_dbg("TV freq (%d.%02d) out of range (%d-%d)\n",
-                          freq / 16, freq % 16 * 100 / 16, tv_range[0],
-                          tv_range[1]);
-               /* V4L2 spec: if the freq is not possible then the closest
-                  possible value should be selected */
-               if (freq < tv_range[0] * 16)
-                       freq = tv_range[0] * 16;
-               else
-                       freq = tv_range[1] * 16;
-       }
-       params.frequency = freq;
-       tuner_dbg("tv freq set to %d.%02d\n",
-                       freq / 16, freq % 16 * 100 / 16);
-       t->tv_freq = freq;
-       t->standby = false;
-
-       analog_ops->set_params(&t->fe, &params);
-}
-
-/**
- * tuner_fixup_std - force a given video standard variant
- *
- * @t: tuner internal struct
- * @std:       TV standard
- *
- * A few devices or drivers have problem to detect some standard variations.
- * On other operational systems, the drivers generally have a per-country
- * code, and some logic to apply per-country hacks. V4L2 API doesn't provide
- * such hacks. Instead, it relies on a proper video standard selection from
- * the userspace application. However, as some apps are buggy, not allowing
- * to distinguish all video standard variations, a modprobe parameter can
- * be used to force a video standard match.
- */
-static v4l2_std_id tuner_fixup_std(struct tuner *t, v4l2_std_id std)
-{
-       if (pal[0] != '-' && (std & V4L2_STD_PAL) == V4L2_STD_PAL) {
-               switch (pal[0]) {
-               case '6':
-                       return V4L2_STD_PAL_60;
-               case 'b':
-               case 'B':
-               case 'g':
-               case 'G':
-                       return V4L2_STD_PAL_BG;
-               case 'i':
-               case 'I':
-                       return V4L2_STD_PAL_I;
-               case 'd':
-               case 'D':
-               case 'k':
-               case 'K':
-                       return V4L2_STD_PAL_DK;
-               case 'M':
-               case 'm':
-                       return V4L2_STD_PAL_M;
-               case 'N':
-               case 'n':
-                       if (pal[1] == 'c' || pal[1] == 'C')
-                               return V4L2_STD_PAL_Nc;
-                       return V4L2_STD_PAL_N;
-               default:
-                       tuner_warn("pal= argument not recognised\n");
-                       break;
-               }
-       }
-       if (secam[0] != '-' && (std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
-               switch (secam[0]) {
-               case 'b':
-               case 'B':
-               case 'g':
-               case 'G':
-               case 'h':
-               case 'H':
-                       return V4L2_STD_SECAM_B |
-                              V4L2_STD_SECAM_G |
-                              V4L2_STD_SECAM_H;
-               case 'd':
-               case 'D':
-               case 'k':
-               case 'K':
-                       return V4L2_STD_SECAM_DK;
-               case 'l':
-               case 'L':
-                       if ((secam[1] == 'C') || (secam[1] == 'c'))
-                               return V4L2_STD_SECAM_LC;
-                       return V4L2_STD_SECAM_L;
-               default:
-                       tuner_warn("secam= argument not recognised\n");
-                       break;
-               }
-       }
-
-       if (ntsc[0] != '-' && (std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
-               switch (ntsc[0]) {
-               case 'm':
-               case 'M':
-                       return V4L2_STD_NTSC_M;
-               case 'j':
-               case 'J':
-                       return V4L2_STD_NTSC_M_JP;
-               case 'k':
-               case 'K':
-                       return V4L2_STD_NTSC_M_KR;
-               default:
-                       tuner_info("ntsc= argument not recognised\n");
-                       break;
-               }
-       }
-       return std;
-}
-
-/*
- * Functions that are specific for Radio mode
- */
-
-/**
- * set_radio_freq - Set tuner frequency,  freq in Units of 62.5 Hz  = 1/16kHz
- *
- * @c: i2c_client descriptor
- * @freq: frequency
- */
-static void set_radio_freq(struct i2c_client *c, unsigned int freq)
-{
-       struct tuner *t = to_tuner(i2c_get_clientdata(c));
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
-       struct analog_parameters params = {
-               .mode      = t->mode,
-               .audmode   = t->audmode,
-               .std       = t->std
-       };
-
-       if (t->type == UNSET) {
-               tuner_warn("tuner type not set\n");
-               return;
-       }
-       if (NULL == analog_ops->set_params) {
-               tuner_warn("tuner has no way to set radio frequency\n");
-               return;
-       }
-       if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
-               tuner_dbg("radio freq (%d.%02d) out of range (%d-%d)\n",
-                          freq / 16000, freq % 16000 * 100 / 16000,
-                          radio_range[0], radio_range[1]);
-               /* V4L2 spec: if the freq is not possible then the closest
-                  possible value should be selected */
-               if (freq < radio_range[0] * 16000)
-                       freq = radio_range[0] * 16000;
-               else
-                       freq = radio_range[1] * 16000;
-       }
-       params.frequency = freq;
-       tuner_dbg("radio freq set to %d.%02d\n",
-                       freq / 16000, freq % 16000 * 100 / 16000);
-       t->radio_freq = freq;
-       t->standby = false;
-
-       analog_ops->set_params(&t->fe, &params);
-}
-
-/*
- * Debug function for reporting tuner status to userspace
- */
-
-/**
- * tuner_status - Dumps the current tuner status at dmesg
- * @fe: pointer to struct dvb_frontend
- *
- * This callback is used only for driver debug purposes, answering to
- * VIDIOC_LOG_STATUS. No changes should happen on this call.
- */
-static void tuner_status(struct dvb_frontend *fe)
-{
-       struct tuner *t = fe->analog_demod_priv;
-       unsigned long freq, freq_fraction;
-       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
-       struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
-       const char *p;
-
-       switch (t->mode) {
-       case V4L2_TUNER_RADIO:
-               p = "radio";
-               break;
-       case V4L2_TUNER_DIGITAL_TV: /* Used by mt20xx */
-               p = "digital TV";
-               break;
-       case V4L2_TUNER_ANALOG_TV:
-       default:
-               p = "analog TV";
-               break;
-       }
-       if (t->mode == V4L2_TUNER_RADIO) {
-               freq = t->radio_freq / 16000;
-               freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
-       } else {
-               freq = t->tv_freq / 16;
-               freq_fraction = (t->tv_freq % 16) * 100 / 16;
-       }
-       tuner_info("Tuner mode:      %s%s\n", p,
-                  t->standby ? " on standby mode" : "");
-       tuner_info("Frequency:       %lu.%02lu MHz\n", freq, freq_fraction);
-       tuner_info("Standard:        0x%08lx\n", (unsigned long)t->std);
-       if (t->mode != V4L2_TUNER_RADIO)
-               return;
-       if (fe_tuner_ops->get_status) {
-               u32 tuner_status;
-
-               fe_tuner_ops->get_status(&t->fe, &tuner_status);
-               if (tuner_status & TUNER_STATUS_LOCKED)
-                       tuner_info("Tuner is locked.\n");
-               if (tuner_status & TUNER_STATUS_STEREO)
-                       tuner_info("Stereo:          yes\n");
-       }
-       if (analog_ops->has_signal)
-               tuner_info("Signal strength: %d\n",
-                          analog_ops->has_signal(fe));
-}
-
-/*
- * Function to splicitly change mode to radio. Probably not needed anymore
- */
-
-static int tuner_s_radio(struct v4l2_subdev *sd)
-{
-       struct tuner *t = to_tuner(sd);
-
-       if (set_mode(t, V4L2_TUNER_RADIO) == 0)
-               set_freq(t, 0);
-       return 0;
-}
-
-/*
- * Tuner callbacks to handle userspace ioctl's
- */
-
-/**
- * tuner_s_power - controls the power state of the tuner
- * @sd: pointer to struct v4l2_subdev
- * @on: a zero value puts the tuner to sleep, non-zero wakes it up
- */
-static int tuner_s_power(struct v4l2_subdev *sd, int on)
-{
-       struct tuner *t = to_tuner(sd);
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
-       if (on) {
-               if (t->standby && set_mode(t, t->mode) == 0) {
-                       tuner_dbg("Waking up tuner\n");
-                       set_freq(t, 0);
-               }
-               return 0;
-       }
-
-       tuner_dbg("Putting tuner to sleep\n");
-       t->standby = true;
-       if (analog_ops->standby)
-               analog_ops->standby(&t->fe);
-       return 0;
-}
-
-static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
-{
-       struct tuner *t = to_tuner(sd);
-
-       if (set_mode(t, V4L2_TUNER_ANALOG_TV))
-               return 0;
-
-       t->std = tuner_fixup_std(t, std);
-       if (t->std != std)
-               tuner_dbg("Fixup standard %llx to %llx\n", std, t->std);
-       set_freq(t, 0);
-       return 0;
-}
-
-static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
-{
-       struct tuner *t = to_tuner(sd);
-
-       if (set_mode(t, f->type) == 0)
-               set_freq(t, f->frequency);
-       return 0;
-}
-
-/**
- * tuner_g_frequency - Get the tuned frequency for the tuner
- * @sd: pointer to struct v4l2_subdev
- * @f: pointer to struct v4l2_frequency
- *
- * At return, the structure f will be filled with tuner frequency
- * if the tuner matches the f->type.
- * Note: f->type should be initialized before calling it.
- * This is done by either video_ioctl2 or by the bridge driver.
- */
-static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
-{
-       struct tuner *t = to_tuner(sd);
-       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-
-       if (check_mode(t, f->type) == -EINVAL)
-               return 0;
-       if (f->type == t->mode && fe_tuner_ops->get_frequency && !t->standby) {
-               u32 abs_freq;
-
-               fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
-               f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
-                       DIV_ROUND_CLOSEST(abs_freq * 2, 125) :
-                       DIV_ROUND_CLOSEST(abs_freq, 62500);
-       } else {
-               f->frequency = (V4L2_TUNER_RADIO == f->type) ?
-                       t->radio_freq : t->tv_freq;
-       }
-       return 0;
-}
-
-/**
- * tuner_g_tuner - Fill in tuner information
- * @sd: pointer to struct v4l2_subdev
- * @vt: pointer to struct v4l2_tuner
- *
- * At return, the structure vt will be filled with tuner information
- * if the tuner matches vt->type.
- * Note: vt->type should be initialized before calling it.
- * This is done by either video_ioctl2 or by the bridge driver.
- */
-static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
-       struct tuner *t = to_tuner(sd);
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-
-       if (check_mode(t, vt->type) == -EINVAL)
-               return 0;
-       if (vt->type == t->mode && analog_ops->get_afc)
-               vt->afc = analog_ops->get_afc(&t->fe);
-       if (analog_ops->has_signal)
-               vt->signal = analog_ops->has_signal(&t->fe);
-       if (vt->type != V4L2_TUNER_RADIO) {
-               vt->capability |= V4L2_TUNER_CAP_NORM;
-               vt->rangelow = tv_range[0] * 16;
-               vt->rangehigh = tv_range[1] * 16;
-               return 0;
-       }
-
-       /* radio mode */
-       if (vt->type == t->mode) {
-               vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
-               if (fe_tuner_ops->get_status) {
-                       u32 tuner_status;
-
-                       fe_tuner_ops->get_status(&t->fe, &tuner_status);
-                       vt->rxsubchans =
-                               (tuner_status & TUNER_STATUS_STEREO) ?
-                               V4L2_TUNER_SUB_STEREO :
-                               V4L2_TUNER_SUB_MONO;
-               }
-               vt->audmode = t->audmode;
-       }
-       vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
-       vt->rangelow = radio_range[0] * 16000;
-       vt->rangehigh = radio_range[1] * 16000;
-
-       return 0;
-}
-
-/**
- * tuner_s_tuner - Set the tuner's audio mode
- * @sd: pointer to struct v4l2_subdev
- * @vt: pointer to struct v4l2_tuner
- *
- * Sets the audio mode if the tuner matches vt->type.
- * Note: vt->type should be initialized before calling it.
- * This is done by either video_ioctl2 or by the bridge driver.
- */
-static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
-       struct tuner *t = to_tuner(sd);
-
-       if (set_mode(t, vt->type))
-               return 0;
-
-       if (t->mode == V4L2_TUNER_RADIO)
-               t->audmode = vt->audmode;
-       set_freq(t, 0);
-
-       return 0;
-}
-
-static int tuner_log_status(struct v4l2_subdev *sd)
-{
-       struct tuner *t = to_tuner(sd);
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
-       if (analog_ops->tuner_status)
-               analog_ops->tuner_status(&t->fe);
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int tuner_suspend(struct device *dev)
-{
-       struct i2c_client *c = to_i2c_client(dev);
-       struct tuner *t = to_tuner(i2c_get_clientdata(c));
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
-
-       tuner_dbg("suspend\n");
-
-       if (!t->standby && analog_ops->standby)
-               analog_ops->standby(&t->fe);
-
-       return 0;
-}
-
-static int tuner_resume(struct device *dev)
-{
-       struct i2c_client *c = to_i2c_client(dev);
-       struct tuner *t = to_tuner(i2c_get_clientdata(c));
-
-       tuner_dbg("resume\n");
-
-       if (!t->standby)
-               if (set_mode(t, t->mode) == 0)
-                       set_freq(t, 0);
-
-       return 0;
-}
-#endif
-
-static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
-{
-       struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
-       /* TUNER_SET_CONFIG is still called by tuner-simple.c, so we have
-          to handle it here.
-          There must be a better way of doing this... */
-       switch (cmd) {
-       case TUNER_SET_CONFIG:
-               return tuner_s_config(sd, arg);
-       }
-       return -ENOIOCTLCMD;
-}
-
-/*
- * Callback structs
- */
-
-static const struct v4l2_subdev_core_ops tuner_core_ops = {
-       .log_status = tuner_log_status,
-       .s_std = tuner_s_std,
-       .s_power = tuner_s_power,
-};
-
-static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
-       .s_radio = tuner_s_radio,
-       .g_tuner = tuner_g_tuner,
-       .s_tuner = tuner_s_tuner,
-       .s_frequency = tuner_s_frequency,
-       .g_frequency = tuner_g_frequency,
-       .s_type_addr = tuner_s_type_addr,
-       .s_config = tuner_s_config,
-};
-
-static const struct v4l2_subdev_ops tuner_ops = {
-       .core = &tuner_core_ops,
-       .tuner = &tuner_tuner_ops,
-};
-
-/*
- * I2C structs and module init functions
- */
-
-static const struct dev_pm_ops tuner_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(tuner_suspend, tuner_resume)
-};
-
-static const struct i2c_device_id tuner_id[] = {
-       { "tuner", }, /* autodetect */
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, tuner_id);
-
-static struct i2c_driver tuner_driver = {
-       .driver = {
-               .owner  = THIS_MODULE,
-               .name   = "tuner",
-               .pm     = &tuner_pm_ops,
-       },
-       .probe          = tuner_probe,
-       .remove         = tuner_remove,
-       .command        = tuner_command,
-       .id_table       = tuner_id,
-};
-
-module_i2c_driver(tuner_driver);
-
-MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
deleted file mode 100644 (file)
index 105f88c..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- *     Video for Linux Two
- *
- *     A generic video device interface for the LINUX operating system
- *     using a set of device structures/vectors for low level operations.
- *
- *     This file replaces the videodev.c file that comes with the
- *     regular kernel distribution.
- *
- *     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.
- *
- * Author:     Bill Dirks <bill@thedirks.org>
- *             based on code by Alan Cox, <alan@cymru.net>
- *
- */
-
-/*
- * Video capture interface for Linux
- *
- *     A generic video device interface for the LINUX operating system
- *     using a set of device structures/vectors for low level operations.
- *
- *             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.
- *
- * Author:     Alan Cox, <alan@lxorguk.ukuu.org.uk>
- *
- * Fixes:
- */
-
-/*
- * Video4linux 1/2 integration by Justin Schoeman
- * <justin@suntiger.ee.up.ac.za>
- * 2.4 PROCFS support ported from 2.4 kernels by
- *  Iñaki García Etxebarria <garetxe@euskalnet.net>
- * Makefile fix by "W. Michael Petullo" <mike@flyn.org>
- * 2.4 devfs support ported from 2.4 kernels by
- *  Dan Merillat <dan@merillat.org>
- * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman)
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/i2c.h>
-#if defined(CONFIG_SPI)
-#include <linux/spi/spi.h>
-#endif
-#include <asm/uaccess.h>
-#include <asm/pgtable.h>
-#include <asm/io.h>
-#include <asm/div64.h>
-#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>
-
-MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr");
-MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers");
-MODULE_LICENSE("GPL");
-
-/*
- *
- *     V 4 L 2   D R I V E R   H E L P E R   A P I
- *
- */
-
-/*
- *  Video Standard Operations (contributed by Michael Schimek)
- */
-
-/* Helper functions for control handling                            */
-
-/* Check for correctness of the ctrl's value based on the data from
-   struct v4l2_queryctrl and the available menu items. Note that
-   menu_items may be NULL, in that case it is ignored. */
-int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
-               const char * const *menu_items)
-{
-       if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
-               return -EINVAL;
-       if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
-               return -EBUSY;
-       if (qctrl->type == V4L2_CTRL_TYPE_STRING)
-               return 0;
-       if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
-           qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
-           qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
-               return 0;
-       if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
-               return -ERANGE;
-       if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
-               if (menu_items[ctrl->value] == NULL ||
-                   menu_items[ctrl->value][0] == '\0')
-                       return -EINVAL;
-       }
-       if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
-                       (ctrl->value & ~qctrl->maximum))
-               return -ERANGE;
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_check);
-
-/* Fill in a struct v4l2_queryctrl */
-int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
-{
-       const char *name;
-
-       v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
-                      &min, &max, &step, &def, &qctrl->flags);
-
-       if (name == NULL)
-               return -EINVAL;
-
-       qctrl->minimum = min;
-       qctrl->maximum = max;
-       qctrl->step = step;
-       qctrl->default_value = def;
-       qctrl->reserved[0] = qctrl->reserved[1] = 0;
-       strlcpy(qctrl->name, name, sizeof(qctrl->name));
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_fill);
-
-/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
-   the menu. The qctrl pointer may be NULL, in which case it is ignored.
-   If menu_items is NULL, then the menu items are retrieved using
-   v4l2_ctrl_get_menu. */
-int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl,
-              const char * const *menu_items)
-{
-       int i;
-
-       qmenu->reserved = 0;
-       if (menu_items == NULL)
-               menu_items = v4l2_ctrl_get_menu(qmenu->id);
-       if (menu_items == NULL ||
-           (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum)))
-               return -EINVAL;
-       for (i = 0; i < qmenu->index && menu_items[i]; i++) ;
-       if (menu_items[i] == NULL || menu_items[i][0] == '\0')
-               return -EINVAL;
-       strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name));
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_menu);
-
-/* Fill in a struct v4l2_querymenu based on the specified array of valid
-   menu items (terminated by V4L2_CTRL_MENU_IDS_END).
-   Use this if there are 'holes' in the list of valid menu items. */
-int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids)
-{
-       const char * const *menu_items = v4l2_ctrl_get_menu(qmenu->id);
-
-       qmenu->reserved = 0;
-       if (menu_items == NULL || ids == NULL)
-               return -EINVAL;
-       while (*ids != V4L2_CTRL_MENU_IDS_END) {
-               if (*ids++ == qmenu->index) {
-                       strlcpy(qmenu->name, menu_items[qmenu->index],
-                                       sizeof(qmenu->name));
-                       return 0;
-               }
-       }
-       return -EINVAL;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
-
-/* ctrl_classes points to an array of u32 pointers, the last element is
-   a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
-   Each array must be sorted low to high and belong to the same control
-   class. The array of u32 pointers must also be sorted, from low class IDs
-   to high class IDs.
-
-   This function returns the first ID that follows after the given ID.
-   When no more controls are available 0 is returned. */
-u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
-{
-       u32 ctrl_class = V4L2_CTRL_ID2CLASS(id);
-       const u32 *pctrl;
-
-       if (ctrl_classes == NULL)
-               return 0;
-
-       /* if no query is desired, then check if the ID is part of ctrl_classes */
-       if ((id & V4L2_CTRL_FLAG_NEXT_CTRL) == 0) {
-               /* find class */
-               while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) != ctrl_class)
-                       ctrl_classes++;
-               if (*ctrl_classes == NULL)
-                       return 0;
-               pctrl = *ctrl_classes;
-               /* find control ID */
-               while (*pctrl && *pctrl != id) pctrl++;
-               return *pctrl ? id : 0;
-       }
-       id &= V4L2_CTRL_ID_MASK;
-       id++;   /* select next control */
-       /* find first class that matches (or is greater than) the class of
-          the ID */
-       while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) < ctrl_class)
-               ctrl_classes++;
-       /* no more classes */
-       if (*ctrl_classes == NULL)
-               return 0;
-       pctrl = *ctrl_classes;
-       /* find first ctrl within the class that is >= ID */
-       while (*pctrl && *pctrl < id) pctrl++;
-       if (*pctrl)
-               return *pctrl;
-       /* we are at the end of the controls of the current class. */
-       /* continue with next class if available */
-       ctrl_classes++;
-       if (*ctrl_classes == NULL)
-               return 0;
-       return **ctrl_classes;
-}
-EXPORT_SYMBOL(v4l2_ctrl_next);
-
-int v4l2_chip_match_host(const struct v4l2_dbg_match *match)
-{
-       switch (match->type) {
-       case V4L2_CHIP_MATCH_HOST:
-               return match->addr == 0;
-       default:
-               return 0;
-       }
-}
-EXPORT_SYMBOL(v4l2_chip_match_host);
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-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);
-               /* legacy drivers have a ' suffix, don't try to match that */
-               if (len && c->driver->driver.name[len - 1] == '\'')
-                       len--;
-               return len && !strncmp(c->driver->driver.name, match->name, len);
-       case V4L2_CHIP_MATCH_I2C_ADDR:
-               return c->addr == match->addr;
-       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 */
-
-
-void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
-               const struct v4l2_subdev_ops *ops)
-{
-       v4l2_subdev_init(sd, ops);
-       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;
-       /* i2c_client and v4l2_subdev point to one another */
-       v4l2_set_subdevdata(sd, client);
-       i2c_set_clientdata(client, sd);
-       /* initialize name */
-       snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
-               client->driver->driver.name, i2c_adapter_id(client->adapter),
-               client->addr);
-}
-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,
-               const unsigned short *probe_addrs)
-{
-       struct v4l2_subdev *sd = NULL;
-       struct i2c_client *client;
-
-       BUG_ON(!v4l2_dev);
-
-       request_module(I2C_MODULE_PREFIX "%s", info->type);
-
-       /* Create the i2c client */
-       if (info->addr == 0 && probe_addrs)
-               client = i2c_new_probed_device(adapter, info, probe_addrs,
-                                              NULL);
-       else
-               client = i2c_new_device(adapter, info);
-
-       /* Note: by loading the module first we are certain that c->driver
-          will be set if the driver was found. If the module was not loaded
-          first, then the i2c core tries to delay-load the module for us,
-          and then c->driver is still NULL until the module is finally
-          loaded. This delay-load mechanism doesn't work if other drivers
-          want to use the i2c device, so explicitly loading the module
-          is the best alternative. */
-       if (client == NULL || client->driver == NULL)
-               goto error;
-
-       /* Lock the module so we can safely get the v4l2_subdev pointer */
-       if (!try_module_get(client->driver->driver.owner))
-               goto error;
-       sd = i2c_get_clientdata(client);
-
-       /* Register with the v4l2_device which increases the module's
-          use count as well. */
-       if (v4l2_device_register_subdev(v4l2_dev, sd))
-               sd = NULL;
-       /* Decrease the module use count to match the first try_module_get. */
-       module_put(client->driver->driver.owner);
-
-error:
-       /* If we have a client but no subdev, then something went wrong and
-          we must unregister the client. */
-       if (client && sd == NULL)
-               i2c_unregister_device(client);
-       return sd;
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);
-
-struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
-               struct i2c_adapter *adapter, const char *client_type,
-               u8 addr, const unsigned short *probe_addrs)
-{
-       struct i2c_board_info info;
-
-       /* Setup the i2c board info with the device type and
-          the device address. */
-       memset(&info, 0, sizeof(info));
-       strlcpy(info.type, client_type, sizeof(info.type));
-       info.addr = addr;
-
-       return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs);
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
-
-/* Return i2c client address of v4l2_subdev. */
-unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-       return client ? client->addr : I2C_CLIENT_END;
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);
-
-/* Return a list of I2C tuner addresses to probe. Use only if the tuner
-   addresses are unknown. */
-const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type)
-{
-       static const unsigned short radio_addrs[] = {
-#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE)
-               0x10,
-#endif
-               0x60,
-               I2C_CLIENT_END
-       };
-       static const unsigned short demod_addrs[] = {
-               0x42, 0x43, 0x4a, 0x4b,
-               I2C_CLIENT_END
-       };
-       static const unsigned short tv_addrs[] = {
-               0x42, 0x43, 0x4a, 0x4b,         /* tda8290 */
-               0x60, 0x61, 0x62, 0x63, 0x64,
-               I2C_CLIENT_END
-       };
-
-       switch (type) {
-       case ADDRS_RADIO:
-               return radio_addrs;
-       case ADDRS_DEMOD:
-               return demod_addrs;
-       case ADDRS_TV:
-               return tv_addrs;
-       case ADDRS_TV_WITH_DEMOD:
-               return tv_addrs + 4;
-       }
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);
-
-#endif /* defined(CONFIG_I2C) */
-
-#if defined(CONFIG_SPI)
-
-/* Load an spi sub-device. */
-
-void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
-               const struct v4l2_subdev_ops *ops)
-{
-       v4l2_subdev_init(sd, ops);
-       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;
-       /* spi_device and v4l2_subdev point to one another */
-       v4l2_set_subdevdata(sd, spi);
-       spi_set_drvdata(spi, sd);
-       /* initialize name */
-       strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name));
-}
-EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);
-
-struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
-               struct spi_master *master, struct spi_board_info *info)
-{
-       struct v4l2_subdev *sd = NULL;
-       struct spi_device *spi = NULL;
-
-       BUG_ON(!v4l2_dev);
-
-       if (info->modalias)
-               request_module(info->modalias);
-
-       spi = spi_new_device(master, info);
-
-       if (spi == NULL || spi->dev.driver == NULL)
-               goto error;
-
-       if (!try_module_get(spi->dev.driver->owner))
-               goto error;
-
-       sd = spi_get_drvdata(spi);
-
-       /* Register with the v4l2_device which increases the module's
-          use count as well. */
-       if (v4l2_device_register_subdev(v4l2_dev, sd))
-               sd = NULL;
-
-       /* Decrease the module use count to match the first try_module_get. */
-       module_put(spi->dev.driver->owner);
-
-error:
-       /* If we have a client but no subdev, then something went wrong and
-          we must unregister the client. */
-       if (spi && sd == NULL)
-               spi_unregister_device(spi);
-
-       return sd;
-}
-EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev);
-
-#endif /* defined(CONFIG_SPI) */
-
-/* Clamp x to be between min and max, aligned to a multiple of 2^align.  min
- * and max don't have to be aligned, but there must be at least one valid
- * value.  E.g., min=17,max=31,align=4 is not allowed as there are no multiples
- * of 16 between 17 and 31.  */
-static unsigned int clamp_align(unsigned int x, unsigned int min,
-                               unsigned int max, unsigned int align)
-{
-       /* Bits that must be zero to be aligned */
-       unsigned int mask = ~((1 << align) - 1);
-
-       /* Round to nearest aligned value */
-       if (align)
-               x = (x + (1 << (align - 1))) & mask;
-
-       /* Clamp to aligned value of min and max */
-       if (x < min)
-               x = (min + ~mask) & mask;
-       else if (x > max)
-               x = max & mask;
-
-       return x;
-}
-
-/* Bound an image to have a width between wmin and wmax, and height between
- * hmin and hmax, inclusive.  Additionally, the width will be a multiple of
- * 2^walign, the height will be a multiple of 2^halign, and the overall size
- * (width*height) will be a multiple of 2^salign.  The image may be shrunk
- * or enlarged to fit the alignment constraints.
- *
- * The width or height maximum must not be smaller than the corresponding
- * minimum.  The alignments must not be so high there are no possible image
- * sizes within the allowed bounds.  wmin and hmin must be at least 1
- * (don't use 0).  If you don't care about a certain alignment, specify 0,
- * as 2^0 is 1 and one byte alignment is equivalent to no alignment.  If
- * you only want to adjust downward, specify a maximum that's the same as
- * the initial value.
- */
-void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
-                          unsigned int walign,
-                          u32 *h, unsigned int hmin, unsigned int hmax,
-                          unsigned int halign, unsigned int salign)
-{
-       *w = clamp_align(*w, wmin, wmax, walign);
-       *h = clamp_align(*h, hmin, hmax, halign);
-
-       /* Usually we don't need to align the size and are done now. */
-       if (!salign)
-               return;
-
-       /* How much alignment do we have? */
-       walign = __ffs(*w);
-       halign = __ffs(*h);
-       /* Enough to satisfy the image alignment? */
-       if (walign + halign < salign) {
-               /* Max walign where there is still a valid width */
-               unsigned int wmaxa = __fls(wmax ^ (wmin - 1));
-               /* Max halign where there is still a valid height */
-               unsigned int hmaxa = __fls(hmax ^ (hmin - 1));
-
-               /* up the smaller alignment until we have enough */
-               do {
-                       if (halign >= hmaxa ||
-                           (walign <= halign && walign < wmaxa)) {
-                               *w = clamp_align(*w, wmin, wmax, walign + 1);
-                               walign = __ffs(*w);
-                       } else {
-                               *h = clamp_align(*h, hmin, hmax, halign + 1);
-                               halign = __ffs(*h);
-                       }
-               } while (halign + walign < salign);
-       }
-}
-EXPORT_SYMBOL_GPL(v4l_bound_align_image);
-
-/**
- * v4l_fill_dv_preset_info - fill description of a digital video preset
- * @preset - preset value
- * @info - pointer to struct v4l2_dv_enum_preset
- *
- * drivers can use this helper function to fill description of dv preset
- * in info.
- */
-int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info)
-{
-       static const struct v4l2_dv_preset_info {
-               u16 width;
-               u16 height;
-               const char *name;
-       } dv_presets[] = {
-               { 0, 0, "Invalid" },            /* V4L2_DV_INVALID */
-               { 720,  480, "480p@59.94" },    /* V4L2_DV_480P59_94 */
-               { 720,  576, "576p@50" },       /* V4L2_DV_576P50 */
-               { 1280, 720, "720p@24" },       /* V4L2_DV_720P24 */
-               { 1280, 720, "720p@25" },       /* V4L2_DV_720P25 */
-               { 1280, 720, "720p@30" },       /* V4L2_DV_720P30 */
-               { 1280, 720, "720p@50" },       /* V4L2_DV_720P50 */
-               { 1280, 720, "720p@59.94" },    /* V4L2_DV_720P59_94 */
-               { 1280, 720, "720p@60" },       /* V4L2_DV_720P60 */
-               { 1920, 1080, "1080i@29.97" },  /* V4L2_DV_1080I29_97 */
-               { 1920, 1080, "1080i@30" },     /* V4L2_DV_1080I30 */
-               { 1920, 1080, "1080i@25" },     /* V4L2_DV_1080I25 */
-               { 1920, 1080, "1080i@50" },     /* V4L2_DV_1080I50 */
-               { 1920, 1080, "1080i@60" },     /* V4L2_DV_1080I60 */
-               { 1920, 1080, "1080p@24" },     /* V4L2_DV_1080P24 */
-               { 1920, 1080, "1080p@25" },     /* V4L2_DV_1080P25 */
-               { 1920, 1080, "1080p@30" },     /* V4L2_DV_1080P30 */
-               { 1920, 1080, "1080p@50" },     /* V4L2_DV_1080P50 */
-               { 1920, 1080, "1080p@60" },     /* V4L2_DV_1080P60 */
-       };
-
-       if (info == NULL || preset >= ARRAY_SIZE(dv_presets))
-               return -EINVAL;
-
-       info->preset = preset;
-       info->width = dv_presets[preset].width;
-       info->height = dv_presets[preset].height;
-       strlcpy(info->name, dv_presets[preset].name, sizeof(info->name));
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info);
-
-const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
-               const struct v4l2_discrete_probe *probe,
-               s32 width, s32 height)
-{
-       int i;
-       u32 error, min_error = UINT_MAX;
-       const struct v4l2_frmsize_discrete *size, *best = NULL;
-
-       if (!probe)
-               return best;
-
-       for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) {
-               error = abs(size->width - width) + abs(size->height - height);
-               if (error < min_error) {
-                       min_error = error;
-                       best = size;
-               }
-               if (!error)
-                       break;
-       }
-
-       return best;
-}
-EXPORT_SYMBOL_GPL(v4l2_find_nearest_format);
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
deleted file mode 100644 (file)
index 9ebd5c5..0000000
+++ /dev/null
@@ -1,1045 +0,0 @@
-/*
- * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
- *     Separated from fs stuff by Arnd Bergmann <arnd@arndb.de>
- *
- * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
- * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
- * Copyright (C) 2001,2002  Andi Kleen, SuSE Labs
- * Copyright (C) 2003       Pavel Machek (pavel@ucw.cz)
- * Copyright (C) 2005       Philippe De Muyter (phdm@macqel.be)
- * Copyright (C) 2008       Hans Verkuil <hverkuil@xs4all.nl>
- *
- * These routines maintain argument size conversion between 32bit and 64bit
- * ioctls.
- */
-
-#include <linux/compat.h>
-#include <linux/module.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
-
-static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       long ret = -ENOIOCTLCMD;
-
-       if (file->f_op->unlocked_ioctl)
-               ret = file->f_op->unlocked_ioctl(file, cmd, arg);
-
-       return ret;
-}
-
-
-struct v4l2_clip32 {
-       struct v4l2_rect        c;
-       compat_caddr_t          next;
-};
-
-struct v4l2_window32 {
-       struct v4l2_rect        w;
-       __u32                   field;  /* enum v4l2_field */
-       __u32                   chromakey;
-       compat_caddr_t          clips; /* actually struct v4l2_clip32 * */
-       __u32                   clipcount;
-       compat_caddr_t          bitmap;
-};
-
-static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
-{
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
-               copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
-               get_user(kp->field, &up->field) ||
-               get_user(kp->chromakey, &up->chromakey) ||
-               get_user(kp->clipcount, &up->clipcount))
-                       return -EFAULT;
-       if (kp->clipcount > 2048)
-               return -EINVAL;
-       if (kp->clipcount) {
-               struct v4l2_clip32 __user *uclips;
-               struct v4l2_clip __user *kclips;
-               int n = kp->clipcount;
-               compat_caddr_t p;
-
-               if (get_user(p, &up->clips))
-                       return -EFAULT;
-               uclips = compat_ptr(p);
-               kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
-               kp->clips = kclips;
-               while (--n >= 0) {
-                       if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
-                               return -EFAULT;
-                       if (put_user(n ? kclips + 1 : NULL, &kclips->next))
-                               return -EFAULT;
-                       uclips += 1;
-                       kclips += 1;
-               }
-       } else
-               kp->clips = NULL;
-       return 0;
-}
-
-static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
-{
-       if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
-               put_user(kp->field, &up->field) ||
-               put_user(kp->chromakey, &up->chromakey) ||
-               put_user(kp->clipcount, &up->clipcount))
-                       return -EFAULT;
-       return 0;
-}
-
-static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
-                               struct v4l2_pix_format_mplane __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
-                               struct v4l2_pix_format_mplane __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
-               return -EFAULT;
-       return 0;
-}
-
-struct v4l2_format32 {
-       __u32   type;   /* enum v4l2_buf_type */
-       union {
-               struct v4l2_pix_format  pix;
-               struct v4l2_pix_format_mplane   pix_mp;
-               struct v4l2_window32    win;
-               struct v4l2_vbi_format  vbi;
-               struct v4l2_sliced_vbi_format   sliced;
-               __u8    raw_data[200];        /* user-defined */
-       } fmt;
-};
-
-/**
- * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
- * @index:     on return, index of the first created buffer
- * @count:     entry: number of requested buffers,
- *             return: number of created buffers
- * @memory:    buffer memory type
- * @format:    frame format, for which buffers are requested
- * @reserved:  future extensions
- */
-struct v4l2_create_buffers32 {
-       __u32                   index;
-       __u32                   count;
-       __u32                   memory; /* enum v4l2_memory */
-       struct v4l2_format32    format;
-       __u32                   reserved[8];
-};
-
-static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
-       switch (kp->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
-                                                 &up->fmt.pix_mp);
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data)))
-                       return -EFAULT;
-               return 0;
-       default:
-               printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-                                                               kp->type);
-               return -EINVAL;
-       }
-}
-
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
-                       get_user(kp->type, &up->type))
-                       return -EFAULT;
-       return __get_v4l2_format32(kp, up);
-}
-
-static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
-{
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
-           copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
-                       return -EFAULT;
-       return __get_v4l2_format32(&kp->format, &up->format);
-}
-
-static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
-       switch (kp->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
-                                                 &up->fmt.pix_mp);
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (copy_to_user(up, kp, sizeof(up->fmt.raw_data)))
-                       return -EFAULT;
-               return 0;
-       default:
-               printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-                                                               kp->type);
-               return -EINVAL;
-       }
-}
-
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
-               put_user(kp->type, &up->type))
-               return -EFAULT;
-       return __put_v4l2_format32(kp, up);
-}
-
-static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
-{
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
-           copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
-                       return -EFAULT;
-       return __put_v4l2_format32(&kp->format, &up->format);
-}
-
-struct v4l2_standard32 {
-       __u32                index;
-       __u32                id[2]; /* __u64 would get the alignment wrong */
-       __u8                 name[24];
-       struct v4l2_fract    frameperiod; /* Frames, not fields */
-       __u32                framelines;
-       __u32                reserved[4];
-};
-
-static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
-{
-       /* other fields are not set by the user, nor used by the driver */
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
-               get_user(kp->index, &up->index))
-               return -EFAULT;
-       return 0;
-}
-
-static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
-{
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
-               put_user(kp->index, &up->index) ||
-               copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
-               copy_to_user(up->name, kp->name, 24) ||
-               copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
-               put_user(kp->framelines, &up->framelines) ||
-               copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
-                       return -EFAULT;
-       return 0;
-}
-
-struct v4l2_plane32 {
-       __u32                   bytesused;
-       __u32                   length;
-       union {
-               __u32           mem_offset;
-               compat_long_t   userptr;
-       } m;
-       __u32                   data_offset;
-       __u32                   reserved[11];
-};
-
-struct v4l2_buffer32 {
-       __u32                   index;
-       __u32                   type;   /* enum v4l2_buf_type */
-       __u32                   bytesused;
-       __u32                   flags;
-       __u32                   field;  /* enum v4l2_field */
-       struct compat_timeval   timestamp;
-       struct v4l2_timecode    timecode;
-       __u32                   sequence;
-
-       /* memory location */
-       __u32                   memory; /* enum v4l2_memory */
-       union {
-               __u32           offset;
-               compat_long_t   userptr;
-               compat_caddr_t  planes;
-       } m;
-       __u32                   length;
-       __u32                   reserved2;
-       __u32                   reserved;
-};
-
-static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
-                               enum v4l2_memory memory)
-{
-       void __user *up_pln;
-       compat_long_t p;
-
-       if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
-               copy_in_user(&up->data_offset, &up32->data_offset,
-                               sizeof(__u32)))
-               return -EFAULT;
-
-       if (memory == V4L2_MEMORY_USERPTR) {
-               if (get_user(p, &up32->m.userptr))
-                       return -EFAULT;
-               up_pln = compat_ptr(p);
-               if (put_user((unsigned long)up_pln, &up->m.userptr))
-                       return -EFAULT;
-       } else {
-               if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
-                                       sizeof(__u32)))
-                       return -EFAULT;
-       }
-
-       return 0;
-}
-
-static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
-                               enum v4l2_memory memory)
-{
-       if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
-               copy_in_user(&up32->data_offset, &up->data_offset,
-                               sizeof(__u32)))
-               return -EFAULT;
-
-       /* For MMAP, driver might've set up the offset, so copy it back.
-        * USERPTR stays the same (was userspace-provided), so no copying. */
-       if (memory == V4L2_MEMORY_MMAP)
-               if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
-                                       sizeof(__u32)))
-                       return -EFAULT;
-
-       return 0;
-}
-
-static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
-{
-       struct v4l2_plane32 __user *uplane32;
-       struct v4l2_plane __user *uplane;
-       compat_caddr_t p;
-       int num_planes;
-       int ret;
-
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
-               get_user(kp->index, &up->index) ||
-               get_user(kp->type, &up->type) ||
-               get_user(kp->flags, &up->flags) ||
-               get_user(kp->memory, &up->memory))
-                       return -EFAULT;
-
-       if (V4L2_TYPE_IS_OUTPUT(kp->type))
-               if (get_user(kp->bytesused, &up->bytesused) ||
-                       get_user(kp->field, &up->field) ||
-                       get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
-                       get_user(kp->timestamp.tv_usec,
-                                       &up->timestamp.tv_usec))
-                       return -EFAULT;
-
-       if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
-               if (get_user(kp->length, &up->length))
-                       return -EFAULT;
-
-               num_planes = kp->length;
-               if (num_planes == 0) {
-                       kp->m.planes = NULL;
-                       /* num_planes == 0 is legal, e.g. when userspace doesn't
-                        * need planes array on DQBUF*/
-                       return 0;
-               }
-
-               if (get_user(p, &up->m.planes))
-                       return -EFAULT;
-
-               uplane32 = compat_ptr(p);
-               if (!access_ok(VERIFY_READ, uplane32,
-                               num_planes * sizeof(struct v4l2_plane32)))
-                       return -EFAULT;
-
-               /* We don't really care if userspace decides to kill itself
-                * by passing a very big num_planes value */
-               uplane = compat_alloc_user_space(num_planes *
-                                               sizeof(struct v4l2_plane));
-               kp->m.planes = uplane;
-
-               while (--num_planes >= 0) {
-                       ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
-                       if (ret)
-                               return ret;
-                       ++uplane;
-                       ++uplane32;
-               }
-       } else {
-               switch (kp->memory) {
-               case V4L2_MEMORY_MMAP:
-                       if (get_user(kp->length, &up->length) ||
-                               get_user(kp->m.offset, &up->m.offset))
-                               return -EFAULT;
-                       break;
-               case V4L2_MEMORY_USERPTR:
-                       {
-                       compat_long_t tmp;
-
-                       if (get_user(kp->length, &up->length) ||
-                           get_user(tmp, &up->m.userptr))
-                               return -EFAULT;
-
-                       kp->m.userptr = (unsigned long)compat_ptr(tmp);
-                       }
-                       break;
-               case V4L2_MEMORY_OVERLAY:
-                       if (get_user(kp->m.offset, &up->m.offset))
-                               return -EFAULT;
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
-{
-       struct v4l2_plane32 __user *uplane32;
-       struct v4l2_plane __user *uplane;
-       compat_caddr_t p;
-       int num_planes;
-       int ret;
-
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
-               put_user(kp->index, &up->index) ||
-               put_user(kp->type, &up->type) ||
-               put_user(kp->flags, &up->flags) ||
-               put_user(kp->memory, &up->memory))
-                       return -EFAULT;
-
-       if (put_user(kp->bytesused, &up->bytesused) ||
-               put_user(kp->field, &up->field) ||
-               put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
-               put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
-               copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
-               put_user(kp->sequence, &up->sequence) ||
-               put_user(kp->reserved2, &up->reserved2) ||
-               put_user(kp->reserved, &up->reserved))
-                       return -EFAULT;
-
-       if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
-               num_planes = kp->length;
-               if (num_planes == 0)
-                       return 0;
-
-               uplane = kp->m.planes;
-               if (get_user(p, &up->m.planes))
-                       return -EFAULT;
-               uplane32 = compat_ptr(p);
-
-               while (--num_planes >= 0) {
-                       ret = put_v4l2_plane32(uplane, uplane32, kp->memory);
-                       if (ret)
-                               return ret;
-                       ++uplane;
-                       ++uplane32;
-               }
-       } else {
-               switch (kp->memory) {
-               case V4L2_MEMORY_MMAP:
-                       if (put_user(kp->length, &up->length) ||
-                               put_user(kp->m.offset, &up->m.offset))
-                               return -EFAULT;
-                       break;
-               case V4L2_MEMORY_USERPTR:
-                       if (put_user(kp->length, &up->length) ||
-                               put_user(kp->m.userptr, &up->m.userptr))
-                               return -EFAULT;
-                       break;
-               case V4L2_MEMORY_OVERLAY:
-                       if (put_user(kp->m.offset, &up->m.offset))
-                               return -EFAULT;
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-struct v4l2_framebuffer32 {
-       __u32                   capability;
-       __u32                   flags;
-       compat_caddr_t          base;
-       struct v4l2_pix_format  fmt;
-};
-
-static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
-{
-       u32 tmp;
-
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
-               get_user(tmp, &up->base) ||
-               get_user(kp->capability, &up->capability) ||
-               get_user(kp->flags, &up->flags))
-                       return -EFAULT;
-       kp->base = compat_ptr(tmp);
-       get_v4l2_pix_format(&kp->fmt, &up->fmt);
-       return 0;
-}
-
-static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
-{
-       u32 tmp = (u32)((unsigned long)kp->base);
-
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
-               put_user(tmp, &up->base) ||
-               put_user(kp->capability, &up->capability) ||
-               put_user(kp->flags, &up->flags))
-                       return -EFAULT;
-       put_v4l2_pix_format(&kp->fmt, &up->fmt);
-       return 0;
-}
-
-struct v4l2_input32 {
-       __u32        index;             /*  Which input */
-       __u8         name[32];          /*  Label */
-       __u32        type;              /*  Type of input */
-       __u32        audioset;          /*  Associated audios (bitfield) */
-       __u32        tuner;             /*  Associated tuner */
-       v4l2_std_id  std;
-       __u32        status;
-       __u32        reserved[4];
-} __attribute__ ((packed));
-
-/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
-   Otherwise it is identical to the 32-bit version. */
-static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
-               return -EFAULT;
-       return 0;
-}
-
-struct v4l2_ext_controls32 {
-       __u32 ctrl_class;
-       __u32 count;
-       __u32 error_idx;
-       __u32 reserved[2];
-       compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
-};
-
-struct v4l2_ext_control32 {
-       __u32 id;
-       __u32 size;
-       __u32 reserved2[1];
-       union {
-               __s32 value;
-               __s64 value64;
-               compat_caddr_t string; /* actually char * */
-       };
-} __attribute__ ((packed));
-
-/* The following function really belong in v4l2-common, but that causes
-   a circular dependency between modules. We need to think about this, but
-   for now this will do. */
-
-/* Return non-zero if this control is a pointer type. Currently only
-   type STRING is a pointer type. */
-static inline int ctrl_is_pointer(u32 id)
-{
-       switch (id) {
-       case V4L2_CID_RDS_TX_PS_NAME:
-       case V4L2_CID_RDS_TX_RADIO_TEXT:
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
-{
-       struct v4l2_ext_control32 __user *ucontrols;
-       struct v4l2_ext_control __user *kcontrols;
-       int n;
-       compat_caddr_t p;
-
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
-               get_user(kp->ctrl_class, &up->ctrl_class) ||
-               get_user(kp->count, &up->count) ||
-               get_user(kp->error_idx, &up->error_idx) ||
-               copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
-                       return -EFAULT;
-       n = kp->count;
-       if (n == 0) {
-               kp->controls = NULL;
-               return 0;
-       }
-       if (get_user(p, &up->controls))
-               return -EFAULT;
-       ucontrols = compat_ptr(p);
-       if (!access_ok(VERIFY_READ, ucontrols,
-                       n * sizeof(struct v4l2_ext_control32)))
-               return -EFAULT;
-       kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
-       kp->controls = kcontrols;
-       while (--n >= 0) {
-               if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
-                       return -EFAULT;
-               if (ctrl_is_pointer(kcontrols->id)) {
-                       void __user *s;
-
-                       if (get_user(p, &ucontrols->string))
-                               return -EFAULT;
-                       s = compat_ptr(p);
-                       if (put_user(s, &kcontrols->string))
-                               return -EFAULT;
-               }
-               ucontrols++;
-               kcontrols++;
-       }
-       return 0;
-}
-
-static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
-{
-       struct v4l2_ext_control32 __user *ucontrols;
-       struct v4l2_ext_control __user *kcontrols = kp->controls;
-       int n = kp->count;
-       compat_caddr_t p;
-
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
-               put_user(kp->ctrl_class, &up->ctrl_class) ||
-               put_user(kp->count, &up->count) ||
-               put_user(kp->error_idx, &up->error_idx) ||
-               copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
-                       return -EFAULT;
-       if (!kp->count)
-               return 0;
-
-       if (get_user(p, &up->controls))
-               return -EFAULT;
-       ucontrols = compat_ptr(p);
-       if (!access_ok(VERIFY_WRITE, ucontrols,
-                       n * sizeof(struct v4l2_ext_control32)))
-               return -EFAULT;
-
-       while (--n >= 0) {
-               unsigned size = sizeof(*ucontrols);
-
-               /* Do not modify the pointer when copying a pointer control.
-                  The contents of the pointer was changed, not the pointer
-                  itself. */
-               if (ctrl_is_pointer(kcontrols->id))
-                       size -= sizeof(ucontrols->value64);
-               if (copy_in_user(ucontrols, kcontrols, size))
-                       return -EFAULT;
-               ucontrols++;
-               kcontrols++;
-       }
-       return 0;
-}
-
-struct v4l2_event32 {
-       __u32                           type;
-       union {
-               __u8                    data[64];
-       } u;
-       __u32                           pending;
-       __u32                           sequence;
-       struct compat_timespec          timestamp;
-       __u32                           id;
-       __u32                           reserved[8];
-};
-
-static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
-{
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
-               put_user(kp->type, &up->type) ||
-               copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
-               put_user(kp->pending, &up->pending) ||
-               put_user(kp->sequence, &up->sequence) ||
-               put_compat_timespec(&kp->timestamp, &up->timestamp) ||
-               put_user(kp->id, &up->id) ||
-               copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
-                       return -EFAULT;
-       return 0;
-}
-
-#define VIDIOC_G_FMT32         _IOWR('V',  4, struct v4l2_format32)
-#define VIDIOC_S_FMT32         _IOWR('V',  5, struct v4l2_format32)
-#define VIDIOC_QUERYBUF32      _IOWR('V',  9, struct v4l2_buffer32)
-#define VIDIOC_G_FBUF32                _IOR ('V', 10, struct v4l2_framebuffer32)
-#define VIDIOC_S_FBUF32                _IOW ('V', 11, struct v4l2_framebuffer32)
-#define VIDIOC_QBUF32          _IOWR('V', 15, struct v4l2_buffer32)
-#define VIDIOC_DQBUF32         _IOWR('V', 17, struct v4l2_buffer32)
-#define VIDIOC_ENUMSTD32       _IOWR('V', 25, struct v4l2_standard32)
-#define VIDIOC_ENUMINPUT32     _IOWR('V', 26, struct v4l2_input32)
-#define VIDIOC_TRY_FMT32       _IOWR('V', 64, struct v4l2_format32)
-#define VIDIOC_G_EXT_CTRLS32    _IOWR('V', 71, struct v4l2_ext_controls32)
-#define VIDIOC_S_EXT_CTRLS32    _IOWR('V', 72, struct v4l2_ext_controls32)
-#define VIDIOC_TRY_EXT_CTRLS32  _IOWR('V', 73, struct v4l2_ext_controls32)
-#define        VIDIOC_DQEVENT32        _IOR ('V', 89, struct v4l2_event32)
-#define VIDIOC_CREATE_BUFS32   _IOWR('V', 92, struct v4l2_create_buffers32)
-#define VIDIOC_PREPARE_BUF32   _IOWR('V', 93, struct v4l2_buffer32)
-
-#define VIDIOC_OVERLAY32       _IOW ('V', 14, s32)
-#define VIDIOC_STREAMON32      _IOW ('V', 18, s32)
-#define VIDIOC_STREAMOFF32     _IOW ('V', 19, s32)
-#define VIDIOC_G_INPUT32       _IOR ('V', 38, s32)
-#define VIDIOC_S_INPUT32       _IOWR('V', 39, s32)
-#define VIDIOC_G_OUTPUT32      _IOR ('V', 46, s32)
-#define VIDIOC_S_OUTPUT32      _IOWR('V', 47, s32)
-
-static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       union {
-               struct v4l2_format v2f;
-               struct v4l2_buffer v2b;
-               struct v4l2_framebuffer v2fb;
-               struct v4l2_input v2i;
-               struct v4l2_standard v2s;
-               struct v4l2_ext_controls v2ecs;
-               struct v4l2_event v2ev;
-               struct v4l2_create_buffers v2crt;
-               unsigned long vx;
-               int vi;
-       } karg;
-       void __user *up = compat_ptr(arg);
-       int compatible_arg = 1;
-       long err = 0;
-
-       /* First, convert the command. */
-       switch (cmd) {
-       case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
-       case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
-       case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
-       case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
-       case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
-       case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
-       case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
-       case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break;
-       case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break;
-       case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
-       case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
-       case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
-       case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
-       case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break;
-       case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
-       case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
-       case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
-       case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
-       case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
-       case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
-       case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
-       case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
-       case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
-       }
-
-       switch (cmd) {
-       case VIDIOC_OVERLAY:
-       case VIDIOC_STREAMON:
-       case VIDIOC_STREAMOFF:
-       case VIDIOC_S_INPUT:
-       case VIDIOC_S_OUTPUT:
-               err = get_user(karg.vi, (s32 __user *)up);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_G_INPUT:
-       case VIDIOC_G_OUTPUT:
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_G_FMT:
-       case VIDIOC_S_FMT:
-       case VIDIOC_TRY_FMT:
-               err = get_v4l2_format32(&karg.v2f, up);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_CREATE_BUFS:
-               err = get_v4l2_create32(&karg.v2crt, up);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_PREPARE_BUF:
-       case VIDIOC_QUERYBUF:
-       case VIDIOC_QBUF:
-       case VIDIOC_DQBUF:
-               err = get_v4l2_buffer32(&karg.v2b, up);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_S_FBUF:
-               err = get_v4l2_framebuffer32(&karg.v2fb, up);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_G_FBUF:
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_ENUMSTD:
-               err = get_v4l2_standard32(&karg.v2s, up);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_ENUMINPUT:
-               err = get_v4l2_input32(&karg.v2i, up);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_G_EXT_CTRLS:
-       case VIDIOC_S_EXT_CTRLS:
-       case VIDIOC_TRY_EXT_CTRLS:
-               err = get_v4l2_ext_controls32(&karg.v2ecs, up);
-               compatible_arg = 0;
-               break;
-       case VIDIOC_DQEVENT:
-               compatible_arg = 0;
-               break;
-       }
-       if (err)
-               return err;
-
-       if (compatible_arg)
-               err = native_ioctl(file, cmd, (unsigned long)up);
-       else {
-               mm_segment_t old_fs = get_fs();
-
-               set_fs(KERNEL_DS);
-               err = native_ioctl(file, cmd, (unsigned long)&karg);
-               set_fs(old_fs);
-       }
-
-       /* Special case: even after an error we need to put the
-          results back for these ioctls since the error_idx will
-          contain information on which control failed. */
-       switch (cmd) {
-       case VIDIOC_G_EXT_CTRLS:
-       case VIDIOC_S_EXT_CTRLS:
-       case VIDIOC_TRY_EXT_CTRLS:
-               if (put_v4l2_ext_controls32(&karg.v2ecs, up))
-                       err = -EFAULT;
-               break;
-       }
-       if (err)
-               return err;
-
-       switch (cmd) {
-       case VIDIOC_S_INPUT:
-       case VIDIOC_S_OUTPUT:
-       case VIDIOC_G_INPUT:
-       case VIDIOC_G_OUTPUT:
-               err = put_user(((s32)karg.vi), (s32 __user *)up);
-               break;
-
-       case VIDIOC_G_FBUF:
-               err = put_v4l2_framebuffer32(&karg.v2fb, up);
-               break;
-
-       case VIDIOC_DQEVENT:
-               err = put_v4l2_event32(&karg.v2ev, up);
-               break;
-
-       case VIDIOC_G_FMT:
-       case VIDIOC_S_FMT:
-       case VIDIOC_TRY_FMT:
-               err = put_v4l2_format32(&karg.v2f, up);
-               break;
-
-       case VIDIOC_CREATE_BUFS:
-               err = put_v4l2_create32(&karg.v2crt, up);
-               break;
-
-       case VIDIOC_QUERYBUF:
-       case VIDIOC_QBUF:
-       case VIDIOC_DQBUF:
-               err = put_v4l2_buffer32(&karg.v2b, up);
-               break;
-
-       case VIDIOC_ENUMSTD:
-               err = put_v4l2_standard32(&karg.v2s, up);
-               break;
-
-       case VIDIOC_ENUMINPUT:
-               err = put_v4l2_input32(&karg.v2i, up);
-               break;
-       }
-       return err;
-}
-
-long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       struct video_device *vdev = video_devdata(file);
-       long ret = -ENOIOCTLCMD;
-
-       if (!file->f_op->unlocked_ioctl)
-               return ret;
-
-       switch (cmd) {
-       case VIDIOC_QUERYCAP:
-       case VIDIOC_RESERVED:
-       case VIDIOC_ENUM_FMT:
-       case VIDIOC_G_FMT32:
-       case VIDIOC_S_FMT32:
-       case VIDIOC_REQBUFS:
-       case VIDIOC_QUERYBUF32:
-       case VIDIOC_G_FBUF32:
-       case VIDIOC_S_FBUF32:
-       case VIDIOC_OVERLAY32:
-       case VIDIOC_QBUF32:
-       case VIDIOC_DQBUF32:
-       case VIDIOC_STREAMON32:
-       case VIDIOC_STREAMOFF32:
-       case VIDIOC_G_PARM:
-       case VIDIOC_S_PARM:
-       case VIDIOC_G_STD:
-       case VIDIOC_S_STD:
-       case VIDIOC_ENUMSTD32:
-       case VIDIOC_ENUMINPUT32:
-       case VIDIOC_G_CTRL:
-       case VIDIOC_S_CTRL:
-       case VIDIOC_G_TUNER:
-       case VIDIOC_S_TUNER:
-       case VIDIOC_G_AUDIO:
-       case VIDIOC_S_AUDIO:
-       case VIDIOC_QUERYCTRL:
-       case VIDIOC_QUERYMENU:
-       case VIDIOC_G_INPUT32:
-       case VIDIOC_S_INPUT32:
-       case VIDIOC_G_OUTPUT32:
-       case VIDIOC_S_OUTPUT32:
-       case VIDIOC_ENUMOUTPUT:
-       case VIDIOC_G_AUDOUT:
-       case VIDIOC_S_AUDOUT:
-       case VIDIOC_G_MODULATOR:
-       case VIDIOC_S_MODULATOR:
-       case VIDIOC_S_FREQUENCY:
-       case VIDIOC_G_FREQUENCY:
-       case VIDIOC_CROPCAP:
-       case VIDIOC_G_CROP:
-       case VIDIOC_S_CROP:
-       case VIDIOC_G_SELECTION:
-       case VIDIOC_S_SELECTION:
-       case VIDIOC_G_JPEGCOMP:
-       case VIDIOC_S_JPEGCOMP:
-       case VIDIOC_QUERYSTD:
-       case VIDIOC_TRY_FMT32:
-       case VIDIOC_ENUMAUDIO:
-       case VIDIOC_ENUMAUDOUT:
-       case VIDIOC_G_PRIORITY:
-       case VIDIOC_S_PRIORITY:
-       case VIDIOC_G_SLICED_VBI_CAP:
-       case VIDIOC_LOG_STATUS:
-       case VIDIOC_G_EXT_CTRLS32:
-       case VIDIOC_S_EXT_CTRLS32:
-       case VIDIOC_TRY_EXT_CTRLS32:
-       case VIDIOC_ENUM_FRAMESIZES:
-       case VIDIOC_ENUM_FRAMEINTERVALS:
-       case VIDIOC_G_ENC_INDEX:
-       case VIDIOC_ENCODER_CMD:
-       case VIDIOC_TRY_ENCODER_CMD:
-       case VIDIOC_DECODER_CMD:
-       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_ENUM_DV_PRESETS:
-       case VIDIOC_S_DV_PRESET:
-       case VIDIOC_G_DV_PRESET:
-       case VIDIOC_QUERY_DV_PRESET:
-       case VIDIOC_S_DV_TIMINGS:
-       case VIDIOC_G_DV_TIMINGS:
-       case VIDIOC_DQEVENT:
-       case VIDIOC_DQEVENT32:
-       case VIDIOC_SUBSCRIBE_EVENT:
-       case VIDIOC_UNSUBSCRIBE_EVENT:
-       case VIDIOC_CREATE_BUFS32:
-       case VIDIOC_PREPARE_BUF32:
-       case VIDIOC_ENUM_DV_TIMINGS:
-       case VIDIOC_QUERY_DV_TIMINGS:
-       case VIDIOC_DV_TIMINGS_CAP:
-       case VIDIOC_ENUM_FREQ_BANDS:
-               ret = do_video_ioctl(file, cmd, arg);
-               break;
-
-       default:
-               if (vdev->fops->compat_ioctl32)
-                       ret = vdev->fops->compat_ioctl32(file, cmd, arg);
-
-               if (ret == -ENOIOCTLCMD)
-                       printk(KERN_WARNING "compat_ioctl32: "
-                               "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
-                               _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
-                               cmd);
-               break;
-       }
-       return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
deleted file mode 100644 (file)
index b6a2ee7..0000000
+++ /dev/null
@@ -1,2651 +0,0 @@
-/*
-    V4L2 controls framework implementation.
-
-    Copyright (C) 2010  Hans Verkuil <hverkuil@xs4all.nl>
-
-    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 <linux/ctype.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dev.h>
-
-#define has_op(master, op) \
-       (master->ops && master->ops->op)
-#define call_op(master, op) \
-       (has_op(master, op) ? master->ops->op(master) : 0)
-
-/* Internal temporary helper struct, one for each v4l2_ext_control */
-struct v4l2_ctrl_helper {
-       /* Pointer to the control reference of the master control */
-       struct v4l2_ctrl_ref *mref;
-       /* The control corresponding to the v4l2_ext_control ID field. */
-       struct v4l2_ctrl *ctrl;
-       /* v4l2_ext_control index of the next control belonging to the
-          same cluster, or 0 if there isn't any. */
-       u32 next;
-};
-
-/* Small helper function to determine if the autocluster is set to manual
-   mode. */
-static bool is_cur_manual(const struct v4l2_ctrl *master)
-{
-       return master->is_auto && master->cur.val == master->manual_mode_value;
-}
-
-/* Same as above, but this checks the against the new value instead of the
-   current value. */
-static bool is_new_manual(const struct v4l2_ctrl *master)
-{
-       return master->is_auto && master->val == master->manual_mode_value;
-}
-
-/* Returns NULL or a character pointer array containing the menu for
-   the given control ID. The pointer array ends with a NULL pointer.
-   An empty string signifies a menu entry that is invalid. This allows
-   drivers to disable certain options if it is not supported. */
-const char * const *v4l2_ctrl_get_menu(u32 id)
-{
-       static const char * const mpeg_audio_sampling_freq[] = {
-               "44.1 kHz",
-               "48 kHz",
-               "32 kHz",
-               NULL
-       };
-       static const char * const mpeg_audio_encoding[] = {
-               "MPEG-1/2 Layer I",
-               "MPEG-1/2 Layer II",
-               "MPEG-1/2 Layer III",
-               "MPEG-2/4 AAC",
-               "AC-3",
-               NULL
-       };
-       static const char * const mpeg_audio_l1_bitrate[] = {
-               "32 kbps",
-               "64 kbps",
-               "96 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "288 kbps",
-               "320 kbps",
-               "352 kbps",
-               "384 kbps",
-               "416 kbps",
-               "448 kbps",
-               NULL
-       };
-       static const char * const mpeg_audio_l2_bitrate[] = {
-               "32 kbps",
-               "48 kbps",
-               "56 kbps",
-               "64 kbps",
-               "80 kbps",
-               "96 kbps",
-               "112 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "320 kbps",
-               "384 kbps",
-               NULL
-       };
-       static const char * const mpeg_audio_l3_bitrate[] = {
-               "32 kbps",
-               "40 kbps",
-               "48 kbps",
-               "56 kbps",
-               "64 kbps",
-               "80 kbps",
-               "96 kbps",
-               "112 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "320 kbps",
-               NULL
-       };
-       static const char * const mpeg_audio_ac3_bitrate[] = {
-               "32 kbps",
-               "40 kbps",
-               "48 kbps",
-               "56 kbps",
-               "64 kbps",
-               "80 kbps",
-               "96 kbps",
-               "112 kbps",
-               "128 kbps",
-               "160 kbps",
-               "192 kbps",
-               "224 kbps",
-               "256 kbps",
-               "320 kbps",
-               "384 kbps",
-               "448 kbps",
-               "512 kbps",
-               "576 kbps",
-               "640 kbps",
-               NULL
-       };
-       static const char * const mpeg_audio_mode[] = {
-               "Stereo",
-               "Joint Stereo",
-               "Dual",
-               "Mono",
-               NULL
-       };
-       static const char * const mpeg_audio_mode_extension[] = {
-               "Bound 4",
-               "Bound 8",
-               "Bound 12",
-               "Bound 16",
-               NULL
-       };
-       static const char * const mpeg_audio_emphasis[] = {
-               "No Emphasis",
-               "50/15 us",
-               "CCITT J17",
-               NULL
-       };
-       static const char * const mpeg_audio_crc[] = {
-               "No CRC",
-               "16-bit CRC",
-               NULL
-       };
-       static const char * const mpeg_audio_dec_playback[] = {
-               "Auto",
-               "Stereo",
-               "Left",
-               "Right",
-               "Mono",
-               "Swapped Stereo",
-               NULL
-       };
-       static const char * const mpeg_video_encoding[] = {
-               "MPEG-1",
-               "MPEG-2",
-               "MPEG-4 AVC",
-               NULL
-       };
-       static const char * const mpeg_video_aspect[] = {
-               "1x1",
-               "4x3",
-               "16x9",
-               "2.21x1",
-               NULL
-       };
-       static const char * const mpeg_video_bitrate_mode[] = {
-               "Variable Bitrate",
-               "Constant Bitrate",
-               NULL
-       };
-       static const char * const mpeg_stream_type[] = {
-               "MPEG-2 Program Stream",
-               "MPEG-2 Transport Stream",
-               "MPEG-1 System Stream",
-               "MPEG-2 DVD-compatible Stream",
-               "MPEG-1 VCD-compatible Stream",
-               "MPEG-2 SVCD-compatible Stream",
-               NULL
-       };
-       static const char * const mpeg_stream_vbi_fmt[] = {
-               "No VBI",
-               "Private Packet, IVTV Format",
-               NULL
-       };
-       static const char * const camera_power_line_frequency[] = {
-               "Disabled",
-               "50 Hz",
-               "60 Hz",
-               "Auto",
-               NULL
-       };
-       static const char * const camera_exposure_auto[] = {
-               "Auto Mode",
-               "Manual Mode",
-               "Shutter Priority Mode",
-               "Aperture Priority Mode",
-               NULL
-       };
-       static const char * const camera_exposure_metering[] = {
-               "Average",
-               "Center Weighted",
-               "Spot",
-               NULL
-       };
-       static const char * const camera_auto_focus_range[] = {
-               "Auto",
-               "Normal",
-               "Macro",
-               "Infinity",
-               NULL
-       };
-       static const char * const colorfx[] = {
-               "None",
-               "Black & White",
-               "Sepia",
-               "Negative",
-               "Emboss",
-               "Sketch",
-               "Sky Blue",
-               "Grass Green",
-               "Skin Whiten",
-               "Vivid",
-               "Aqua",
-               "Art Freeze",
-               "Silhouette",
-               "Solarization",
-               "Antique",
-               "Set Cb/Cr",
-               NULL
-       };
-       static const char * const auto_n_preset_white_balance[] = {
-               "Manual",
-               "Auto",
-               "Incandescent",
-               "Fluorescent",
-               "Fluorescent H",
-               "Horizon",
-               "Daylight",
-               "Flash",
-               "Cloudy",
-               "Shade",
-               NULL,
-       };
-       static const char * const camera_iso_sensitivity_auto[] = {
-               "Manual",
-               "Auto",
-               NULL
-       };
-       static const char * const scene_mode[] = {
-               "None",
-               "Backlight",
-               "Beach/Snow",
-               "Candle Light",
-               "Dusk/Dawn",
-               "Fall Colors",
-               "Fireworks",
-               "Landscape",
-               "Night",
-               "Party/Indoor",
-               "Portrait",
-               "Sports",
-               "Sunset",
-               "Text",
-               NULL
-       };
-       static const char * const tune_preemphasis[] = {
-               "No Preemphasis",
-               "50 Microseconds",
-               "75 Microseconds",
-               NULL,
-       };
-       static const char * const header_mode[] = {
-               "Separate Buffer",
-               "Joined With 1st Frame",
-               NULL,
-       };
-       static const char * const multi_slice[] = {
-               "Single",
-               "Max Macroblocks",
-               "Max Bytes",
-               NULL,
-       };
-       static const char * const entropy_mode[] = {
-               "CAVLC",
-               "CABAC",
-               NULL,
-       };
-       static const char * const mpeg_h264_level[] = {
-               "1",
-               "1b",
-               "1.1",
-               "1.2",
-               "1.3",
-               "2",
-               "2.1",
-               "2.2",
-               "3",
-               "3.1",
-               "3.2",
-               "4",
-               "4.1",
-               "4.2",
-               "5",
-               "5.1",
-               NULL,
-       };
-       static const char * const h264_loop_filter[] = {
-               "Enabled",
-               "Disabled",
-               "Disabled at Slice Boundary",
-               NULL,
-       };
-       static const char * const h264_profile[] = {
-               "Baseline",
-               "Constrained Baseline",
-               "Main",
-               "Extended",
-               "High",
-               "High 10",
-               "High 422",
-               "High 444 Predictive",
-               "High 10 Intra",
-               "High 422 Intra",
-               "High 444 Intra",
-               "CAVLC 444 Intra",
-               "Scalable Baseline",
-               "Scalable High",
-               "Scalable High Intra",
-               "Multiview High",
-               NULL,
-       };
-       static const char * const vui_sar_idc[] = {
-               "Unspecified",
-               "1:1",
-               "12:11",
-               "10:11",
-               "16:11",
-               "40:33",
-               "24:11",
-               "20:11",
-               "32:11",
-               "80:33",
-               "18:11",
-               "15:11",
-               "64:33",
-               "160:99",
-               "4:3",
-               "3:2",
-               "2:1",
-               "Extended SAR",
-               NULL,
-       };
-       static const char * const mpeg_mpeg4_level[] = {
-               "0",
-               "0b",
-               "1",
-               "2",
-               "3",
-               "3b",
-               "4",
-               "5",
-               NULL,
-       };
-       static const char * const mpeg4_profile[] = {
-               "Simple",
-               "Advanced Simple",
-               "Core",
-               "Simple Scalable",
-               "Advanced Coding Efficency",
-               NULL,
-       };
-
-       static const char * const flash_led_mode[] = {
-               "Off",
-               "Flash",
-               "Torch",
-               NULL,
-       };
-       static const char * const flash_strobe_source[] = {
-               "Software",
-               "External",
-               NULL,
-       };
-
-       static const char * const jpeg_chroma_subsampling[] = {
-               "4:4:4",
-               "4:2:2",
-               "4:2:0",
-               "4:1:1",
-               "4:1:0",
-               "Gray",
-               NULL,
-       };
-
-       switch (id) {
-       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
-               return mpeg_audio_sampling_freq;
-       case V4L2_CID_MPEG_AUDIO_ENCODING:
-               return mpeg_audio_encoding;
-       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
-               return mpeg_audio_l1_bitrate;
-       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
-               return mpeg_audio_l2_bitrate;
-       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
-               return mpeg_audio_l3_bitrate;
-       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
-               return mpeg_audio_ac3_bitrate;
-       case V4L2_CID_MPEG_AUDIO_MODE:
-               return mpeg_audio_mode;
-       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
-               return mpeg_audio_mode_extension;
-       case V4L2_CID_MPEG_AUDIO_EMPHASIS:
-               return mpeg_audio_emphasis;
-       case V4L2_CID_MPEG_AUDIO_CRC:
-               return mpeg_audio_crc;
-       case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
-       case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
-               return mpeg_audio_dec_playback;
-       case V4L2_CID_MPEG_VIDEO_ENCODING:
-               return mpeg_video_encoding;
-       case V4L2_CID_MPEG_VIDEO_ASPECT:
-               return mpeg_video_aspect;
-       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
-               return mpeg_video_bitrate_mode;
-       case V4L2_CID_MPEG_STREAM_TYPE:
-               return mpeg_stream_type;
-       case V4L2_CID_MPEG_STREAM_VBI_FMT:
-               return mpeg_stream_vbi_fmt;
-       case V4L2_CID_POWER_LINE_FREQUENCY:
-               return camera_power_line_frequency;
-       case V4L2_CID_EXPOSURE_AUTO:
-               return camera_exposure_auto;
-       case V4L2_CID_EXPOSURE_METERING:
-               return camera_exposure_metering;
-       case V4L2_CID_AUTO_FOCUS_RANGE:
-               return camera_auto_focus_range;
-       case V4L2_CID_COLORFX:
-               return colorfx;
-       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
-               return auto_n_preset_white_balance;
-       case V4L2_CID_ISO_SENSITIVITY_AUTO:
-               return camera_iso_sensitivity_auto;
-       case V4L2_CID_SCENE_MODE:
-               return scene_mode;
-       case V4L2_CID_TUNE_PREEMPHASIS:
-               return tune_preemphasis;
-       case V4L2_CID_FLASH_LED_MODE:
-               return flash_led_mode;
-       case V4L2_CID_FLASH_STROBE_SOURCE:
-               return flash_strobe_source;
-       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
-               return header_mode;
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
-               return multi_slice;
-       case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
-               return entropy_mode;
-       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
-               return mpeg_h264_level;
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
-               return h264_loop_filter;
-       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
-               return h264_profile;
-       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
-               return vui_sar_idc;
-       case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
-               return mpeg_mpeg4_level;
-       case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
-               return mpeg4_profile;
-       case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
-               return jpeg_chroma_subsampling;
-
-       default:
-               return NULL;
-       }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_menu);
-
-/* Return the control name. */
-const char *v4l2_ctrl_get_name(u32 id)
-{
-       switch (id) {
-       /* USER controls */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
-       case V4L2_CID_USER_CLASS:               return "User Controls";
-       case V4L2_CID_BRIGHTNESS:               return "Brightness";
-       case V4L2_CID_CONTRAST:                 return "Contrast";
-       case V4L2_CID_SATURATION:               return "Saturation";
-       case V4L2_CID_HUE:                      return "Hue";
-       case V4L2_CID_AUDIO_VOLUME:             return "Volume";
-       case V4L2_CID_AUDIO_BALANCE:            return "Balance";
-       case V4L2_CID_AUDIO_BASS:               return "Bass";
-       case V4L2_CID_AUDIO_TREBLE:             return "Treble";
-       case V4L2_CID_AUDIO_MUTE:               return "Mute";
-       case V4L2_CID_AUDIO_LOUDNESS:           return "Loudness";
-       case V4L2_CID_BLACK_LEVEL:              return "Black Level";
-       case V4L2_CID_AUTO_WHITE_BALANCE:       return "White Balance, Automatic";
-       case V4L2_CID_DO_WHITE_BALANCE:         return "Do White Balance";
-       case V4L2_CID_RED_BALANCE:              return "Red Balance";
-       case V4L2_CID_BLUE_BALANCE:             return "Blue Balance";
-       case V4L2_CID_GAMMA:                    return "Gamma";
-       case V4L2_CID_EXPOSURE:                 return "Exposure";
-       case V4L2_CID_AUTOGAIN:                 return "Gain, Automatic";
-       case V4L2_CID_GAIN:                     return "Gain";
-       case V4L2_CID_HFLIP:                    return "Horizontal Flip";
-       case V4L2_CID_VFLIP:                    return "Vertical Flip";
-       case V4L2_CID_HCENTER:                  return "Horizontal Center";
-       case V4L2_CID_VCENTER:                  return "Vertical Center";
-       case V4L2_CID_POWER_LINE_FREQUENCY:     return "Power Line Frequency";
-       case V4L2_CID_HUE_AUTO:                 return "Hue, Automatic";
-       case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
-       case V4L2_CID_SHARPNESS:                return "Sharpness";
-       case V4L2_CID_BACKLIGHT_COMPENSATION:   return "Backlight Compensation";
-       case V4L2_CID_CHROMA_AGC:               return "Chroma AGC";
-       case V4L2_CID_COLOR_KILLER:             return "Color Killer";
-       case V4L2_CID_COLORFX:                  return "Color Effects";
-       case V4L2_CID_AUTOBRIGHTNESS:           return "Brightness, Automatic";
-       case V4L2_CID_BAND_STOP_FILTER:         return "Band-Stop Filter";
-       case V4L2_CID_ROTATE:                   return "Rotate";
-       case V4L2_CID_BG_COLOR:                 return "Background Color";
-       case V4L2_CID_CHROMA_GAIN:              return "Chroma Gain";
-       case V4L2_CID_ILLUMINATORS_1:           return "Illuminator 1";
-       case V4L2_CID_ILLUMINATORS_2:           return "Illuminator 2";
-       case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:  return "Min Number of Capture Buffers";
-       case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:   return "Min Number of Output Buffers";
-       case V4L2_CID_ALPHA_COMPONENT:          return "Alpha Component";
-       case V4L2_CID_COLORFX_CBCR:             return "Color Effects, CbCr";
-
-       /* MPEG controls */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
-       case V4L2_CID_MPEG_CLASS:               return "MPEG Encoder Controls";
-       case V4L2_CID_MPEG_STREAM_TYPE:         return "Stream Type";
-       case V4L2_CID_MPEG_STREAM_PID_PMT:      return "Stream PMT Program ID";
-       case V4L2_CID_MPEG_STREAM_PID_AUDIO:    return "Stream Audio Program ID";
-       case V4L2_CID_MPEG_STREAM_PID_VIDEO:    return "Stream Video Program ID";
-       case V4L2_CID_MPEG_STREAM_PID_PCR:      return "Stream PCR Program ID";
-       case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
-       case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
-       case V4L2_CID_MPEG_STREAM_VBI_FMT:      return "Stream VBI Format";
-       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
-       case V4L2_CID_MPEG_AUDIO_ENCODING:      return "Audio Encoding";
-       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:    return "Audio Layer I Bitrate";
-       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:    return "Audio Layer II Bitrate";
-       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:    return "Audio Layer III Bitrate";
-       case V4L2_CID_MPEG_AUDIO_MODE:          return "Audio Stereo Mode";
-       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
-       case V4L2_CID_MPEG_AUDIO_EMPHASIS:      return "Audio Emphasis";
-       case V4L2_CID_MPEG_AUDIO_CRC:           return "Audio CRC";
-       case V4L2_CID_MPEG_AUDIO_MUTE:          return "Audio Mute";
-       case V4L2_CID_MPEG_AUDIO_AAC_BITRATE:   return "Audio AAC Bitrate";
-       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:   return "Audio AC-3 Bitrate";
-       case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:  return "Audio Playback";
-       case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback";
-       case V4L2_CID_MPEG_VIDEO_ENCODING:      return "Video Encoding";
-       case V4L2_CID_MPEG_VIDEO_ASPECT:        return "Video Aspect";
-       case V4L2_CID_MPEG_VIDEO_B_FRAMES:      return "Video B Frames";
-       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:      return "Video GOP Size";
-       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:   return "Video GOP Closure";
-       case V4L2_CID_MPEG_VIDEO_PULLDOWN:      return "Video Pulldown";
-       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:  return "Video Bitrate Mode";
-       case V4L2_CID_MPEG_VIDEO_BITRATE:       return "Video Bitrate";
-       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:  return "Video Peak Bitrate";
-       case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
-       case V4L2_CID_MPEG_VIDEO_MUTE:          return "Video Mute";
-       case V4L2_CID_MPEG_VIDEO_MUTE_YUV:      return "Video Mute YUV";
-       case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:       return "Decoder Slice Interface";
-       case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:  return "MPEG4 Loop Filter Enable";
-       case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:       return "Number of Intra Refresh MBs";
-       case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:               return "Frame Level Rate Control Enable";
-       case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:                  return "H264 MB Level Rate Control";
-       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:                   return "Sequence Header Mode";
-       case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC:                   return "Max Number of Reference Pics";
-       case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:               return "H263 I-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:               return "H263 P-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:               return "H263 B-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:                   return "H263 Minimum QP Value";
-       case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:                   return "H263 Maximum QP Value";
-       case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:               return "H264 I-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:               return "H264 P-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:               return "H264 B-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:                   return "H264 Maximum QP Value";
-       case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:                   return "H264 Minimum QP Value";
-       case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:            return "H264 8x8 Transform Enable";
-       case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:                 return "H264 CPB Buffer Size";
-       case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:             return "H264 Entropy Mode";
-       case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:                 return "H264 I-Frame Period";
-       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:                    return "H264 Level";
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:        return "H264 Loop Filter Alpha Offset";
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:         return "H264 Loop Filter Beta Offset";
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:         return "H264 Loop Filter Mode";
-       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:                  return "H264 Profile";
-       case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:       return "Vertical Size of SAR";
-       case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:        return "Horizontal Size of SAR";
-       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:           return "Aspect Ratio VUI Enable";
-       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:              return "VUI Aspect Ratio IDC";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:              return "MPEG4 I-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:              return "MPEG4 P-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:              return "MPEG4 B-Frame QP Value";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:                  return "MPEG4 Minimum QP Value";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:                  return "MPEG4 Maximum QP Value";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:                   return "MPEG4 Level";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:                 return "MPEG4 Profile";
-       case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:                    return "Quarter Pixel Search Enable";
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:         return "Maximum Bytes in a Slice";
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:            return "Number of MBs in a Slice";
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:              return "Slice Partitioning Method";
-       case V4L2_CID_MPEG_VIDEO_VBV_SIZE:                      return "VBV Buffer Size";
-       case V4L2_CID_MPEG_VIDEO_DEC_PTS:                       return "Video Decoder PTS";
-       case V4L2_CID_MPEG_VIDEO_DEC_FRAME:                     return "Video Decoder Frame Count";
-
-       /* CAMERA controls */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
-       case V4L2_CID_CAMERA_CLASS:             return "Camera Controls";
-       case V4L2_CID_EXPOSURE_AUTO:            return "Auto Exposure";
-       case V4L2_CID_EXPOSURE_ABSOLUTE:        return "Exposure Time, Absolute";
-       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:   return "Exposure, Dynamic Framerate";
-       case V4L2_CID_PAN_RELATIVE:             return "Pan, Relative";
-       case V4L2_CID_TILT_RELATIVE:            return "Tilt, Relative";
-       case V4L2_CID_PAN_RESET:                return "Pan, Reset";
-       case V4L2_CID_TILT_RESET:               return "Tilt, Reset";
-       case V4L2_CID_PAN_ABSOLUTE:             return "Pan, Absolute";
-       case V4L2_CID_TILT_ABSOLUTE:            return "Tilt, Absolute";
-       case V4L2_CID_FOCUS_ABSOLUTE:           return "Focus, Absolute";
-       case V4L2_CID_FOCUS_RELATIVE:           return "Focus, Relative";
-       case V4L2_CID_FOCUS_AUTO:               return "Focus, Automatic Continuous";
-       case V4L2_CID_ZOOM_ABSOLUTE:            return "Zoom, Absolute";
-       case V4L2_CID_ZOOM_RELATIVE:            return "Zoom, Relative";
-       case V4L2_CID_ZOOM_CONTINUOUS:          return "Zoom, Continuous";
-       case V4L2_CID_PRIVACY:                  return "Privacy";
-       case V4L2_CID_IRIS_ABSOLUTE:            return "Iris, Absolute";
-       case V4L2_CID_IRIS_RELATIVE:            return "Iris, Relative";
-       case V4L2_CID_AUTO_EXPOSURE_BIAS:       return "Auto Exposure, Bias";
-       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset";
-       case V4L2_CID_WIDE_DYNAMIC_RANGE:       return "Wide Dynamic Range";
-       case V4L2_CID_IMAGE_STABILIZATION:      return "Image Stabilization";
-       case V4L2_CID_ISO_SENSITIVITY:          return "ISO Sensitivity";
-       case V4L2_CID_ISO_SENSITIVITY_AUTO:     return "ISO Sensitivity, Auto";
-       case V4L2_CID_EXPOSURE_METERING:        return "Exposure, Metering Mode";
-       case V4L2_CID_SCENE_MODE:               return "Scene Mode";
-       case V4L2_CID_3A_LOCK:                  return "3A Lock";
-       case V4L2_CID_AUTO_FOCUS_START:         return "Auto Focus, Start";
-       case V4L2_CID_AUTO_FOCUS_STOP:          return "Auto Focus, Stop";
-       case V4L2_CID_AUTO_FOCUS_STATUS:        return "Auto Focus, Status";
-       case V4L2_CID_AUTO_FOCUS_RANGE:         return "Auto Focus, Range";
-
-       /* FM Radio Modulator control */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
-       case V4L2_CID_FM_TX_CLASS:              return "FM Radio Modulator Controls";
-       case V4L2_CID_RDS_TX_DEVIATION:         return "RDS Signal Deviation";
-       case V4L2_CID_RDS_TX_PI:                return "RDS Program ID";
-       case V4L2_CID_RDS_TX_PTY:               return "RDS Program Type";
-       case V4L2_CID_RDS_TX_PS_NAME:           return "RDS PS Name";
-       case V4L2_CID_RDS_TX_RADIO_TEXT:        return "RDS Radio Text";
-       case V4L2_CID_AUDIO_LIMITER_ENABLED:    return "Audio Limiter Feature Enabled";
-       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
-       case V4L2_CID_AUDIO_LIMITER_DEVIATION:  return "Audio Limiter Deviation";
-       case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Enabled";
-       case V4L2_CID_AUDIO_COMPRESSION_GAIN:   return "Audio Compression Gain";
-       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
-       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
-       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
-       case V4L2_CID_PILOT_TONE_ENABLED:       return "Pilot Tone Feature Enabled";
-       case V4L2_CID_PILOT_TONE_DEVIATION:     return "Pilot Tone Deviation";
-       case V4L2_CID_PILOT_TONE_FREQUENCY:     return "Pilot Tone Frequency";
-       case V4L2_CID_TUNE_PREEMPHASIS:         return "Pre-Emphasis";
-       case V4L2_CID_TUNE_POWER_LEVEL:         return "Tune Power Level";
-       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:   return "Tune Antenna Capacitor";
-
-       /* Flash controls */
-       case V4L2_CID_FLASH_CLASS:              return "Flash Controls";
-       case V4L2_CID_FLASH_LED_MODE:           return "LED Mode";
-       case V4L2_CID_FLASH_STROBE_SOURCE:      return "Strobe Source";
-       case V4L2_CID_FLASH_STROBE:             return "Strobe";
-       case V4L2_CID_FLASH_STROBE_STOP:        return "Stop Strobe";
-       case V4L2_CID_FLASH_STROBE_STATUS:      return "Strobe Status";
-       case V4L2_CID_FLASH_TIMEOUT:            return "Strobe Timeout";
-       case V4L2_CID_FLASH_INTENSITY:          return "Intensity, Flash Mode";
-       case V4L2_CID_FLASH_TORCH_INTENSITY:    return "Intensity, Torch Mode";
-       case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, Indicator";
-       case V4L2_CID_FLASH_FAULT:              return "Faults";
-       case V4L2_CID_FLASH_CHARGE:             return "Charge";
-       case V4L2_CID_FLASH_READY:              return "Ready to Strobe";
-
-       /* JPEG encoder controls */
-       /* Keep the order of the 'case's the same as in videodev2.h! */
-       case V4L2_CID_JPEG_CLASS:               return "JPEG Compression Controls";
-       case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:  return "Chroma Subsampling";
-       case V4L2_CID_JPEG_RESTART_INTERVAL:    return "Restart Interval";
-       case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
-       case V4L2_CID_JPEG_ACTIVE_MARKER:       return "Active Markers";
-
-       /* Image source controls */
-       case V4L2_CID_IMAGE_SOURCE_CLASS:       return "Image Source Controls";
-       case V4L2_CID_VBLANK:                   return "Vertical Blanking";
-       case V4L2_CID_HBLANK:                   return "Horizontal Blanking";
-       case V4L2_CID_ANALOGUE_GAIN:            return "Analogue Gain";
-
-       /* Image processing controls */
-       case V4L2_CID_IMAGE_PROC_CLASS:         return "Image Processing Controls";
-       case V4L2_CID_LINK_FREQ:                return "Link Frequency";
-       case V4L2_CID_PIXEL_RATE:               return "Pixel Rate";
-
-       default:
-               return NULL;
-       }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_name);
-
-void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
-                   s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
-{
-       *name = v4l2_ctrl_get_name(id);
-       *flags = 0;
-
-       switch (id) {
-       case V4L2_CID_AUDIO_MUTE:
-       case V4L2_CID_AUDIO_LOUDNESS:
-       case V4L2_CID_AUTO_WHITE_BALANCE:
-       case V4L2_CID_AUTOGAIN:
-       case V4L2_CID_HFLIP:
-       case V4L2_CID_VFLIP:
-       case V4L2_CID_HUE_AUTO:
-       case V4L2_CID_CHROMA_AGC:
-       case V4L2_CID_COLOR_KILLER:
-       case V4L2_CID_AUTOBRIGHTNESS:
-       case V4L2_CID_MPEG_AUDIO_MUTE:
-       case V4L2_CID_MPEG_VIDEO_MUTE:
-       case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
-       case V4L2_CID_MPEG_VIDEO_PULLDOWN:
-       case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
-       case V4L2_CID_FOCUS_AUTO:
-       case V4L2_CID_PRIVACY:
-       case V4L2_CID_AUDIO_LIMITER_ENABLED:
-       case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
-       case V4L2_CID_PILOT_TONE_ENABLED:
-       case V4L2_CID_ILLUMINATORS_1:
-       case V4L2_CID_ILLUMINATORS_2:
-       case V4L2_CID_FLASH_STROBE_STATUS:
-       case V4L2_CID_FLASH_CHARGE:
-       case V4L2_CID_FLASH_READY:
-       case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
-       case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
-       case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
-       case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
-       case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
-       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
-       case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
-       case V4L2_CID_WIDE_DYNAMIC_RANGE:
-       case V4L2_CID_IMAGE_STABILIZATION:
-               *type = V4L2_CTRL_TYPE_BOOLEAN;
-               *min = 0;
-               *max = *step = 1;
-               break;
-       case V4L2_CID_PAN_RESET:
-       case V4L2_CID_TILT_RESET:
-       case V4L2_CID_FLASH_STROBE:
-       case V4L2_CID_FLASH_STROBE_STOP:
-       case V4L2_CID_AUTO_FOCUS_START:
-       case V4L2_CID_AUTO_FOCUS_STOP:
-               *type = V4L2_CTRL_TYPE_BUTTON;
-               *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
-               *min = *max = *step = *def = 0;
-               break;
-       case V4L2_CID_POWER_LINE_FREQUENCY:
-       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
-       case V4L2_CID_MPEG_AUDIO_ENCODING:
-       case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
-       case V4L2_CID_MPEG_AUDIO_MODE:
-       case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
-       case V4L2_CID_MPEG_AUDIO_EMPHASIS:
-       case V4L2_CID_MPEG_AUDIO_CRC:
-       case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
-       case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
-       case V4L2_CID_MPEG_VIDEO_ENCODING:
-       case V4L2_CID_MPEG_VIDEO_ASPECT:
-       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
-       case V4L2_CID_MPEG_STREAM_TYPE:
-       case V4L2_CID_MPEG_STREAM_VBI_FMT:
-       case V4L2_CID_EXPOSURE_AUTO:
-       case V4L2_CID_AUTO_FOCUS_RANGE:
-       case V4L2_CID_COLORFX:
-       case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
-       case V4L2_CID_TUNE_PREEMPHASIS:
-       case V4L2_CID_FLASH_LED_MODE:
-       case V4L2_CID_FLASH_STROBE_SOURCE:
-       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
-       case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
-       case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
-       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
-       case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
-       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
-       case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
-       case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
-       case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
-       case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
-       case V4L2_CID_ISO_SENSITIVITY_AUTO:
-       case V4L2_CID_EXPOSURE_METERING:
-       case V4L2_CID_SCENE_MODE:
-               *type = V4L2_CTRL_TYPE_MENU;
-               break;
-       case V4L2_CID_LINK_FREQ:
-               *type = V4L2_CTRL_TYPE_INTEGER_MENU;
-               break;
-       case V4L2_CID_RDS_TX_PS_NAME:
-       case V4L2_CID_RDS_TX_RADIO_TEXT:
-               *type = V4L2_CTRL_TYPE_STRING;
-               break;
-       case V4L2_CID_ISO_SENSITIVITY:
-       case V4L2_CID_AUTO_EXPOSURE_BIAS:
-               *type = V4L2_CTRL_TYPE_INTEGER_MENU;
-               break;
-       case V4L2_CID_USER_CLASS:
-       case V4L2_CID_CAMERA_CLASS:
-       case V4L2_CID_MPEG_CLASS:
-       case V4L2_CID_FM_TX_CLASS:
-       case V4L2_CID_FLASH_CLASS:
-       case V4L2_CID_JPEG_CLASS:
-       case V4L2_CID_IMAGE_SOURCE_CLASS:
-       case V4L2_CID_IMAGE_PROC_CLASS:
-               *type = V4L2_CTRL_TYPE_CTRL_CLASS;
-               /* You can neither read not write these */
-               *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
-               *min = *max = *step = *def = 0;
-               break;
-       case V4L2_CID_BG_COLOR:
-               *type = V4L2_CTRL_TYPE_INTEGER;
-               *step = 1;
-               *min = 0;
-               /* Max is calculated as RGB888 that is 2^24 */
-               *max = 0xFFFFFF;
-               break;
-       case V4L2_CID_FLASH_FAULT:
-       case V4L2_CID_JPEG_ACTIVE_MARKER:
-       case V4L2_CID_3A_LOCK:
-       case V4L2_CID_AUTO_FOCUS_STATUS:
-               *type = V4L2_CTRL_TYPE_BITMASK;
-               break;
-       case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
-       case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
-               *type = V4L2_CTRL_TYPE_INTEGER;
-               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
-               break;
-       case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
-       case V4L2_CID_MPEG_VIDEO_DEC_PTS:
-               *flags |= V4L2_CTRL_FLAG_VOLATILE;
-               /* Fall through */
-       case V4L2_CID_PIXEL_RATE:
-               *type = V4L2_CTRL_TYPE_INTEGER64;
-               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
-               *min = *max = *step = *def = 0;
-               break;
-       default:
-               *type = V4L2_CTRL_TYPE_INTEGER;
-               break;
-       }
-       switch (id) {
-       case V4L2_CID_MPEG_AUDIO_ENCODING:
-       case V4L2_CID_MPEG_AUDIO_MODE:
-       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
-       case V4L2_CID_MPEG_VIDEO_B_FRAMES:
-       case V4L2_CID_MPEG_STREAM_TYPE:
-               *flags |= V4L2_CTRL_FLAG_UPDATE;
-               break;
-       case V4L2_CID_AUDIO_VOLUME:
-       case V4L2_CID_AUDIO_BALANCE:
-       case V4L2_CID_AUDIO_BASS:
-       case V4L2_CID_AUDIO_TREBLE:
-       case V4L2_CID_BRIGHTNESS:
-       case V4L2_CID_CONTRAST:
-       case V4L2_CID_SATURATION:
-       case V4L2_CID_HUE:
-       case V4L2_CID_RED_BALANCE:
-       case V4L2_CID_BLUE_BALANCE:
-       case V4L2_CID_GAMMA:
-       case V4L2_CID_SHARPNESS:
-       case V4L2_CID_CHROMA_GAIN:
-       case V4L2_CID_RDS_TX_DEVIATION:
-       case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
-       case V4L2_CID_AUDIO_LIMITER_DEVIATION:
-       case V4L2_CID_AUDIO_COMPRESSION_GAIN:
-       case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
-       case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
-       case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
-       case V4L2_CID_PILOT_TONE_DEVIATION:
-       case V4L2_CID_PILOT_TONE_FREQUENCY:
-       case V4L2_CID_TUNE_POWER_LEVEL:
-       case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
-               *flags |= V4L2_CTRL_FLAG_SLIDER;
-               break;
-       case V4L2_CID_PAN_RELATIVE:
-       case V4L2_CID_TILT_RELATIVE:
-       case V4L2_CID_FOCUS_RELATIVE:
-       case V4L2_CID_IRIS_RELATIVE:
-       case V4L2_CID_ZOOM_RELATIVE:
-               *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
-               break;
-       case V4L2_CID_FLASH_STROBE_STATUS:
-       case V4L2_CID_AUTO_FOCUS_STATUS:
-       case V4L2_CID_FLASH_READY:
-               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
-               break;
-       }
-}
-EXPORT_SYMBOL(v4l2_ctrl_fill);
-
-/* Helper function to determine whether the control type is compatible with
-   VIDIOC_G/S_CTRL. */
-static bool type_is_int(const struct v4l2_ctrl *ctrl)
-{
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER64:
-       case V4L2_CTRL_TYPE_STRING:
-               /* Nope, these need v4l2_ext_control */
-               return false;
-       default:
-               return true;
-       }
-}
-
-static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
-{
-       memset(ev->reserved, 0, sizeof(ev->reserved));
-       ev->type = V4L2_EVENT_CTRL;
-       ev->id = ctrl->id;
-       ev->u.ctrl.changes = changes;
-       ev->u.ctrl.type = ctrl->type;
-       ev->u.ctrl.flags = ctrl->flags;
-       if (ctrl->type == V4L2_CTRL_TYPE_STRING)
-               ev->u.ctrl.value64 = 0;
-       else
-               ev->u.ctrl.value64 = ctrl->cur.val64;
-       ev->u.ctrl.minimum = ctrl->minimum;
-       ev->u.ctrl.maximum = ctrl->maximum;
-       if (ctrl->type == V4L2_CTRL_TYPE_MENU
-           || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
-               ev->u.ctrl.step = 1;
-       else
-               ev->u.ctrl.step = ctrl->step;
-       ev->u.ctrl.default_value = ctrl->default_value;
-}
-
-static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
-{
-       struct v4l2_event ev;
-       struct v4l2_subscribed_event *sev;
-
-       if (list_empty(&ctrl->ev_subs))
-               return;
-       fill_event(&ev, ctrl, changes);
-
-       list_for_each_entry(sev, &ctrl->ev_subs, node)
-               if (sev->fh != fh ||
-                   (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))
-                       v4l2_event_queue_fh(sev->fh, &ev);
-}
-
-/* Helper function: copy the current control value back to the caller */
-static int cur_to_user(struct v4l2_ext_control *c,
-                      struct v4l2_ctrl *ctrl)
-{
-       u32 len;
-
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_STRING:
-               len = strlen(ctrl->cur.string);
-               if (c->size < len + 1) {
-                       c->size = len + 1;
-                       return -ENOSPC;
-               }
-               return copy_to_user(c->string, ctrl->cur.string,
-                                               len + 1) ? -EFAULT : 0;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               c->value64 = ctrl->cur.val64;
-               break;
-       default:
-               c->value = ctrl->cur.val;
-               break;
-       }
-       return 0;
-}
-
-/* Helper function: copy the caller-provider value as the new control value */
-static int user_to_new(struct v4l2_ext_control *c,
-                      struct v4l2_ctrl *ctrl)
-{
-       int ret;
-       u32 size;
-
-       ctrl->is_new = 1;
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER64:
-               ctrl->val64 = c->value64;
-               break;
-       case V4L2_CTRL_TYPE_STRING:
-               size = c->size;
-               if (size == 0)
-                       return -ERANGE;
-               if (size > ctrl->maximum + 1)
-                       size = ctrl->maximum + 1;
-               ret = copy_from_user(ctrl->string, c->string, size);
-               if (!ret) {
-                       char last = ctrl->string[size - 1];
-
-                       ctrl->string[size - 1] = 0;
-                       /* If the string was longer than ctrl->maximum,
-                          then return an error. */
-                       if (strlen(ctrl->string) == ctrl->maximum && last)
-                               return -ERANGE;
-               }
-               return ret ? -EFAULT : 0;
-       default:
-               ctrl->val = c->value;
-               break;
-       }
-       return 0;
-}
-
-/* Helper function: copy the new control value back to the caller */
-static int new_to_user(struct v4l2_ext_control *c,
-                      struct v4l2_ctrl *ctrl)
-{
-       u32 len;
-
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_STRING:
-               len = strlen(ctrl->string);
-               if (c->size < len + 1) {
-                       c->size = ctrl->maximum + 1;
-                       return -ENOSPC;
-               }
-               return copy_to_user(c->string, ctrl->string,
-                                               len + 1) ? -EFAULT : 0;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               c->value64 = ctrl->val64;
-               break;
-       default:
-               c->value = ctrl->val;
-               break;
-       }
-       return 0;
-}
-
-/* Copy the new value to the current value. */
-static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
-                                               bool update_inactive)
-{
-       bool changed = false;
-
-       if (ctrl == NULL)
-               return;
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_BUTTON:
-               changed = true;
-               break;
-       case V4L2_CTRL_TYPE_STRING:
-               /* strings are always 0-terminated */
-               changed = strcmp(ctrl->string, ctrl->cur.string);
-               strcpy(ctrl->cur.string, ctrl->string);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               changed = ctrl->val64 != ctrl->cur.val64;
-               ctrl->cur.val64 = ctrl->val64;
-               break;
-       default:
-               changed = ctrl->val != ctrl->cur.val;
-               ctrl->cur.val = ctrl->val;
-               break;
-       }
-       if (update_inactive) {
-               /* Note: update_inactive can only be true for auto clusters. */
-               ctrl->flags &=
-                       ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
-               if (!is_cur_manual(ctrl->cluster[0])) {
-                       ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
-                       if (ctrl->cluster[0]->has_volatiles)
-                               ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
-               }
-               fh = NULL;
-       }
-       if (changed || update_inactive) {
-               /* If a control was changed that was not one of the controls
-                  modified by the application, then send the event to all. */
-               if (!ctrl->is_new)
-                       fh = NULL;
-               send_event(fh, ctrl,
-                       (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
-                       (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
-       }
-}
-
-/* Copy the current value to the new value */
-static void cur_to_new(struct v4l2_ctrl *ctrl)
-{
-       if (ctrl == NULL)
-               return;
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_STRING:
-               /* strings are always 0-terminated */
-               strcpy(ctrl->string, ctrl->cur.string);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               ctrl->val64 = ctrl->cur.val64;
-               break;
-       default:
-               ctrl->val = ctrl->cur.val;
-               break;
-       }
-}
-
-/* Return non-zero if one or more of the controls in the cluster has a new
-   value that differs from the current value. */
-static int cluster_changed(struct v4l2_ctrl *master)
-{
-       int diff = 0;
-       int i;
-
-       for (i = 0; !diff && i < master->ncontrols; i++) {
-               struct v4l2_ctrl *ctrl = master->cluster[i];
-
-               if (ctrl == NULL)
-                       continue;
-               switch (ctrl->type) {
-               case V4L2_CTRL_TYPE_BUTTON:
-                       /* Button controls are always 'different' */
-                       return 1;
-               case V4L2_CTRL_TYPE_STRING:
-                       /* strings are always 0-terminated */
-                       diff = strcmp(ctrl->string, ctrl->cur.string);
-                       break;
-               case V4L2_CTRL_TYPE_INTEGER64:
-                       diff = ctrl->val64 != ctrl->cur.val64;
-                       break;
-               default:
-                       diff = ctrl->val != ctrl->cur.val;
-                       break;
-               }
-       }
-       return diff;
-}
-
-/* Validate integer-type control */
-static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval)
-{
-       s32 val = *pval;
-       u32 offset;
-
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER:
-               /* Round towards the closest legal value */
-               val += ctrl->step / 2;
-               if (val < ctrl->minimum)
-                       val = ctrl->minimum;
-               if (val > ctrl->maximum)
-                       val = ctrl->maximum;
-               offset = val - ctrl->minimum;
-               offset = ctrl->step * (offset / ctrl->step);
-               val = ctrl->minimum + offset;
-               *pval = val;
-               return 0;
-
-       case V4L2_CTRL_TYPE_BOOLEAN:
-               *pval = !!val;
-               return 0;
-
-       case V4L2_CTRL_TYPE_MENU:
-       case V4L2_CTRL_TYPE_INTEGER_MENU:
-               if (val < ctrl->minimum || val > ctrl->maximum)
-                       return -ERANGE;
-               if (ctrl->menu_skip_mask & (1 << val))
-                       return -EINVAL;
-               if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
-                   ctrl->qmenu[val][0] == '\0')
-                       return -EINVAL;
-               return 0;
-
-       case V4L2_CTRL_TYPE_BITMASK:
-               *pval &= ctrl->maximum;
-               return 0;
-
-       case V4L2_CTRL_TYPE_BUTTON:
-       case V4L2_CTRL_TYPE_CTRL_CLASS:
-               *pval = 0;
-               return 0;
-
-       default:
-               return -EINVAL;
-       }
-}
-
-/* Validate a new control */
-static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
-{
-       char *s = c->string;
-       size_t len;
-
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER:
-       case V4L2_CTRL_TYPE_BOOLEAN:
-       case V4L2_CTRL_TYPE_MENU:
-       case V4L2_CTRL_TYPE_INTEGER_MENU:
-       case V4L2_CTRL_TYPE_BITMASK:
-       case V4L2_CTRL_TYPE_BUTTON:
-       case V4L2_CTRL_TYPE_CTRL_CLASS:
-               return validate_new_int(ctrl, &c->value);
-
-       case V4L2_CTRL_TYPE_INTEGER64:
-               return 0;
-
-       case V4L2_CTRL_TYPE_STRING:
-               len = strlen(s);
-               if (len < ctrl->minimum)
-                       return -ERANGE;
-               if ((len - ctrl->minimum) % ctrl->step)
-                       return -ERANGE;
-               return 0;
-
-       default:
-               return -EINVAL;
-       }
-}
-
-static inline u32 node2id(struct list_head *node)
-{
-       return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
-}
-
-/* Set the handler's error code if it wasn't set earlier already */
-static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
-{
-       if (hdl->error == 0)
-               hdl->error = err;
-       return err;
-}
-
-/* Initialize the handler */
-int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
-                          unsigned nr_of_controls_hint)
-{
-       hdl->lock = &hdl->_lock;
-       mutex_init(hdl->lock);
-       INIT_LIST_HEAD(&hdl->ctrls);
-       INIT_LIST_HEAD(&hdl->ctrl_refs);
-       hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
-       hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]),
-                              GFP_KERNEL);
-       hdl->error = hdl->buckets ? 0 : -ENOMEM;
-       return hdl->error;
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_init);
-
-/* Free all controls and control refs */
-void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
-{
-       struct v4l2_ctrl_ref *ref, *next_ref;
-       struct v4l2_ctrl *ctrl, *next_ctrl;
-       struct v4l2_subscribed_event *sev, *next_sev;
-
-       if (hdl == NULL || hdl->buckets == NULL)
-               return;
-
-       mutex_lock(hdl->lock);
-       /* Free all nodes */
-       list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
-               list_del(&ref->node);
-               kfree(ref);
-       }
-       /* Free all controls owned by the handler */
-       list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
-               list_del(&ctrl->node);
-               list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
-                       list_del(&sev->node);
-               kfree(ctrl);
-       }
-       kfree(hdl->buckets);
-       hdl->buckets = NULL;
-       hdl->cached = NULL;
-       hdl->error = 0;
-       mutex_unlock(hdl->lock);
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_free);
-
-/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
-   be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
-   with applications that do not use the NEXT_CTRL flag.
-
-   We just find the n-th private user control. It's O(N), but that should not
-   be an issue in this particular case. */
-static struct v4l2_ctrl_ref *find_private_ref(
-               struct v4l2_ctrl_handler *hdl, u32 id)
-{
-       struct v4l2_ctrl_ref *ref;
-
-       id -= V4L2_CID_PRIVATE_BASE;
-       list_for_each_entry(ref, &hdl->ctrl_refs, node) {
-               /* Search for private user controls that are compatible with
-                  VIDIOC_G/S_CTRL. */
-               if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
-                   V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
-                       if (!type_is_int(ref->ctrl))
-                               continue;
-                       if (id == 0)
-                               return ref;
-                       id--;
-               }
-       }
-       return NULL;
-}
-
-/* Find a control with the given ID. */
-static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
-{
-       struct v4l2_ctrl_ref *ref;
-       int bucket;
-
-       id &= V4L2_CTRL_ID_MASK;
-
-       /* Old-style private controls need special handling */
-       if (id >= V4L2_CID_PRIVATE_BASE)
-               return find_private_ref(hdl, id);
-       bucket = id % hdl->nr_of_buckets;
-
-       /* Simple optimization: cache the last control found */
-       if (hdl->cached && hdl->cached->ctrl->id == id)
-               return hdl->cached;
-
-       /* Not in cache, search the hash */
-       ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
-       while (ref && ref->ctrl->id != id)
-               ref = ref->next;
-
-       if (ref)
-               hdl->cached = ref; /* cache it! */
-       return ref;
-}
-
-/* Find a control with the given ID. Take the handler's lock first. */
-static struct v4l2_ctrl_ref *find_ref_lock(
-               struct v4l2_ctrl_handler *hdl, u32 id)
-{
-       struct v4l2_ctrl_ref *ref = NULL;
-
-       if (hdl) {
-               mutex_lock(hdl->lock);
-               ref = find_ref(hdl, id);
-               mutex_unlock(hdl->lock);
-       }
-       return ref;
-}
-
-/* Find a control with the given ID. */
-struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
-{
-       struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
-
-       return ref ? ref->ctrl : NULL;
-}
-EXPORT_SYMBOL(v4l2_ctrl_find);
-
-/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
-static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
-                          struct v4l2_ctrl *ctrl)
-{
-       struct v4l2_ctrl_ref *ref;
-       struct v4l2_ctrl_ref *new_ref;
-       u32 id = ctrl->id;
-       u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
-       int bucket = id % hdl->nr_of_buckets;   /* which bucket to use */
-
-       /* Automatically add the control class if it is not yet present. */
-       if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
-               if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
-                       return hdl->error;
-
-       if (hdl->error)
-               return hdl->error;
-
-       new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
-       if (!new_ref)
-               return handler_set_err(hdl, -ENOMEM);
-       new_ref->ctrl = ctrl;
-       if (ctrl->handler == hdl) {
-               /* By default each control starts in a cluster of its own.
-                  new_ref->ctrl is basically a cluster array with one
-                  element, so that's perfect to use as the cluster pointer.
-                  But only do this for the handler that owns the control. */
-               ctrl->cluster = &new_ref->ctrl;
-               ctrl->ncontrols = 1;
-       }
-
-       INIT_LIST_HEAD(&new_ref->node);
-
-       mutex_lock(hdl->lock);
-
-       /* Add immediately at the end of the list if the list is empty, or if
-          the last element in the list has a lower ID.
-          This ensures that when elements are added in ascending order the
-          insertion is an O(1) operation. */
-       if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
-               list_add_tail(&new_ref->node, &hdl->ctrl_refs);
-               goto insert_in_hash;
-       }
-
-       /* Find insert position in sorted list */
-       list_for_each_entry(ref, &hdl->ctrl_refs, node) {
-               if (ref->ctrl->id < id)
-                       continue;
-               /* Don't add duplicates */
-               if (ref->ctrl->id == id) {
-                       kfree(new_ref);
-                       goto unlock;
-               }
-               list_add(&new_ref->node, ref->node.prev);
-               break;
-       }
-
-insert_in_hash:
-       /* Insert the control node in the hash */
-       new_ref->next = hdl->buckets[bucket];
-       hdl->buckets[bucket] = new_ref;
-
-unlock:
-       mutex_unlock(hdl->lock);
-       return 0;
-}
-
-/* Add a new control */
-static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
-                       const struct v4l2_ctrl_ops *ops,
-                       u32 id, const char *name, enum v4l2_ctrl_type type,
-                       s32 min, s32 max, u32 step, s32 def,
-                       u32 flags, const char * const *qmenu,
-                       const s64 *qmenu_int, void *priv)
-{
-       struct v4l2_ctrl *ctrl;
-       unsigned sz_extra = 0;
-
-       if (hdl->error)
-               return NULL;
-
-       /* Sanity checks */
-       if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
-           (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
-           (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
-           (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
-           (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) ||
-           (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
-               handler_set_err(hdl, -ERANGE);
-               return NULL;
-       }
-       if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
-               handler_set_err(hdl, -ERANGE);
-               return NULL;
-       }
-       if ((type == V4L2_CTRL_TYPE_INTEGER ||
-            type == V4L2_CTRL_TYPE_MENU ||
-            type == V4L2_CTRL_TYPE_INTEGER_MENU ||
-            type == V4L2_CTRL_TYPE_BOOLEAN) &&
-           (def < min || def > max)) {
-               handler_set_err(hdl, -ERANGE);
-               return NULL;
-       }
-       if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
-               handler_set_err(hdl, -ERANGE);
-               return NULL;
-       }
-
-       if (type == V4L2_CTRL_TYPE_BUTTON)
-               flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
-       else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
-               flags |= V4L2_CTRL_FLAG_READ_ONLY;
-       else if (type == V4L2_CTRL_TYPE_STRING)
-               sz_extra += 2 * (max + 1);
-
-       ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
-       if (ctrl == NULL) {
-               handler_set_err(hdl, -ENOMEM);
-               return NULL;
-       }
-
-       INIT_LIST_HEAD(&ctrl->node);
-       INIT_LIST_HEAD(&ctrl->ev_subs);
-       ctrl->handler = hdl;
-       ctrl->ops = ops;
-       ctrl->id = id;
-       ctrl->name = name;
-       ctrl->type = type;
-       ctrl->flags = flags;
-       ctrl->minimum = min;
-       ctrl->maximum = max;
-       ctrl->step = step;
-       if (type == V4L2_CTRL_TYPE_MENU)
-               ctrl->qmenu = qmenu;
-       else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)
-               ctrl->qmenu_int = qmenu_int;
-       ctrl->priv = priv;
-       ctrl->cur.val = ctrl->val = ctrl->default_value = def;
-
-       if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
-               ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
-               ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
-               if (ctrl->minimum)
-                       memset(ctrl->cur.string, ' ', ctrl->minimum);
-       }
-       if (handler_new_ref(hdl, ctrl)) {
-               kfree(ctrl);
-               return NULL;
-       }
-       mutex_lock(hdl->lock);
-       list_add_tail(&ctrl->node, &hdl->ctrls);
-       mutex_unlock(hdl->lock);
-       return ctrl;
-}
-
-struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
-                       const struct v4l2_ctrl_config *cfg, void *priv)
-{
-       bool is_menu;
-       struct v4l2_ctrl *ctrl;
-       const char *name = cfg->name;
-       const char * const *qmenu = cfg->qmenu;
-       const s64 *qmenu_int = cfg->qmenu_int;
-       enum v4l2_ctrl_type type = cfg->type;
-       u32 flags = cfg->flags;
-       s32 min = cfg->min;
-       s32 max = cfg->max;
-       u32 step = cfg->step;
-       s32 def = cfg->def;
-
-       if (name == NULL)
-               v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
-                                                               &def, &flags);
-
-       is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU ||
-                  cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU);
-       if (is_menu)
-               WARN_ON(step);
-       else
-               WARN_ON(cfg->menu_skip_mask);
-       if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL)
-               qmenu = v4l2_ctrl_get_menu(cfg->id);
-       else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU &&
-                qmenu_int == NULL) {
-               handler_set_err(hdl, -EINVAL);
-               return NULL;
-       }
-
-       ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
-                       type, min, max,
-                       is_menu ? cfg->menu_skip_mask : step,
-                       def, flags, qmenu, qmenu_int, priv);
-       if (ctrl)
-               ctrl->is_private = cfg->is_private;
-       return ctrl;
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_custom);
-
-/* Helper function for standard non-menu controls */
-struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
-                       const struct v4l2_ctrl_ops *ops,
-                       u32 id, s32 min, s32 max, u32 step, s32 def)
-{
-       const char *name;
-       enum v4l2_ctrl_type type;
-       u32 flags;
-
-       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
-       if (type == V4L2_CTRL_TYPE_MENU
-           || type == V4L2_CTRL_TYPE_INTEGER_MENU) {
-               handler_set_err(hdl, -EINVAL);
-               return NULL;
-       }
-       return v4l2_ctrl_new(hdl, ops, id, name, type,
-                            min, max, step, def, flags, NULL, NULL, NULL);
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_std);
-
-/* Helper function for standard menu controls */
-struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
-                       const struct v4l2_ctrl_ops *ops,
-                       u32 id, s32 max, s32 mask, s32 def)
-{
-       const char * const *qmenu = v4l2_ctrl_get_menu(id);
-       const char *name;
-       enum v4l2_ctrl_type type;
-       s32 min;
-       s32 step;
-       u32 flags;
-
-       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
-       if (type != V4L2_CTRL_TYPE_MENU) {
-               handler_set_err(hdl, -EINVAL);
-               return NULL;
-       }
-       return v4l2_ctrl_new(hdl, ops, id, name, type,
-                            0, max, mask, def, flags, qmenu, NULL, NULL);
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
-
-/* Helper function for standard integer menu controls */
-struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
-                       const struct v4l2_ctrl_ops *ops,
-                       u32 id, s32 max, s32 def, const s64 *qmenu_int)
-{
-       const char *name;
-       enum v4l2_ctrl_type type;
-       s32 min;
-       s32 step;
-       u32 flags;
-
-       v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
-       if (type != V4L2_CTRL_TYPE_INTEGER_MENU) {
-               handler_set_err(hdl, -EINVAL);
-               return NULL;
-       }
-       return v4l2_ctrl_new(hdl, ops, id, name, type,
-                            0, max, 0, def, flags, NULL, qmenu_int, NULL);
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
-
-/* Add a control from another handler to this handler */
-struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
-                                         struct v4l2_ctrl *ctrl)
-{
-       if (hdl == NULL || hdl->error)
-               return NULL;
-       if (ctrl == NULL) {
-               handler_set_err(hdl, -EINVAL);
-               return NULL;
-       }
-       if (ctrl->handler == hdl)
-               return ctrl;
-       return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
-}
-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_ref *ref;
-       int ret = 0;
-
-       /* Do nothing if either handler is NULL or if they are the same */
-       if (!hdl || !add || hdl == add)
-               return 0;
-       if (hdl->error)
-               return hdl->error;
-       mutex_lock(add->lock);
-       list_for_each_entry(ref, &add->ctrl_refs, node) {
-               struct v4l2_ctrl *ctrl = ref->ctrl;
-
-               /* Skip handler-private controls. */
-               if (ctrl->is_private)
-                       continue;
-               /* And control classes */
-               if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
-                       continue;
-               ret = handler_new_ref(hdl, ctrl);
-               if (ret)
-                       break;
-       }
-       mutex_unlock(add->lock);
-       return ret;
-}
-EXPORT_SYMBOL(v4l2_ctrl_add_handler);
-
-/* Cluster controls */
-void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
-{
-       bool has_volatiles = false;
-       int i;
-
-       /* The first control is the master control and it must not be NULL */
-       BUG_ON(ncontrols == 0 || controls[0] == NULL);
-
-       for (i = 0; i < ncontrols; i++) {
-               if (controls[i]) {
-                       controls[i]->cluster = controls;
-                       controls[i]->ncontrols = ncontrols;
-                       if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
-                               has_volatiles = true;
-               }
-       }
-       controls[0]->has_volatiles = has_volatiles;
-}
-EXPORT_SYMBOL(v4l2_ctrl_cluster);
-
-void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
-                           u8 manual_val, bool set_volatile)
-{
-       struct v4l2_ctrl *master = controls[0];
-       u32 flag = 0;
-       int i;
-
-       v4l2_ctrl_cluster(ncontrols, controls);
-       WARN_ON(ncontrols <= 1);
-       WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
-       WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
-       master->is_auto = true;
-       master->has_volatiles = set_volatile;
-       master->manual_mode_value = manual_val;
-       master->flags |= V4L2_CTRL_FLAG_UPDATE;
-
-       if (!is_cur_manual(master))
-               flag = V4L2_CTRL_FLAG_INACTIVE |
-                       (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
-
-       for (i = 1; i < ncontrols; i++)
-               if (controls[i])
-                       controls[i]->flags |= flag;
-}
-EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
-
-/* Activate/deactivate a control. */
-void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
-{
-       /* invert since the actual flag is called 'inactive' */
-       bool inactive = !active;
-       bool old;
-
-       if (ctrl == NULL)
-               return;
-
-       if (inactive)
-               /* set V4L2_CTRL_FLAG_INACTIVE */
-               old = test_and_set_bit(4, &ctrl->flags);
-       else
-               /* clear V4L2_CTRL_FLAG_INACTIVE */
-               old = test_and_clear_bit(4, &ctrl->flags);
-       if (old != inactive)
-               send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
-}
-EXPORT_SYMBOL(v4l2_ctrl_activate);
-
-/* Grab/ungrab a control.
-   Typically used when streaming starts and you want to grab controls,
-   preventing the user from changing them.
-
-   Just call this and the framework will block any attempts to change
-   these controls. */
-void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
-{
-       bool old;
-
-       if (ctrl == NULL)
-               return;
-
-       v4l2_ctrl_lock(ctrl);
-       if (grabbed)
-               /* set V4L2_CTRL_FLAG_GRABBED */
-               old = test_and_set_bit(1, &ctrl->flags);
-       else
-               /* clear V4L2_CTRL_FLAG_GRABBED */
-               old = test_and_clear_bit(1, &ctrl->flags);
-       if (old != grabbed)
-               send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
-       v4l2_ctrl_unlock(ctrl);
-}
-EXPORT_SYMBOL(v4l2_ctrl_grab);
-
-/* Log the control name and value */
-static void log_ctrl(const struct v4l2_ctrl *ctrl,
-                    const char *prefix, const char *colon)
-{
-       if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
-               return;
-       if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
-               return;
-
-       printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
-
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER:
-               printk(KERN_CONT "%d", ctrl->cur.val);
-               break;
-       case V4L2_CTRL_TYPE_BOOLEAN:
-               printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
-               break;
-       case V4L2_CTRL_TYPE_MENU:
-               printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER_MENU:
-               printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]);
-               break;
-       case V4L2_CTRL_TYPE_BITMASK:
-               printk(KERN_CONT "0x%08x", ctrl->cur.val);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               printk(KERN_CONT "%lld", ctrl->cur.val64);
-               break;
-       case V4L2_CTRL_TYPE_STRING:
-               printk(KERN_CONT "%s", ctrl->cur.string);
-               break;
-       default:
-               printk(KERN_CONT "unknown type %d", ctrl->type);
-               break;
-       }
-       if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
-                          V4L2_CTRL_FLAG_GRABBED |
-                          V4L2_CTRL_FLAG_VOLATILE)) {
-               if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
-                       printk(KERN_CONT " inactive");
-               if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
-                       printk(KERN_CONT " grabbed");
-               if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
-                       printk(KERN_CONT " volatile");
-       }
-       printk(KERN_CONT "\n");
-}
-
-/* Log all controls owned by the handler */
-void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
-                                 const char *prefix)
-{
-       struct v4l2_ctrl *ctrl;
-       const char *colon = "";
-       int len;
-
-       if (hdl == NULL)
-               return;
-       if (prefix == NULL)
-               prefix = "";
-       len = strlen(prefix);
-       if (len && prefix[len - 1] != ' ')
-               colon = ": ";
-       mutex_lock(hdl->lock);
-       list_for_each_entry(ctrl, &hdl->ctrls, node)
-               if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
-                       log_ctrl(ctrl, prefix, colon);
-       mutex_unlock(hdl->lock);
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
-
-/* Call s_ctrl for all controls owned by the handler */
-int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
-{
-       struct v4l2_ctrl *ctrl;
-       int ret = 0;
-
-       if (hdl == NULL)
-               return 0;
-       mutex_lock(hdl->lock);
-       list_for_each_entry(ctrl, &hdl->ctrls, node)
-               ctrl->done = false;
-
-       list_for_each_entry(ctrl, &hdl->ctrls, node) {
-               struct v4l2_ctrl *master = ctrl->cluster[0];
-               int i;
-
-               /* Skip if this control was already handled by a cluster. */
-               /* Skip button controls and read-only controls. */
-               if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
-                   (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
-                       continue;
-
-               for (i = 0; i < master->ncontrols; i++) {
-                       if (master->cluster[i]) {
-                               cur_to_new(master->cluster[i]);
-                               master->cluster[i]->is_new = 1;
-                               master->cluster[i]->done = true;
-                       }
-               }
-               ret = call_op(master, s_ctrl);
-               if (ret)
-                       break;
-       }
-       mutex_unlock(hdl->lock);
-       return ret;
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
-
-/* Implement VIDIOC_QUERYCTRL */
-int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
-{
-       u32 id = qc->id & V4L2_CTRL_ID_MASK;
-       struct v4l2_ctrl_ref *ref;
-       struct v4l2_ctrl *ctrl;
-
-       if (hdl == NULL)
-               return -EINVAL;
-
-       mutex_lock(hdl->lock);
-
-       /* Try to find it */
-       ref = find_ref(hdl, id);
-
-       if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
-               /* Find the next control with ID > qc->id */
-
-               /* Did we reach the end of the control list? */
-               if (id >= node2id(hdl->ctrl_refs.prev)) {
-                       ref = NULL; /* Yes, so there is no next control */
-               } else if (ref) {
-                       /* We found a control with the given ID, so just get
-                          the next one in the list. */
-                       ref = list_entry(ref->node.next, typeof(*ref), node);
-               } else {
-                       /* No control with the given ID exists, so start
-                          searching for the next largest ID. We know there
-                          is one, otherwise the first 'if' above would have
-                          been true. */
-                       list_for_each_entry(ref, &hdl->ctrl_refs, node)
-                               if (id < ref->ctrl->id)
-                                       break;
-               }
-       }
-       mutex_unlock(hdl->lock);
-       if (!ref)
-               return -EINVAL;
-
-       ctrl = ref->ctrl;
-       memset(qc, 0, sizeof(*qc));
-       if (id >= V4L2_CID_PRIVATE_BASE)
-               qc->id = id;
-       else
-               qc->id = ctrl->id;
-       strlcpy(qc->name, ctrl->name, sizeof(qc->name));
-       qc->minimum = ctrl->minimum;
-       qc->maximum = ctrl->maximum;
-       qc->default_value = ctrl->default_value;
-       if (ctrl->type == V4L2_CTRL_TYPE_MENU
-           || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
-               qc->step = 1;
-       else
-               qc->step = ctrl->step;
-       qc->flags = ctrl->flags;
-       qc->type = ctrl->type;
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_queryctrl);
-
-int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL)
-               return -EINVAL;
-       return v4l2_queryctrl(sd->ctrl_handler, qc);
-}
-EXPORT_SYMBOL(v4l2_subdev_queryctrl);
-
-/* Implement VIDIOC_QUERYMENU */
-int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
-{
-       struct v4l2_ctrl *ctrl;
-       u32 i = qm->index;
-
-       ctrl = v4l2_ctrl_find(hdl, qm->id);
-       if (!ctrl)
-               return -EINVAL;
-
-       qm->reserved = 0;
-       /* Sanity checks */
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_MENU:
-               if (ctrl->qmenu == NULL)
-                       return -EINVAL;
-               break;
-       case V4L2_CTRL_TYPE_INTEGER_MENU:
-               if (ctrl->qmenu_int == NULL)
-                       return -EINVAL;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       if (i < ctrl->minimum || i > ctrl->maximum)
-               return -EINVAL;
-
-       /* Use mask to see if this menu item should be skipped */
-       if (ctrl->menu_skip_mask & (1 << i))
-               return -EINVAL;
-       /* Empty menu items should also be skipped */
-       if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
-               if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
-                       return -EINVAL;
-               strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
-       } else {
-               qm->value = ctrl->qmenu_int[i];
-       }
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_querymenu);
-
-int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
-{
-       return v4l2_querymenu(sd->ctrl_handler, qm);
-}
-EXPORT_SYMBOL(v4l2_subdev_querymenu);
-
-
-
-/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
-
-   It is not a fully atomic operation, just best-effort only. After all, if
-   multiple controls have to be set through multiple i2c writes (for example)
-   then some initial writes may succeed while others fail. Thus leaving the
-   system in an inconsistent state. The question is how much effort you are
-   willing to spend on trying to make something atomic that really isn't.
-
-   From the point of view of an application the main requirement is that
-   when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
-   error should be returned without actually affecting any controls.
-
-   If all the values are correct, then it is acceptable to just give up
-   in case of low-level errors.
-
-   It is important though that the application can tell when only a partial
-   configuration was done. The way we do that is through the error_idx field
-   of struct v4l2_ext_controls: if that is equal to the count field then no
-   controls were affected. Otherwise all controls before that index were
-   successful in performing their 'get' or 'set' operation, the control at
-   the given index failed, and you don't know what happened with the controls
-   after the failed one. Since if they were part of a control cluster they
-   could have been successfully processed (if a cluster member was encountered
-   at index < error_idx), they could have failed (if a cluster member was at
-   error_idx), or they may not have been processed yet (if the first cluster
-   member appeared after error_idx).
-
-   It is all fairly theoretical, though. In practice all you can do is to
-   bail out. If error_idx == count, then it is an application bug. If
-   error_idx < count then it is only an application bug if the error code was
-   EBUSY. That usually means that something started streaming just when you
-   tried to set the controls. In all other cases it is a driver/hardware
-   problem and all you can do is to retry or bail out.
-
-   Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
-   never modifies controls the error_idx is just set to whatever control
-   has an invalid value.
- */
-
-/* Prepare for the extended g/s/try functions.
-   Find the controls in the control array and do some basic checks. */
-static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
-                            struct v4l2_ext_controls *cs,
-                            struct v4l2_ctrl_helper *helpers)
-{
-       struct v4l2_ctrl_helper *h;
-       bool have_clusters = false;
-       u32 i;
-
-       for (i = 0, h = helpers; i < cs->count; i++, h++) {
-               struct v4l2_ext_control *c = &cs->controls[i];
-               struct v4l2_ctrl_ref *ref;
-               struct v4l2_ctrl *ctrl;
-               u32 id = c->id & V4L2_CTRL_ID_MASK;
-
-               cs->error_idx = i;
-
-               if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
-                       return -EINVAL;
-
-               /* Old-style private controls are not allowed for
-                  extended controls */
-               if (id >= V4L2_CID_PRIVATE_BASE)
-                       return -EINVAL;
-               ref = find_ref_lock(hdl, id);
-               if (ref == NULL)
-                       return -EINVAL;
-               ctrl = ref->ctrl;
-               if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
-                       return -EINVAL;
-
-               if (ctrl->cluster[0]->ncontrols > 1)
-                       have_clusters = true;
-               if (ctrl->cluster[0] != ctrl)
-                       ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
-               /* Store the ref to the master control of the cluster */
-               h->mref = ref;
-               h->ctrl = ctrl;
-               /* Initially set next to 0, meaning that there is no other
-                  control in this helper array belonging to the same
-                  cluster */
-               h->next = 0;
-       }
-
-       /* We are done if there were no controls that belong to a multi-
-          control cluster. */
-       if (!have_clusters)
-               return 0;
-
-       /* The code below figures out in O(n) time which controls in the list
-          belong to the same cluster. */
-
-       /* This has to be done with the handler lock taken. */
-       mutex_lock(hdl->lock);
-
-       /* First zero the helper field in the master control references */
-       for (i = 0; i < cs->count; i++)
-               helpers[i].mref->helper = NULL;
-       for (i = 0, h = helpers; i < cs->count; i++, h++) {
-               struct v4l2_ctrl_ref *mref = h->mref;
-
-               /* If the mref->helper is set, then it points to an earlier
-                  helper that belongs to the same cluster. */
-               if (mref->helper) {
-                       /* Set the next field of mref->helper to the current
-                          index: this means that that earlier helper now
-                          points to the next helper in the same cluster. */
-                       mref->helper->next = i;
-                       /* mref should be set only for the first helper in the
-                          cluster, clear the others. */
-                       h->mref = NULL;
-               }
-               /* Point the mref helper to the current helper struct. */
-               mref->helper = h;
-       }
-       mutex_unlock(hdl->lock);
-       return 0;
-}
-
-/* Handles the corner case where cs->count == 0. It checks whether the
-   specified control class exists. If that class ID is 0, then it checks
-   whether there are any controls at all. */
-static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
-{
-       if (ctrl_class == 0)
-               return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
-       return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
-}
-
-
-
-/* Get extended controls. Allocates the helpers array if needed. */
-int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
-{
-       struct v4l2_ctrl_helper helper[4];
-       struct v4l2_ctrl_helper *helpers = helper;
-       int ret;
-       int i, j;
-
-       cs->error_idx = cs->count;
-       cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
-
-       if (hdl == NULL)
-               return -EINVAL;
-
-       if (cs->count == 0)
-               return class_check(hdl, cs->ctrl_class);
-
-       if (cs->count > ARRAY_SIZE(helper)) {
-               helpers = kmalloc_array(cs->count, sizeof(helper[0]),
-                                       GFP_KERNEL);
-               if (helpers == NULL)
-                       return -ENOMEM;
-       }
-
-       ret = prepare_ext_ctrls(hdl, cs, helpers);
-       cs->error_idx = cs->count;
-
-       for (i = 0; !ret && i < cs->count; i++)
-               if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
-                       ret = -EACCES;
-
-       for (i = 0; !ret && i < cs->count; i++) {
-               int (*ctrl_to_user)(struct v4l2_ext_control *c,
-                                   struct v4l2_ctrl *ctrl) = cur_to_user;
-               struct v4l2_ctrl *master;
-
-               if (helpers[i].mref == NULL)
-                       continue;
-
-               master = helpers[i].mref->ctrl;
-               cs->error_idx = i;
-
-               v4l2_ctrl_lock(master);
-
-               /* g_volatile_ctrl will update the new control values */
-               if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
-                       (master->has_volatiles && !is_cur_manual(master))) {
-                       for (j = 0; j < master->ncontrols; j++)
-                               cur_to_new(master->cluster[j]);
-                       ret = call_op(master, g_volatile_ctrl);
-                       ctrl_to_user = new_to_user;
-               }
-               /* If OK, then copy the current (for non-volatile controls)
-                  or the new (for volatile controls) control values to the
-                  caller */
-               if (!ret) {
-                       u32 idx = i;
-
-                       do {
-                               ret = ctrl_to_user(cs->controls + idx,
-                                                  helpers[idx].ctrl);
-                               idx = helpers[idx].next;
-                       } while (!ret && idx);
-               }
-               v4l2_ctrl_unlock(master);
-       }
-
-       if (cs->count > ARRAY_SIZE(helper))
-               kfree(helpers);
-       return ret;
-}
-EXPORT_SYMBOL(v4l2_g_ext_ctrls);
-
-int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
-{
-       return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
-}
-EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
-
-/* Helper function to get a single control */
-static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
-{
-       struct v4l2_ctrl *master = ctrl->cluster[0];
-       int ret = 0;
-       int i;
-
-       if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
-               return -EACCES;
-
-       v4l2_ctrl_lock(master);
-       /* g_volatile_ctrl will update the current control values */
-       if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
-               for (i = 0; i < master->ncontrols; i++)
-                       cur_to_new(master->cluster[i]);
-               ret = call_op(master, g_volatile_ctrl);
-               *val = ctrl->val;
-       } else {
-               *val = ctrl->cur.val;
-       }
-       v4l2_ctrl_unlock(master);
-       return ret;
-}
-
-int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
-{
-       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
-
-       if (ctrl == NULL || !type_is_int(ctrl))
-               return -EINVAL;
-       return get_ctrl(ctrl, &control->value);
-}
-EXPORT_SYMBOL(v4l2_g_ctrl);
-
-int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
-{
-       return v4l2_g_ctrl(sd->ctrl_handler, control);
-}
-EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
-
-s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
-{
-       s32 val = 0;
-
-       /* It's a driver bug if this happens. */
-       WARN_ON(!type_is_int(ctrl));
-       get_ctrl(ctrl, &val);
-       return val;
-}
-EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
-
-
-/* Core function that calls try/s_ctrl and ensures that the new value is
-   copied to the current value on a set.
-   Must be called with ctrl->handler->lock held. */
-static int try_or_set_cluster(struct v4l2_fh *fh,
-                             struct v4l2_ctrl *master, bool set)
-{
-       bool update_flag;
-       int ret;
-       int i;
-
-       /* Go through the cluster and either validate the new value or
-          (if no new value was set), copy the current value to the new
-          value, ensuring a consistent view for the control ops when
-          called. */
-       for (i = 0; i < master->ncontrols; i++) {
-               struct v4l2_ctrl *ctrl = master->cluster[i];
-
-               if (ctrl == NULL)
-                       continue;
-
-               if (!ctrl->is_new) {
-                       cur_to_new(ctrl);
-                       continue;
-               }
-               /* Check again: it may have changed since the
-                  previous check in try_or_set_ext_ctrls(). */
-               if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
-                       return -EBUSY;
-       }
-
-       ret = call_op(master, try_ctrl);
-
-       /* Don't set if there is no change */
-       if (ret || !set || !cluster_changed(master))
-               return ret;
-       ret = call_op(master, s_ctrl);
-       if (ret)
-               return ret;
-
-       /* If OK, then make the new values permanent. */
-       update_flag = is_cur_manual(master) != is_new_manual(master);
-       for (i = 0; i < master->ncontrols; i++)
-               new_to_cur(fh, master->cluster[i], update_flag && i > 0);
-       return 0;
-}
-
-/* Validate controls. */
-static int validate_ctrls(struct v4l2_ext_controls *cs,
-                         struct v4l2_ctrl_helper *helpers, bool set)
-{
-       unsigned i;
-       int ret = 0;
-
-       cs->error_idx = cs->count;
-       for (i = 0; i < cs->count; i++) {
-               struct v4l2_ctrl *ctrl = helpers[i].ctrl;
-
-               cs->error_idx = i;
-
-               if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
-                       return -EACCES;
-               /* This test is also done in try_set_control_cluster() which
-                  is called in atomic context, so that has the final say,
-                  but it makes sense to do an up-front check as well. Once
-                  an error occurs in try_set_control_cluster() some other
-                  controls may have been set already and we want to do a
-                  best-effort to avoid that. */
-               if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
-                       return -EBUSY;
-               ret = validate_new(ctrl, &cs->controls[i]);
-               if (ret)
-                       return ret;
-       }
-       return 0;
-}
-
-/* Obtain the current volatile values of an autocluster and mark them
-   as new. */
-static void update_from_auto_cluster(struct v4l2_ctrl *master)
-{
-       int i;
-
-       for (i = 0; i < master->ncontrols; i++)
-               cur_to_new(master->cluster[i]);
-       if (!call_op(master, g_volatile_ctrl))
-               for (i = 1; i < master->ncontrols; i++)
-                       if (master->cluster[i])
-                               master->cluster[i]->is_new = 1;
-}
-
-/* Try or try-and-set controls */
-static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
-                            struct v4l2_ext_controls *cs,
-                            bool set)
-{
-       struct v4l2_ctrl_helper helper[4];
-       struct v4l2_ctrl_helper *helpers = helper;
-       unsigned i, j;
-       int ret;
-
-       cs->error_idx = cs->count;
-       cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
-
-       if (hdl == NULL)
-               return -EINVAL;
-
-       if (cs->count == 0)
-               return class_check(hdl, cs->ctrl_class);
-
-       if (cs->count > ARRAY_SIZE(helper)) {
-               helpers = kmalloc_array(cs->count, sizeof(helper[0]),
-                                       GFP_KERNEL);
-               if (!helpers)
-                       return -ENOMEM;
-       }
-       ret = prepare_ext_ctrls(hdl, cs, helpers);
-       if (!ret)
-               ret = validate_ctrls(cs, helpers, set);
-       if (ret && set)
-               cs->error_idx = cs->count;
-       for (i = 0; !ret && i < cs->count; i++) {
-               struct v4l2_ctrl *master;
-               u32 idx = i;
-
-               if (helpers[i].mref == NULL)
-                       continue;
-
-               cs->error_idx = i;
-               master = helpers[i].mref->ctrl;
-               v4l2_ctrl_lock(master);
-
-               /* Reset the 'is_new' flags of the cluster */
-               for (j = 0; j < master->ncontrols; j++)
-                       if (master->cluster[j])
-                               master->cluster[j]->is_new = 0;
-
-               /* For volatile autoclusters that are currently in auto mode
-                  we need to discover if it will be set to manual mode.
-                  If so, then we have to copy the current volatile values
-                  first since those will become the new manual values (which
-                  may be overwritten by explicit new values from this set
-                  of controls). */
-               if (master->is_auto && master->has_volatiles &&
-                                               !is_cur_manual(master)) {
-                       /* Pick an initial non-manual value */
-                       s32 new_auto_val = master->manual_mode_value + 1;
-                       u32 tmp_idx = idx;
-
-                       do {
-                               /* Check if the auto control is part of the
-                                  list, and remember the new value. */
-                               if (helpers[tmp_idx].ctrl == master)
-                                       new_auto_val = cs->controls[tmp_idx].value;
-                               tmp_idx = helpers[tmp_idx].next;
-                       } while (tmp_idx);
-                       /* If the new value == the manual value, then copy
-                          the current volatile values. */
-                       if (new_auto_val == master->manual_mode_value)
-                               update_from_auto_cluster(master);
-               }
-
-               /* Copy the new caller-supplied control values.
-                  user_to_new() sets 'is_new' to 1. */
-               do {
-                       ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
-                       idx = helpers[idx].next;
-               } while (!ret && idx);
-
-               if (!ret)
-                       ret = try_or_set_cluster(fh, master, set);
-
-               /* Copy the new values back to userspace. */
-               if (!ret) {
-                       idx = i;
-                       do {
-                               ret = new_to_user(cs->controls + idx,
-                                               helpers[idx].ctrl);
-                               idx = helpers[idx].next;
-                       } while (!ret && idx);
-               }
-               v4l2_ctrl_unlock(master);
-       }
-
-       if (cs->count > ARRAY_SIZE(helper))
-               kfree(helpers);
-       return ret;
-}
-
-int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
-{
-       return try_set_ext_ctrls(NULL, hdl, cs, false);
-}
-EXPORT_SYMBOL(v4l2_try_ext_ctrls);
-
-int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
-                                       struct v4l2_ext_controls *cs)
-{
-       return try_set_ext_ctrls(fh, hdl, cs, true);
-}
-EXPORT_SYMBOL(v4l2_s_ext_ctrls);
-
-int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
-{
-       return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false);
-}
-EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
-
-int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
-{
-       return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true);
-}
-EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
-
-/* Helper function for VIDIOC_S_CTRL compatibility */
-static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
-{
-       struct v4l2_ctrl *master = ctrl->cluster[0];
-       int ret;
-       int i;
-
-       ret = validate_new_int(ctrl, val);
-       if (ret)
-               return ret;
-
-       v4l2_ctrl_lock(ctrl);
-
-       /* Reset the 'is_new' flags of the cluster */
-       for (i = 0; i < master->ncontrols; i++)
-               if (master->cluster[i])
-                       master->cluster[i]->is_new = 0;
-
-       /* For autoclusters with volatiles that are switched from auto to
-          manual mode we have to update the current volatile values since
-          those will become the initial manual values after such a switch. */
-       if (master->is_auto && master->has_volatiles && ctrl == master &&
-           !is_cur_manual(master) && *val == master->manual_mode_value)
-               update_from_auto_cluster(master);
-       ctrl->val = *val;
-       ctrl->is_new = 1;
-       ret = try_or_set_cluster(fh, master, true);
-       *val = ctrl->cur.val;
-       v4l2_ctrl_unlock(ctrl);
-       return ret;
-}
-
-int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
-                                       struct v4l2_control *control)
-{
-       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
-
-       if (ctrl == NULL || !type_is_int(ctrl))
-               return -EINVAL;
-
-       if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
-               return -EACCES;
-
-       return set_ctrl(fh, ctrl, &control->value);
-}
-EXPORT_SYMBOL(v4l2_s_ctrl);
-
-int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
-{
-       return v4l2_s_ctrl(NULL, sd->ctrl_handler, control);
-}
-EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
-
-int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
-{
-       /* It's a driver bug if this happens. */
-       WARN_ON(!type_is_int(ctrl));
-       return set_ctrl(NULL, ctrl, &val);
-}
-EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
-
-static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
-{
-       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
-
-       if (ctrl == NULL)
-               return -EINVAL;
-
-       v4l2_ctrl_lock(ctrl);
-       list_add_tail(&sev->node, &ctrl->ev_subs);
-       if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
-           (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
-               struct v4l2_event ev;
-               u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
-
-               if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
-                       changes |= V4L2_EVENT_CTRL_CH_VALUE;
-               fill_event(&ev, ctrl, changes);
-               /* Mark the queue as active, allowing this initial
-                  event to be accepted. */
-               sev->elems = elems;
-               v4l2_event_queue_fh(sev->fh, &ev);
-       }
-       v4l2_ctrl_unlock(ctrl);
-       return 0;
-}
-
-static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev)
-{
-       struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
-
-       v4l2_ctrl_lock(ctrl);
-       list_del(&sev->node);
-       v4l2_ctrl_unlock(ctrl);
-}
-
-void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new)
-{
-       u32 old_changes = old->u.ctrl.changes;
-
-       old->u.ctrl = new->u.ctrl;
-       old->u.ctrl.changes |= old_changes;
-}
-EXPORT_SYMBOL(v4l2_ctrl_replace);
-
-void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new)
-{
-       new->u.ctrl.changes |= old->u.ctrl.changes;
-}
-EXPORT_SYMBOL(v4l2_ctrl_merge);
-
-const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = {
-       .add = v4l2_ctrl_add_event,
-       .del = v4l2_ctrl_del_event,
-       .replace = v4l2_ctrl_replace,
-       .merge = v4l2_ctrl_merge,
-};
-EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops);
-
-int v4l2_ctrl_log_status(struct file *file, void *fh)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_fh *vfh = file->private_data;
-
-       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
-               v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
-                       vfd->v4l2_dev->name);
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_log_status);
-
-int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
-                               struct v4l2_event_subscription *sub)
-{
-       if (sub->type == V4L2_EVENT_CTRL)
-               return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
-       return -EINVAL;
-}
-EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
-
-unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
-{
-       struct v4l2_fh *fh = file->private_data;
-
-       if (v4l2_event_pending(fh))
-               return POLLPRI;
-       poll_wait(file, &fh->wait, wait);
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_poll);
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
deleted file mode 100644 (file)
index 71237f5..0000000
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- * Video capture interface for Linux version 2
- *
- *     A generic video device interface for the LINUX operating system
- *     using a set of device structures/vectors for low level operations.
- *
- *     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.
- *
- * Authors:    Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
- *              Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
- *
- * Fixes:      20000516  Claudio Matsuoka <claudio@conectiva.com>
- *             - Added procfs support
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/kmod.h>
-#include <linux/slab.h>
-#include <asm/uaccess.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-
-#define VIDEO_NUM_DEVICES      256
-#define VIDEO_NAME              "video4linux"
-
-/*
- *     sysfs stuff
- */
-
-static ssize_t show_index(struct device *cd,
-                        struct device_attribute *attr, char *buf)
-{
-       struct video_device *vdev = to_video_device(cd);
-
-       return sprintf(buf, "%i\n", vdev->index);
-}
-
-static ssize_t show_debug(struct device *cd,
-                        struct device_attribute *attr, char *buf)
-{
-       struct video_device *vdev = to_video_device(cd);
-
-       return sprintf(buf, "%i\n", vdev->debug);
-}
-
-static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
-                  const char *buf, size_t len)
-{
-       struct video_device *vdev = to_video_device(cd);
-       int res = 0;
-       u16 value;
-
-       res = kstrtou16(buf, 0, &value);
-       if (res)
-               return res;
-
-       vdev->debug = value;
-       return len;
-}
-
-static ssize_t show_name(struct device *cd,
-                        struct device_attribute *attr, char *buf)
-{
-       struct video_device *vdev = to_video_device(cd);
-
-       return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
-}
-
-static struct device_attribute video_device_attrs[] = {
-       __ATTR(name, S_IRUGO, show_name, NULL),
-       __ATTR(debug, 0644, show_debug, set_debug),
-       __ATTR(index, S_IRUGO, show_index, NULL),
-       __ATTR_NULL
-};
-
-/*
- *     Active devices
- */
-static struct video_device *video_device[VIDEO_NUM_DEVICES];
-static DEFINE_MUTEX(videodev_lock);
-static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
-
-/* Device node utility functions */
-
-/* Note: these utility functions all assume that vfl_type is in the range
-   [0, VFL_TYPE_MAX-1]. */
-
-#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
-/* Return the bitmap corresponding to vfl_type. */
-static inline unsigned long *devnode_bits(int vfl_type)
-{
-       /* Any types not assigned to fixed minor ranges must be mapped to
-          one single bitmap for the purposes of finding a free node number
-          since all those unassigned types use the same minor range. */
-       int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type;
-
-       return devnode_nums[idx];
-}
-#else
-/* Return the bitmap corresponding to vfl_type. */
-static inline unsigned long *devnode_bits(int vfl_type)
-{
-       return devnode_nums[vfl_type];
-}
-#endif
-
-/* Mark device node number vdev->num as used */
-static inline void devnode_set(struct video_device *vdev)
-{
-       set_bit(vdev->num, devnode_bits(vdev->vfl_type));
-}
-
-/* Mark device node number vdev->num as unused */
-static inline void devnode_clear(struct video_device *vdev)
-{
-       clear_bit(vdev->num, devnode_bits(vdev->vfl_type));
-}
-
-/* Try to find a free device node number in the range [from, to> */
-static inline int devnode_find(struct video_device *vdev, int from, int to)
-{
-       return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from);
-}
-
-struct video_device *video_device_alloc(void)
-{
-       return kzalloc(sizeof(struct video_device), GFP_KERNEL);
-}
-EXPORT_SYMBOL(video_device_alloc);
-
-void video_device_release(struct video_device *vdev)
-{
-       kfree(vdev);
-}
-EXPORT_SYMBOL(video_device_release);
-
-void video_device_release_empty(struct video_device *vdev)
-{
-       /* Do nothing */
-       /* Only valid when the video_device struct is a static. */
-}
-EXPORT_SYMBOL(video_device_release_empty);
-
-static inline void video_get(struct video_device *vdev)
-{
-       get_device(&vdev->dev);
-}
-
-static inline void video_put(struct video_device *vdev)
-{
-       put_device(&vdev->dev);
-}
-
-/* Called when the last user of the video device exits. */
-static void v4l2_device_release(struct device *cd)
-{
-       struct video_device *vdev = to_video_device(cd);
-       struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
-
-       mutex_lock(&videodev_lock);
-       if (WARN_ON(video_device[vdev->minor] != vdev)) {
-               /* should not happen */
-               mutex_unlock(&videodev_lock);
-               return;
-       }
-
-       /* Free up this device for reuse */
-       video_device[vdev->minor] = NULL;
-
-       /* Delete the cdev on this minor as well */
-       cdev_del(vdev->cdev);
-       /* Just in case some driver tries to access this from
-          the release() callback. */
-       vdev->cdev = NULL;
-
-       /* Mark device node number as free */
-       devnode_clear(vdev);
-
-       mutex_unlock(&videodev_lock);
-
-#if defined(CONFIG_MEDIA_CONTROLLER)
-       if (v4l2_dev && v4l2_dev->mdev &&
-           vdev->vfl_type != VFL_TYPE_SUBDEV)
-               media_device_unregister_entity(&vdev->entity);
-#endif
-
-       /* Do not call v4l2_device_put if there is no release callback set.
-        * Drivers that have no v4l2_device release callback might free the
-        * v4l2_dev instance in the video_device release callback below, so we
-        * must perform this check here.
-        *
-        * TODO: In the long run all drivers that use v4l2_device should use the
-        * v4l2_device release callback. This check will then be unnecessary.
-        */
-       if (v4l2_dev && v4l2_dev->release == NULL)
-               v4l2_dev = NULL;
-
-       /* Release video_device and perform other
-          cleanups as needed. */
-       vdev->release(vdev);
-
-       /* Decrease v4l2_device refcount */
-       if (v4l2_dev)
-               v4l2_device_put(v4l2_dev);
-}
-
-static struct class video_class = {
-       .name = VIDEO_NAME,
-       .dev_attrs = video_device_attrs,
-};
-
-struct video_device *video_devdata(struct file *file)
-{
-       return video_device[iminor(file->f_path.dentry->d_inode)];
-}
-EXPORT_SYMBOL(video_devdata);
-
-
-/* Priority handling */
-
-static inline bool prio_is_valid(enum v4l2_priority prio)
-{
-       return prio == V4L2_PRIORITY_BACKGROUND ||
-              prio == V4L2_PRIORITY_INTERACTIVE ||
-              prio == V4L2_PRIORITY_RECORD;
-}
-
-void v4l2_prio_init(struct v4l2_prio_state *global)
-{
-       memset(global, 0, sizeof(*global));
-}
-EXPORT_SYMBOL(v4l2_prio_init);
-
-int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local,
-                    enum v4l2_priority new)
-{
-       if (!prio_is_valid(new))
-               return -EINVAL;
-       if (*local == new)
-               return 0;
-
-       atomic_inc(&global->prios[new]);
-       if (prio_is_valid(*local))
-               atomic_dec(&global->prios[*local]);
-       *local = new;
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_prio_change);
-
-void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local)
-{
-       v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT);
-}
-EXPORT_SYMBOL(v4l2_prio_open);
-
-void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local)
-{
-       if (prio_is_valid(local))
-               atomic_dec(&global->prios[local]);
-}
-EXPORT_SYMBOL(v4l2_prio_close);
-
-enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global)
-{
-       if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0)
-               return V4L2_PRIORITY_RECORD;
-       if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0)
-               return V4L2_PRIORITY_INTERACTIVE;
-       if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0)
-               return V4L2_PRIORITY_BACKGROUND;
-       return V4L2_PRIORITY_UNSET;
-}
-EXPORT_SYMBOL(v4l2_prio_max);
-
-int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local)
-{
-       return (local < v4l2_prio_max(global)) ? -EBUSY : 0;
-}
-EXPORT_SYMBOL(v4l2_prio_check);
-
-
-static ssize_t v4l2_read(struct file *filp, char __user *buf,
-               size_t sz, loff_t *off)
-{
-       struct video_device *vdev = video_devdata(filp);
-       int ret = -ENODEV;
-
-       if (!vdev->fops->read)
-               return -EINVAL;
-       if (video_is_registered(vdev))
-               ret = vdev->fops->read(filp, buf, sz, off);
-       if (vdev->debug)
-               printk(KERN_DEBUG "%s: read: %zd (%d)\n",
-                       video_device_node_name(vdev), sz, ret);
-       return ret;
-}
-
-static ssize_t v4l2_write(struct file *filp, const char __user *buf,
-               size_t sz, loff_t *off)
-{
-       struct video_device *vdev = video_devdata(filp);
-       int ret = -ENODEV;
-
-       if (!vdev->fops->write)
-               return -EINVAL;
-       if (video_is_registered(vdev))
-               ret = vdev->fops->write(filp, buf, sz, off);
-       if (vdev->debug)
-               printk(KERN_DEBUG "%s: write: %zd (%d)\n",
-                       video_device_node_name(vdev), sz, ret);
-       return ret;
-}
-
-static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
-{
-       struct video_device *vdev = video_devdata(filp);
-       unsigned int res = POLLERR | POLLHUP;
-
-       if (!vdev->fops->poll)
-               return DEFAULT_POLLMASK;
-       if (video_is_registered(vdev))
-               res = vdev->fops->poll(filp, poll);
-       if (vdev->debug)
-               printk(KERN_DEBUG "%s: poll: %08x\n",
-                       video_device_node_name(vdev), res);
-       return res;
-}
-
-static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
-       struct video_device *vdev = video_devdata(filp);
-       int ret = -ENODEV;
-
-       if (vdev->fops->unlocked_ioctl) {
-               struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
-
-               if (lock && mutex_lock_interruptible(lock))
-                       return -ERESTARTSYS;
-               if (video_is_registered(vdev))
-                       ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
-               if (lock)
-                       mutex_unlock(lock);
-       } else if (vdev->fops->ioctl) {
-               /* This code path is a replacement for the BKL. It is a major
-                * hack but it will have to do for those drivers that are not
-                * yet converted to use unlocked_ioctl.
-                *
-                * There are two options: if the driver implements struct
-                * v4l2_device, then the lock defined there is used to
-                * serialize the ioctls. Otherwise the v4l2 core lock defined
-                * below is used. This lock is really bad since it serializes
-                * completely independent devices.
-                *
-                * Both variants suffer from the same problem: if the driver
-                * sleeps, then it blocks all ioctls since the lock is still
-                * held. This is very common for VIDIOC_DQBUF since that
-                * normally waits for a frame to arrive. As a result any other
-                * ioctl calls will proceed very, very slowly since each call
-                * will have to wait for the VIDIOC_QBUF to finish. Things that
-                * should take 0.01s may now take 10-20 seconds.
-                *
-                * The workaround is to *not* take the lock for VIDIOC_DQBUF.
-                * This actually works OK for videobuf-based drivers, since
-                * videobuf will take its own internal lock.
-                */
-               static DEFINE_MUTEX(v4l2_ioctl_mutex);
-               struct mutex *m = vdev->v4l2_dev ?
-                       &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;
-
-               if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
-                       return -ERESTARTSYS;
-               if (video_is_registered(vdev))
-                       ret = vdev->fops->ioctl(filp, cmd, arg);
-               if (cmd != VIDIOC_DQBUF)
-                       mutex_unlock(m);
-       } else
-               ret = -ENOTTY;
-
-       return ret;
-}
-
-#ifdef CONFIG_MMU
-#define v4l2_get_unmapped_area NULL
-#else
-static unsigned long v4l2_get_unmapped_area(struct file *filp,
-               unsigned long addr, unsigned long len, unsigned long pgoff,
-               unsigned long flags)
-{
-       struct video_device *vdev = video_devdata(filp);
-       int ret;
-
-       if (!vdev->fops->get_unmapped_area)
-               return -ENOSYS;
-       if (!video_is_registered(vdev))
-               return -ENODEV;
-       ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
-       if (vdev->debug)
-               printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
-                       video_device_node_name(vdev), ret);
-       return ret;
-}
-#endif
-
-static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
-{
-       struct video_device *vdev = video_devdata(filp);
-       int ret = -ENODEV;
-
-       if (!vdev->fops->mmap)
-               return -ENODEV;
-       if (video_is_registered(vdev))
-               ret = vdev->fops->mmap(filp, vm);
-       if (vdev->debug)
-               printk(KERN_DEBUG "%s: mmap (%d)\n",
-                       video_device_node_name(vdev), ret);
-       return ret;
-}
-
-/* Override for the open function */
-static int v4l2_open(struct inode *inode, struct file *filp)
-{
-       struct video_device *vdev;
-       int ret = 0;
-
-       /* Check if the video device is available */
-       mutex_lock(&videodev_lock);
-       vdev = video_devdata(filp);
-       /* return ENODEV if the video device has already been removed. */
-       if (vdev == NULL || !video_is_registered(vdev)) {
-               mutex_unlock(&videodev_lock);
-               return -ENODEV;
-       }
-       /* and increase the device refcount */
-       video_get(vdev);
-       mutex_unlock(&videodev_lock);
-       if (vdev->fops->open) {
-               if (video_is_registered(vdev))
-                       ret = vdev->fops->open(filp);
-               else
-                       ret = -ENODEV;
-       }
-
-       if (vdev->debug)
-               printk(KERN_DEBUG "%s: open (%d)\n",
-                       video_device_node_name(vdev), ret);
-       /* decrease the refcount in case of an error */
-       if (ret)
-               video_put(vdev);
-       return ret;
-}
-
-/* Override for the release function */
-static int v4l2_release(struct inode *inode, struct file *filp)
-{
-       struct video_device *vdev = video_devdata(filp);
-       int ret = 0;
-
-       if (vdev->fops->release)
-               ret = vdev->fops->release(filp);
-       if (vdev->debug)
-               printk(KERN_DEBUG "%s: release\n",
-                       video_device_node_name(vdev));
-
-       /* decrease the refcount unconditionally since the release()
-          return value is ignored. */
-       video_put(vdev);
-       return ret;
-}
-
-static const struct file_operations v4l2_fops = {
-       .owner = THIS_MODULE,
-       .read = v4l2_read,
-       .write = v4l2_write,
-       .open = v4l2_open,
-       .get_unmapped_area = v4l2_get_unmapped_area,
-       .mmap = v4l2_mmap,
-       .unlocked_ioctl = v4l2_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = v4l2_compat_ioctl32,
-#endif
-       .release = v4l2_release,
-       .poll = v4l2_poll,
-       .llseek = no_llseek,
-};
-
-/**
- * get_index - assign stream index number based on parent device
- * @vdev: video_device to assign index number to, vdev->parent 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.
- *
- * This means that we can always obtain a free stream index number since
- * the worst case scenario is that there are VIDEO_NUM_DEVICES - 1 slots in
- * use of the video_device array.
- *
- * Returns a free index number.
- */
-static int get_index(struct video_device *vdev)
-{
-       /* This can be static since this function is called with the global
-          videodev_lock held. */
-       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) {
-                       set_bit(video_device[i]->index, used);
-               }
-       }
-
-       return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
-}
-
-#define SET_VALID_IOCTL(ops, cmd, op)                  \
-       if (ops->op)                                    \
-               set_bit(_IOC_NR(cmd), valid_ioctls)
-
-/* This determines which ioctls are actually implemented in the driver.
-   It's a one-time thing which simplifies video_ioctl2 as it can just do
-   a bit test.
-
-   Note that drivers can override this by setting bits to 1 in
-   vdev->valid_ioctls. If an ioctl is marked as 1 when this function is
-   called, then that ioctl will actually be marked as unimplemented.
-
-   It does that by first setting up the local valid_ioctls bitmap, and
-   at the end do a:
-
-   vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls)
- */
-static void determine_valid_ioctls(struct video_device *vdev)
-{
-       DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
-       const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
-
-       bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE);
-
-       SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
-       if (ops->vidioc_g_priority ||
-                       test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
-               set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
-       if (ops->vidioc_s_priority ||
-                       test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
-               set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
-       if (ops->vidioc_enum_fmt_vid_cap ||
-           ops->vidioc_enum_fmt_vid_out ||
-           ops->vidioc_enum_fmt_vid_cap_mplane ||
-           ops->vidioc_enum_fmt_vid_out_mplane ||
-           ops->vidioc_enum_fmt_vid_overlay ||
-           ops->vidioc_enum_fmt_type_private)
-               set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
-       if (ops->vidioc_g_fmt_vid_cap ||
-           ops->vidioc_g_fmt_vid_out ||
-           ops->vidioc_g_fmt_vid_cap_mplane ||
-           ops->vidioc_g_fmt_vid_out_mplane ||
-           ops->vidioc_g_fmt_vid_overlay ||
-           ops->vidioc_g_fmt_vbi_cap ||
-           ops->vidioc_g_fmt_vid_out_overlay ||
-           ops->vidioc_g_fmt_vbi_out ||
-           ops->vidioc_g_fmt_sliced_vbi_cap ||
-           ops->vidioc_g_fmt_sliced_vbi_out ||
-           ops->vidioc_g_fmt_type_private)
-               set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
-       if (ops->vidioc_s_fmt_vid_cap ||
-           ops->vidioc_s_fmt_vid_out ||
-           ops->vidioc_s_fmt_vid_cap_mplane ||
-           ops->vidioc_s_fmt_vid_out_mplane ||
-           ops->vidioc_s_fmt_vid_overlay ||
-           ops->vidioc_s_fmt_vbi_cap ||
-           ops->vidioc_s_fmt_vid_out_overlay ||
-           ops->vidioc_s_fmt_vbi_out ||
-           ops->vidioc_s_fmt_sliced_vbi_cap ||
-           ops->vidioc_s_fmt_sliced_vbi_out ||
-           ops->vidioc_s_fmt_type_private)
-               set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
-       if (ops->vidioc_try_fmt_vid_cap ||
-           ops->vidioc_try_fmt_vid_out ||
-           ops->vidioc_try_fmt_vid_cap_mplane ||
-           ops->vidioc_try_fmt_vid_out_mplane ||
-           ops->vidioc_try_fmt_vid_overlay ||
-           ops->vidioc_try_fmt_vbi_cap ||
-           ops->vidioc_try_fmt_vid_out_overlay ||
-           ops->vidioc_try_fmt_vbi_out ||
-           ops->vidioc_try_fmt_sliced_vbi_cap ||
-           ops->vidioc_try_fmt_sliced_vbi_out ||
-           ops->vidioc_try_fmt_type_private)
-               set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
-       SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
-       SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
-       SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
-       SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
-       SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
-       SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
-       SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
-       SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
-       SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
-       if (vdev->tvnorms)
-               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_QUERYSTD, vidioc_querystd);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
-       SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
-       SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
-       SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
-       SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
-       /* Note: the control handler can also be passed through the filehandle,
-          and that can't be tested here. If the bit for these control ioctls
-          is set, then the ioctl is valid. But if it is 0, then it can still
-          be valid if the filehandle passed the control handler. */
-       if (vdev->ctrl_handler || ops->vidioc_queryctrl)
-               set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
-       if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls)
-               set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
-       if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls)
-               set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
-       if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
-               set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
-       if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
-               set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
-       if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls)
-               set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
-       if (vdev->ctrl_handler || ops->vidioc_querymenu)
-               set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio);
-       SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio);
-       SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout);
-       SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout);
-       SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
-       SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator);
-       SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator);
-       if (ops->vidioc_g_crop || ops->vidioc_g_selection)
-               set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
-       if (ops->vidioc_s_crop || ops->vidioc_s_selection)
-               set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
-       SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
-       SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
-       if (ops->vidioc_cropcap || ops->vidioc_g_selection)
-               set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
-       SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp);
-       SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp);
-       SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index);
-       SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd);
-       SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd);
-       SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd);
-       SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
-       if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
-                                       (ops->vidioc_g_std || vdev->tvnorms)))
-               set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
-       SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
-       SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
-       SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner);
-       SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
-       SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
-       SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
-       SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register);
-       SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register);
-#endif
-       SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
-       SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets);
-       SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset);
-       SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset);
-       SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset);
-       SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
-       SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
-       SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings);
-       SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings);
-       SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap);
-       /* yes, really vidioc_subscribe_event */
-       SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
-       SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
-       SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
-       SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
-       SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
-       if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
-               set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
-       bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
-                       BASE_VIDIOC_PRIVATE);
-}
-
-/**
- *     __video_register_device - register video4linux devices
- *     @vdev: video device structure we want to register
- *     @type: type of device to register
- *     @nr:   which device node number (0 == /dev/video0, 1 == /dev/video1, ...
- *             -1 == first free)
- *     @warn_if_nr_in_use: warn if the desired device node number
- *            was already in use and another number was chosen instead.
- *     @owner: module that owns the video device node
- *
- *     The registration code assigns minor numbers and device node numbers
- *     based on the requested type and registers the new device node with
- *     the kernel.
- *
- *     This function assumes that struct video_device was zeroed when it
- *     was allocated and does not contain any stale date.
- *
- *     An error is returned if no free minor or device node number could be
- *     found, or if the registration of the device node failed.
- *
- *     Zero is returned on success.
- *
- *     Valid types are
- *
- *     %VFL_TYPE_GRABBER - A frame grabber
- *
- *     %VFL_TYPE_VBI - Vertical blank data (undecoded)
- *
- *     %VFL_TYPE_RADIO - A radio card
- *
- *     %VFL_TYPE_SUBDEV - A subdevice
- */
-int __video_register_device(struct video_device *vdev, int type, int nr,
-               int warn_if_nr_in_use, struct module *owner)
-{
-       int i = 0;
-       int ret;
-       int minor_offset = 0;
-       int minor_cnt = VIDEO_NUM_DEVICES;
-       const char *name_base;
-
-       /* A minor value of -1 marks this video device as never
-          having been registered */
-       vdev->minor = -1;
-
-       /* the release callback MUST be present */
-       if (WARN_ON(!vdev->release))
-               return -EINVAL;
-
-       /* v4l2_fh support */
-       spin_lock_init(&vdev->fh_lock);
-       INIT_LIST_HEAD(&vdev->fh_list);
-
-       /* Part 1: check device type */
-       switch (type) {
-       case VFL_TYPE_GRABBER:
-               name_base = "video";
-               break;
-       case VFL_TYPE_VBI:
-               name_base = "vbi";
-               break;
-       case VFL_TYPE_RADIO:
-               name_base = "radio";
-               break;
-       case VFL_TYPE_SUBDEV:
-               name_base = "v4l-subdev";
-               break;
-       default:
-               printk(KERN_ERR "%s called with unknown type: %d\n",
-                      __func__, type);
-               return -EINVAL;
-       }
-
-       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;
-       }
-
-       /* Part 2: find a free minor, device node number and device index. */
-#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
-       /* Keep the ranges for the first four types for historical
-        * reasons.
-        * Newer devices (not yet in place) should use the range
-        * of 128-191 and just pick the first free minor there
-        * (new style). */
-       switch (type) {
-       case VFL_TYPE_GRABBER:
-               minor_offset = 0;
-               minor_cnt = 64;
-               break;
-       case VFL_TYPE_RADIO:
-               minor_offset = 64;
-               minor_cnt = 64;
-               break;
-       case VFL_TYPE_VBI:
-               minor_offset = 224;
-               minor_cnt = 32;
-               break;
-       default:
-               minor_offset = 128;
-               minor_cnt = 64;
-               break;
-       }
-#endif
-
-       /* Pick a device node number */
-       mutex_lock(&videodev_lock);
-       nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
-       if (nr == minor_cnt)
-               nr = devnode_find(vdev, 0, minor_cnt);
-       if (nr == minor_cnt) {
-               printk(KERN_ERR "could not get a free device node number\n");
-               mutex_unlock(&videodev_lock);
-               return -ENFILE;
-       }
-#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
-       /* 1-on-1 mapping of device node number to minor number */
-       i = nr;
-#else
-       /* The device node number and minor numbers are independent, so
-          we just find the first free minor number. */
-       for (i = 0; i < VIDEO_NUM_DEVICES; i++)
-               if (video_device[i] == NULL)
-                       break;
-       if (i == VIDEO_NUM_DEVICES) {
-               mutex_unlock(&videodev_lock);
-               printk(KERN_ERR "could not get a free minor\n");
-               return -ENFILE;
-       }
-#endif
-       vdev->minor = i + minor_offset;
-       vdev->num = nr;
-       devnode_set(vdev);
-
-       /* Should not happen since we thought this minor was free */
-       WARN_ON(video_device[vdev->minor] != NULL);
-       vdev->index = get_index(vdev);
-       mutex_unlock(&videodev_lock);
-
-       if (vdev->ioctl_ops)
-               determine_valid_ioctls(vdev);
-
-       /* Part 3: Initialize the character device */
-       vdev->cdev = cdev_alloc();
-       if (vdev->cdev == NULL) {
-               ret = -ENOMEM;
-               goto cleanup;
-       }
-       vdev->cdev->ops = &v4l2_fops;
-       vdev->cdev->owner = owner;
-       ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
-       if (ret < 0) {
-               printk(KERN_ERR "%s: cdev_add failed\n", __func__);
-               kfree(vdev->cdev);
-               vdev->cdev = NULL;
-               goto cleanup;
-       }
-
-       /* 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;
-       dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
-       ret = device_register(&vdev->dev);
-       if (ret < 0) {
-               printk(KERN_ERR "%s: device_register failed\n", __func__);
-               goto cleanup;
-       }
-       /* Register the release callback that will be called when the last
-          reference to the device goes away. */
-       vdev->dev.release = v4l2_device_release;
-
-       if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
-               printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
-                       name_base, nr, video_device_node_name(vdev));
-
-       /* Increase v4l2_device refcount */
-       if (vdev->v4l2_dev)
-               v4l2_device_get(vdev->v4l2_dev);
-
-#if defined(CONFIG_MEDIA_CONTROLLER)
-       /* Part 5: Register the entity. */
-       if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
-           vdev->vfl_type != VFL_TYPE_SUBDEV) {
-               vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
-               vdev->entity.name = vdev->name;
-               vdev->entity.info.v4l.major = VIDEO_MAJOR;
-               vdev->entity.info.v4l.minor = vdev->minor;
-               ret = media_device_register_entity(vdev->v4l2_dev->mdev,
-                       &vdev->entity);
-               if (ret < 0)
-                       printk(KERN_WARNING
-                              "%s: media_device_register_entity failed\n",
-                              __func__);
-       }
-#endif
-       /* Part 6: Activate this minor. The char device can now be used. */
-       set_bit(V4L2_FL_REGISTERED, &vdev->flags);
-       mutex_lock(&videodev_lock);
-       video_device[vdev->minor] = vdev;
-       mutex_unlock(&videodev_lock);
-
-       return 0;
-
-cleanup:
-       mutex_lock(&videodev_lock);
-       if (vdev->cdev)
-               cdev_del(vdev->cdev);
-       devnode_clear(vdev);
-       mutex_unlock(&videodev_lock);
-       /* Mark this video device as never having been registered. */
-       vdev->minor = -1;
-       return ret;
-}
-EXPORT_SYMBOL(__video_register_device);
-
-/**
- *     video_unregister_device - unregister a video4linux device
- *     @vdev: the device to unregister
- *
- *     This unregisters the passed device. Future open calls will
- *     be met with errors.
- */
-void video_unregister_device(struct video_device *vdev)
-{
-       /* Check if vdev was ever registered at all */
-       if (!vdev || !video_is_registered(vdev))
-               return;
-
-       mutex_lock(&videodev_lock);
-       /* This must be in a critical section to prevent a race with v4l2_open.
-        * Once this bit has been cleared video_get may never be called again.
-        */
-       clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
-       mutex_unlock(&videodev_lock);
-       device_unregister(&vdev->dev);
-}
-EXPORT_SYMBOL(video_unregister_device);
-
-/*
- *     Initialise video for linux
- */
-static int __init videodev_init(void)
-{
-       dev_t dev = MKDEV(VIDEO_MAJOR, 0);
-       int ret;
-
-       printk(KERN_INFO "Linux video capture interface: v2.00\n");
-       ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
-       if (ret < 0) {
-               printk(KERN_WARNING "videodev: unable to get major %d\n",
-                               VIDEO_MAJOR);
-               return ret;
-       }
-
-       ret = class_register(&video_class);
-       if (ret < 0) {
-               unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
-               printk(KERN_WARNING "video_dev: class_register failed\n");
-               return -EIO;
-       }
-
-       return 0;
-}
-
-static void __exit videodev_exit(void)
-{
-       dev_t dev = MKDEV(VIDEO_MAJOR, 0);
-
-       class_unregister(&video_class);
-       unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
-}
-
-subsys_initcall(videodev_init);
-module_exit(videodev_exit)
-
-MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
deleted file mode 100644 (file)
index 1f203b8..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
-    V4L2 device support.
-
-    Copyright (C) 2008  Hans Verkuil <hverkuil@xs4all.nl>
-
-    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 <linux/types.h>
-#include <linux/ioctl.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#if defined(CONFIG_SPI)
-#include <linux/spi/spi.h>
-#endif
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-
-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);
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
deleted file mode 100644 (file)
index ef2a33c..0000000
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * v4l2-event.c
- *
- * V4L2 events.
- *
- * Copyright (C) 2009--2010 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
- *
- * 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.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
-{
-       idx += sev->first;
-       return idx >= sev->elems ? idx - sev->elems : idx;
-}
-
-static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
-{
-       struct v4l2_kevent *kev;
-       unsigned long flags;
-
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
-       if (list_empty(&fh->available)) {
-               spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-               return -ENOENT;
-       }
-
-       WARN_ON(fh->navailable == 0);
-
-       kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
-       list_del(&kev->list);
-       fh->navailable--;
-
-       kev->event.pending = fh->navailable;
-       *event = kev->event;
-       kev->sev->first = sev_pos(kev->sev, 1);
-       kev->sev->in_use--;
-
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
-       return 0;
-}
-
-int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
-                      int nonblocking)
-{
-       int ret;
-
-       if (nonblocking)
-               return __v4l2_event_dequeue(fh, event);
-
-       /* Release the vdev lock while waiting */
-       if (fh->vdev->lock)
-               mutex_unlock(fh->vdev->lock);
-
-       do {
-               ret = wait_event_interruptible(fh->wait,
-                                              fh->navailable != 0);
-               if (ret < 0)
-                       break;
-
-               ret = __v4l2_event_dequeue(fh, event);
-       } while (ret == -ENOENT);
-
-       if (fh->vdev->lock)
-               mutex_lock(fh->vdev->lock);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
-
-/* Caller must hold fh->vdev->fh_lock! */
-static struct v4l2_subscribed_event *v4l2_event_subscribed(
-               struct v4l2_fh *fh, u32 type, u32 id)
-{
-       struct v4l2_subscribed_event *sev;
-
-       assert_spin_locked(&fh->vdev->fh_lock);
-
-       list_for_each_entry(sev, &fh->subscribed, list)
-               if (sev->type == type && sev->id == id)
-                       return sev;
-
-       return NULL;
-}
-
-static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
-               const struct timespec *ts)
-{
-       struct v4l2_subscribed_event *sev;
-       struct v4l2_kevent *kev;
-       bool copy_payload = true;
-
-       /* Are we subscribed? */
-       sev = v4l2_event_subscribed(fh, ev->type, ev->id);
-       if (sev == NULL)
-               return;
-
-       /*
-        * If the event has been added to the fh->subscribed list, but its
-        * add op has not completed yet elems will be 0, treat this as
-        * not being subscribed.
-        */
-       if (!sev->elems)
-               return;
-
-       /* Increase event sequence number on fh. */
-       fh->sequence++;
-
-       /* Do we have any free events? */
-       if (sev->in_use == sev->elems) {
-               /* no, remove the oldest one */
-               kev = sev->events + sev_pos(sev, 0);
-               list_del(&kev->list);
-               sev->in_use--;
-               sev->first = sev_pos(sev, 1);
-               fh->navailable--;
-               if (sev->elems == 1) {
-                       if (sev->ops && sev->ops->replace) {
-                               sev->ops->replace(&kev->event, ev);
-                               copy_payload = false;
-                       }
-               } else if (sev->ops && sev->ops->merge) {
-                       struct v4l2_kevent *second_oldest =
-                               sev->events + sev_pos(sev, 0);
-                       sev->ops->merge(&kev->event, &second_oldest->event);
-               }
-       }
-
-       /* Take one and fill it. */
-       kev = sev->events + sev_pos(sev, sev->in_use);
-       kev->event.type = ev->type;
-       if (copy_payload)
-               kev->event.u = ev->u;
-       kev->event.id = ev->id;
-       kev->event.timestamp = *ts;
-       kev->event.sequence = fh->sequence;
-       sev->in_use++;
-       list_add_tail(&kev->list, &fh->available);
-
-       fh->navailable++;
-
-       wake_up_all(&fh->wait);
-}
-
-void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
-{
-       struct v4l2_fh *fh;
-       unsigned long flags;
-       struct timespec timestamp;
-
-       ktime_get_ts(&timestamp);
-
-       spin_lock_irqsave(&vdev->fh_lock, flags);
-
-       list_for_each_entry(fh, &vdev->fh_list, list)
-               __v4l2_event_queue_fh(fh, ev, &timestamp);
-
-       spin_unlock_irqrestore(&vdev->fh_lock, flags);
-}
-EXPORT_SYMBOL_GPL(v4l2_event_queue);
-
-void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
-{
-       unsigned long flags;
-       struct timespec timestamp;
-
-       ktime_get_ts(&timestamp);
-
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-       __v4l2_event_queue_fh(fh, ev, &timestamp);
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-}
-EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
-
-int v4l2_event_pending(struct v4l2_fh *fh)
-{
-       return fh->navailable;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_pending);
-
-int v4l2_event_subscribe(struct v4l2_fh *fh,
-                        struct v4l2_event_subscription *sub, unsigned elems,
-                        const struct v4l2_subscribed_event_ops *ops)
-{
-       struct v4l2_subscribed_event *sev, *found_ev;
-       unsigned long flags;
-       unsigned i;
-
-       if (sub->type == V4L2_EVENT_ALL)
-               return -EINVAL;
-
-       if (elems < 1)
-               elems = 1;
-
-       sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
-       if (!sev)
-               return -ENOMEM;
-       for (i = 0; i < elems; i++)
-               sev->events[i].sev = sev;
-       sev->type = sub->type;
-       sev->id = sub->id;
-       sev->flags = sub->flags;
-       sev->fh = fh;
-       sev->ops = ops;
-
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-       found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
-       if (!found_ev)
-               list_add(&sev->list, &fh->subscribed);
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
-       if (found_ev) {
-               kfree(sev);
-               return 0; /* Already listening */
-       }
-
-       if (sev->ops && sev->ops->add) {
-               int ret = sev->ops->add(sev, elems);
-               if (ret) {
-                       sev->ops = NULL;
-                       v4l2_event_unsubscribe(fh, sub);
-                       return ret;
-               }
-       }
-
-       /* Mark as ready for use */
-       sev->elems = elems;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
-
-void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
-{
-       struct v4l2_event_subscription sub;
-       struct v4l2_subscribed_event *sev;
-       unsigned long flags;
-
-       do {
-               sev = NULL;
-
-               spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-               if (!list_empty(&fh->subscribed)) {
-                       sev = list_first_entry(&fh->subscribed,
-                                       struct v4l2_subscribed_event, list);
-                       sub.type = sev->type;
-                       sub.id = sev->id;
-               }
-               spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-               if (sev)
-                       v4l2_event_unsubscribe(fh, &sub);
-       } while (sev);
-}
-EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
-
-int v4l2_event_unsubscribe(struct v4l2_fh *fh,
-                          struct v4l2_event_subscription *sub)
-{
-       struct v4l2_subscribed_event *sev;
-       unsigned long flags;
-       int i;
-
-       if (sub->type == V4L2_EVENT_ALL) {
-               v4l2_event_unsubscribe_all(fh);
-               return 0;
-       }
-
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
-       sev = v4l2_event_subscribed(fh, sub->type, sub->id);
-       if (sev != NULL) {
-               /* Remove any pending events for this subscription */
-               for (i = 0; i < sev->in_use; i++) {
-                       list_del(&sev->events[sev_pos(sev, i)].list);
-                       fh->navailable--;
-               }
-               list_del(&sev->list);
-       }
-
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
-       if (sev && sev->ops && sev->ops->del)
-               sev->ops->del(sev);
-
-       kfree(sev);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
deleted file mode 100644 (file)
index 9e3fc04..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * v4l2-fh.c
- *
- * V4L2 file handles.
- *
- * Copyright (C) 2009--2010 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
- *
- * 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.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/bitops.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ioctl.h>
-
-void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
-{
-       fh->vdev = vdev;
-       /* Inherit from video_device. May be overridden by the driver. */
-       fh->ctrl_handler = vdev->ctrl_handler;
-       INIT_LIST_HEAD(&fh->list);
-       set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
-       fh->prio = V4L2_PRIORITY_UNSET;
-       init_waitqueue_head(&fh->wait);
-       INIT_LIST_HEAD(&fh->available);
-       INIT_LIST_HEAD(&fh->subscribed);
-       fh->sequence = -1;
-}
-EXPORT_SYMBOL_GPL(v4l2_fh_init);
-
-void v4l2_fh_add(struct v4l2_fh *fh)
-{
-       unsigned long flags;
-
-       if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags))
-               v4l2_prio_open(fh->vdev->prio, &fh->prio);
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-       list_add(&fh->list, &fh->vdev->fh_list);
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-}
-EXPORT_SYMBOL_GPL(v4l2_fh_add);
-
-int v4l2_fh_open(struct file *filp)
-{
-       struct video_device *vdev = video_devdata(filp);
-       struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
-
-       filp->private_data = fh;
-       if (fh == NULL)
-               return -ENOMEM;
-       v4l2_fh_init(fh, vdev);
-       v4l2_fh_add(fh);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_fh_open);
-
-void v4l2_fh_del(struct v4l2_fh *fh)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-       list_del_init(&fh->list);
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-       if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags))
-               v4l2_prio_close(fh->vdev->prio, fh->prio);
-}
-EXPORT_SYMBOL_GPL(v4l2_fh_del);
-
-void v4l2_fh_exit(struct v4l2_fh *fh)
-{
-       if (fh->vdev == NULL)
-               return;
-       v4l2_event_unsubscribe_all(fh);
-       fh->vdev = NULL;
-}
-EXPORT_SYMBOL_GPL(v4l2_fh_exit);
-
-int v4l2_fh_release(struct file *filp)
-{
-       struct v4l2_fh *fh = filp->private_data;
-
-       if (fh) {
-               v4l2_fh_del(fh);
-               v4l2_fh_exit(fh);
-               kfree(fh);
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_fh_release);
-
-int v4l2_fh_is_singular(struct v4l2_fh *fh)
-{
-       unsigned long flags;
-       int is_singular;
-
-       if (fh == NULL || fh->vdev == NULL)
-               return 0;
-       spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-       is_singular = list_is_singular(&fh->list);
-       spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-       return is_singular;
-}
-EXPORT_SYMBOL_GPL(v4l2_fh_is_singular);
diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c
deleted file mode 100644 (file)
index f447349..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * drivers/media/video/v4l2-int-device.c
- *
- * V4L2 internal ioctl interface.
- *
- * Copyright (C) 2007 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * 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.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/sort.h>
-#include <linux/string.h>
-#include <linux/module.h>
-
-#include <media/v4l2-int-device.h>
-
-static DEFINE_MUTEX(mutex);
-static LIST_HEAD(int_list);
-
-void v4l2_int_device_try_attach_all(void)
-{
-       struct v4l2_int_device *m, *s;
-
-       list_for_each_entry(m, &int_list, head) {
-               if (m->type != v4l2_int_type_master)
-                       continue;
-
-               list_for_each_entry(s, &int_list, head) {
-                       if (s->type != v4l2_int_type_slave)
-                               continue;
-
-                       /* Slave is connected? */
-                       if (s->u.slave->master)
-                               continue;
-
-                       /* Slave wants to attach to master? */
-                       if (s->u.slave->attach_to[0] != 0
-                           && strncmp(m->name, s->u.slave->attach_to,
-                                      V4L2NAMESIZE))
-                               continue;
-
-                       if (!try_module_get(m->module))
-                               continue;
-
-                       s->u.slave->master = m;
-                       if (m->u.master->attach(s)) {
-                               s->u.slave->master = NULL;
-                               module_put(m->module);
-                               continue;
-                       }
-               }
-       }
-}
-EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);
-
-static int ioctl_sort_cmp(const void *a, const void *b)
-{
-       const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b;
-
-       if (d1->num > d2->num)
-               return 1;
-
-       if (d1->num < d2->num)
-               return -1;
-
-       return 0;
-}
-
-int v4l2_int_device_register(struct v4l2_int_device *d)
-{
-       if (d->type == v4l2_int_type_slave)
-               sort(d->u.slave->ioctls, d->u.slave->num_ioctls,
-                    sizeof(struct v4l2_int_ioctl_desc),
-                    &ioctl_sort_cmp, NULL);
-       mutex_lock(&mutex);
-       list_add(&d->head, &int_list);
-       v4l2_int_device_try_attach_all();
-       mutex_unlock(&mutex);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_int_device_register);
-
-void v4l2_int_device_unregister(struct v4l2_int_device *d)
-{
-       mutex_lock(&mutex);
-       list_del(&d->head);
-       if (d->type == v4l2_int_type_slave
-           && d->u.slave->master != NULL) {
-               d->u.slave->master->u.master->detach(d);
-               module_put(d->u.slave->master->module);
-               d->u.slave->master = NULL;
-       }
-       mutex_unlock(&mutex);
-}
-EXPORT_SYMBOL_GPL(v4l2_int_device_unregister);
-
-/* Adapted from search_extable in extable.c. */
-static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,
-                                      v4l2_int_ioctl_func *no_such_ioctl)
-{
-       const struct v4l2_int_ioctl_desc *first = slave->ioctls;
-       const struct v4l2_int_ioctl_desc *last =
-               first + slave->num_ioctls - 1;
-
-       while (first <= last) {
-               const struct v4l2_int_ioctl_desc *mid;
-
-               mid = (last - first) / 2 + first;
-
-               if (mid->num < cmd)
-                       first = mid + 1;
-               else if (mid->num > cmd)
-                       last = mid - 1;
-               else
-                       return mid->func;
-       }
-
-       return no_such_ioctl;
-}
-
-static int no_such_ioctl_0(struct v4l2_int_device *d)
-{
-       return -ENOIOCTLCMD;
-}
-
-int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd)
-{
-       return ((v4l2_int_ioctl_func_0 *)
-               find_ioctl(d->u.slave, cmd,
-                          (v4l2_int_ioctl_func *)no_such_ioctl_0))(d);
-}
-EXPORT_SYMBOL_GPL(v4l2_int_ioctl_0);
-
-static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg)
-{
-       return -ENOIOCTLCMD;
-}
-
-int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)
-{
-       return ((v4l2_int_ioctl_func_1 *)
-               find_ioctl(d->u.slave, cmd,
-                          (v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);
-}
-EXPORT_SYMBOL_GPL(v4l2_int_ioctl_1);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
deleted file mode 100644 (file)
index c3b7b5f..0000000
+++ /dev/null
@@ -1,2324 +0,0 @@
-/*
- * Video capture interface for Linux version 2
- *
- * A generic framework to process V4L2 ioctl commands.
- *
- * 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.
- *
- * Authors:    Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
- *              Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/version.h>
-
-#include <linux/videodev2.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#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
- * not including, the specified field is cleared. */
-#define CLEAR_AFTER_FIELD(p, field) \
-       memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
-       0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
-
-struct std_descr {
-       v4l2_std_id std;
-       const char *descr;
-};
-
-static const struct std_descr standards[] = {
-       { V4L2_STD_NTSC,        "NTSC"      },
-       { V4L2_STD_NTSC_M,      "NTSC-M"    },
-       { V4L2_STD_NTSC_M_JP,   "NTSC-M-JP" },
-       { V4L2_STD_NTSC_M_KR,   "NTSC-M-KR" },
-       { V4L2_STD_NTSC_443,    "NTSC-443"  },
-       { V4L2_STD_PAL,         "PAL"       },
-       { V4L2_STD_PAL_BG,      "PAL-BG"    },
-       { V4L2_STD_PAL_B,       "PAL-B"     },
-       { V4L2_STD_PAL_B1,      "PAL-B1"    },
-       { V4L2_STD_PAL_G,       "PAL-G"     },
-       { V4L2_STD_PAL_H,       "PAL-H"     },
-       { V4L2_STD_PAL_I,       "PAL-I"     },
-       { V4L2_STD_PAL_DK,      "PAL-DK"    },
-       { V4L2_STD_PAL_D,       "PAL-D"     },
-       { V4L2_STD_PAL_D1,      "PAL-D1"    },
-       { V4L2_STD_PAL_K,       "PAL-K"     },
-       { V4L2_STD_PAL_M,       "PAL-M"     },
-       { V4L2_STD_PAL_N,       "PAL-N"     },
-       { V4L2_STD_PAL_Nc,      "PAL-Nc"    },
-       { V4L2_STD_PAL_60,      "PAL-60"    },
-       { V4L2_STD_SECAM,       "SECAM"     },
-       { V4L2_STD_SECAM_B,     "SECAM-B"   },
-       { V4L2_STD_SECAM_G,     "SECAM-G"   },
-       { V4L2_STD_SECAM_H,     "SECAM-H"   },
-       { V4L2_STD_SECAM_DK,    "SECAM-DK"  },
-       { V4L2_STD_SECAM_D,     "SECAM-D"   },
-       { V4L2_STD_SECAM_K,     "SECAM-K"   },
-       { V4L2_STD_SECAM_K1,    "SECAM-K1"  },
-       { V4L2_STD_SECAM_L,     "SECAM-L"   },
-       { V4L2_STD_SECAM_LC,    "SECAM-Lc"  },
-       { 0,                    "Unknown"   }
-};
-
-/* video4linux standard ID conversion to standard name
- */
-const char *v4l2_norm_to_name(v4l2_std_id id)
-{
-       u32 myid = id;
-       int i;
-
-       /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle
-          64 bit comparations. So, on that architecture, with some gcc
-          variants, compilation fails. Currently, the max value is 30bit wide.
-        */
-       BUG_ON(myid != id);
-
-       for (i = 0; standards[i].std; i++)
-               if (myid == standards[i].std)
-                       break;
-       return standards[i].descr;
-}
-EXPORT_SYMBOL(v4l2_norm_to_name);
-
-/* Returns frame period for the given standard */
-void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod)
-{
-       if (id & V4L2_STD_525_60) {
-               frameperiod->numerator = 1001;
-               frameperiod->denominator = 30000;
-       } else {
-               frameperiod->numerator = 1;
-               frameperiod->denominator = 25;
-       }
-}
-EXPORT_SYMBOL(v4l2_video_std_frame_period);
-
-/* Fill in the fields of a v4l2_standard structure according to the
-   'id' and 'transmission' parameters.  Returns negative on error.  */
-int v4l2_video_std_construct(struct v4l2_standard *vs,
-                            int id, const char *name)
-{
-       vs->id = id;
-       v4l2_video_std_frame_period(id, &vs->frameperiod);
-       vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625;
-       strlcpy(vs->name, name, sizeof(vs->name));
-       return 0;
-}
-EXPORT_SYMBOL(v4l2_video_std_construct);
-
-/* ----------------------------------------------------------------- */
-/* some arrays for pretty-printing debug messages of enum types      */
-
-const char *v4l2_field_names[] = {
-       [V4L2_FIELD_ANY]        = "any",
-       [V4L2_FIELD_NONE]       = "none",
-       [V4L2_FIELD_TOP]        = "top",
-       [V4L2_FIELD_BOTTOM]     = "bottom",
-       [V4L2_FIELD_INTERLACED] = "interlaced",
-       [V4L2_FIELD_SEQ_TB]     = "seq-tb",
-       [V4L2_FIELD_SEQ_BT]     = "seq-bt",
-       [V4L2_FIELD_ALTERNATE]  = "alternate",
-       [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
-       [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
-};
-EXPORT_SYMBOL(v4l2_field_names);
-
-const char *v4l2_type_names[] = {
-       [V4L2_BUF_TYPE_VIDEO_CAPTURE]      = "vid-cap",
-       [V4L2_BUF_TYPE_VIDEO_OVERLAY]      = "vid-overlay",
-       [V4L2_BUF_TYPE_VIDEO_OUTPUT]       = "vid-out",
-       [V4L2_BUF_TYPE_VBI_CAPTURE]        = "vbi-cap",
-       [V4L2_BUF_TYPE_VBI_OUTPUT]         = "vbi-out",
-       [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
-       [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "sliced-vbi-out",
-       [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay",
-       [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane",
-       [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane",
-};
-EXPORT_SYMBOL(v4l2_type_names);
-
-static const char *v4l2_memory_names[] = {
-       [V4L2_MEMORY_MMAP]    = "mmap",
-       [V4L2_MEMORY_USERPTR] = "userptr",
-       [V4L2_MEMORY_OVERLAY] = "overlay",
-};
-
-#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \
-                          arr[a] : "unknown")
-
-/* ------------------------------------------------------------------ */
-/* debug help functions                                               */
-
-static void v4l_print_querycap(const void *arg, bool write_only)
-{
-       const struct v4l2_capability *p = arg;
-
-       pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
-               "capabilities=0x%08x, device_caps=0x%08x\n",
-               p->driver, p->card, p->bus_info,
-               p->version, p->capabilities, p->device_caps);
-}
-
-static void v4l_print_enuminput(const void *arg, bool write_only)
-{
-       const struct v4l2_input *p = arg;
-
-       pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
-               "std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
-               p->index, p->name, p->type, p->audioset, p->tuner,
-               (unsigned long long)p->std, p->status, p->capabilities);
-}
-
-static void v4l_print_enumoutput(const void *arg, bool write_only)
-{
-       const struct v4l2_output *p = arg;
-
-       pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
-               "modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
-               p->index, p->name, p->type, p->audioset, p->modulator,
-               (unsigned long long)p->std, p->capabilities);
-}
-
-static void v4l_print_audio(const void *arg, bool write_only)
-{
-       const struct v4l2_audio *p = arg;
-
-       if (write_only)
-               pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
-       else
-               pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
-                       p->index, p->name, p->capability, p->mode);
-}
-
-static void v4l_print_audioout(const void *arg, bool write_only)
-{
-       const struct v4l2_audioout *p = arg;
-
-       if (write_only)
-               pr_cont("index=%u\n", p->index);
-       else
-               pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
-                       p->index, p->name, p->capability, p->mode);
-}
-
-static void v4l_print_fmtdesc(const void *arg, bool write_only)
-{
-       const struct v4l2_fmtdesc *p = arg;
-
-       pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
-               p->index, prt_names(p->type, v4l2_type_names),
-               p->flags, (p->pixelformat & 0xff),
-               (p->pixelformat >>  8) & 0xff,
-               (p->pixelformat >> 16) & 0xff,
-               (p->pixelformat >> 24) & 0xff,
-               p->description);
-}
-
-static void v4l_print_format(const void *arg, bool write_only)
-{
-       const struct v4l2_format *p = arg;
-       const struct v4l2_pix_format *pix;
-       const struct v4l2_pix_format_mplane *mp;
-       const struct v4l2_vbi_format *vbi;
-       const struct v4l2_sliced_vbi_format *sliced;
-       const struct v4l2_window *win;
-       const struct v4l2_clip *clip;
-       unsigned i;
-
-       pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
-       switch (p->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               pix = &p->fmt.pix;
-               pr_cont(", width=%u, height=%u, "
-                       "pixelformat=%c%c%c%c, field=%s, "
-                       "bytesperline=%u sizeimage=%u, colorspace=%d\n",
-                       pix->width, pix->height,
-                       (pix->pixelformat & 0xff),
-                       (pix->pixelformat >>  8) & 0xff,
-                       (pix->pixelformat >> 16) & 0xff,
-                       (pix->pixelformat >> 24) & 0xff,
-                       prt_names(pix->field, v4l2_field_names),
-                       pix->bytesperline, pix->sizeimage,
-                       pix->colorspace);
-               break;
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               mp = &p->fmt.pix_mp;
-               pr_cont(", width=%u, height=%u, "
-                       "format=%c%c%c%c, field=%s, "
-                       "colorspace=%d, num_planes=%u\n",
-                       mp->width, mp->height,
-                       (mp->pixelformat & 0xff),
-                       (mp->pixelformat >>  8) & 0xff,
-                       (mp->pixelformat >> 16) & 0xff,
-                       (mp->pixelformat >> 24) & 0xff,
-                       prt_names(mp->field, v4l2_field_names),
-                       mp->colorspace, mp->num_planes);
-               for (i = 0; i < mp->num_planes; i++)
-                       printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
-                                       mp->plane_fmt[i].bytesperline,
-                                       mp->plane_fmt[i].sizeimage);
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               win = &p->fmt.win;
-               pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, "
-                       "chromakey=0x%08x, bitmap=%p, "
-                       "global_alpha=0x%02x\n",
-                       win->w.width, win->w.height,
-                       win->w.left, win->w.top,
-                       prt_names(win->field, v4l2_field_names),
-                       win->chromakey, win->bitmap, win->global_alpha);
-               clip = win->clips;
-               for (i = 0; i < win->clipcount; i++) {
-                       printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n",
-                                       i, clip->c.width, clip->c.height,
-                                       clip->c.left, clip->c.top);
-                       clip = clip->next;
-               }
-               break;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               vbi = &p->fmt.vbi;
-               pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, "
-                       "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n",
-                       vbi->sampling_rate, vbi->offset,
-                       vbi->samples_per_line,
-                       (vbi->sample_format & 0xff),
-                       (vbi->sample_format >>  8) & 0xff,
-                       (vbi->sample_format >> 16) & 0xff,
-                       (vbi->sample_format >> 24) & 0xff,
-                       vbi->start[0], vbi->start[1],
-                       vbi->count[0], vbi->count[1]);
-               break;
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               sliced = &p->fmt.sliced;
-               pr_cont(", service_set=0x%08x, io_size=%d\n",
-                               sliced->service_set, sliced->io_size);
-               for (i = 0; i < 24; i++)
-                       printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
-                               sliced->service_lines[0][i],
-                               sliced->service_lines[1][i]);
-               break;
-       case V4L2_BUF_TYPE_PRIVATE:
-               pr_cont("\n");
-               break;
-       }
-}
-
-static void v4l_print_framebuffer(const void *arg, bool write_only)
-{
-       const struct v4l2_framebuffer *p = arg;
-
-       pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, "
-               "height=%u, pixelformat=%c%c%c%c, "
-               "bytesperline=%u sizeimage=%u, colorspace=%d\n",
-                       p->capability, p->flags, p->base,
-                       p->fmt.width, p->fmt.height,
-                       (p->fmt.pixelformat & 0xff),
-                       (p->fmt.pixelformat >>  8) & 0xff,
-                       (p->fmt.pixelformat >> 16) & 0xff,
-                       (p->fmt.pixelformat >> 24) & 0xff,
-                       p->fmt.bytesperline, p->fmt.sizeimage,
-                       p->fmt.colorspace);
-}
-
-static void v4l_print_buftype(const void *arg, bool write_only)
-{
-       pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names));
-}
-
-static void v4l_print_modulator(const void *arg, bool write_only)
-{
-       const struct v4l2_modulator *p = arg;
-
-       if (write_only)
-               pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
-       else
-               pr_cont("index=%u, name=%s, capability=0x%x, "
-                       "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
-                       p->index, p->name, p->capability,
-                       p->rangelow, p->rangehigh, p->txsubchans);
-}
-
-static void v4l_print_tuner(const void *arg, bool write_only)
-{
-       const struct v4l2_tuner *p = arg;
-
-       if (write_only)
-               pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
-       else
-               pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
-                       "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
-                       "rxsubchans=0x%x, audmode=%u\n",
-                       p->index, p->name, p->type,
-                       p->capability, p->rangelow,
-                       p->rangehigh, p->signal, p->afc,
-                       p->rxsubchans, p->audmode);
-}
-
-static void v4l_print_frequency(const void *arg, bool write_only)
-{
-       const struct v4l2_frequency *p = arg;
-
-       pr_cont("tuner=%u, type=%u, frequency=%u\n",
-                               p->tuner, p->type, p->frequency);
-}
-
-static void v4l_print_standard(const void *arg, bool write_only)
-{
-       const struct v4l2_standard *p = arg;
-
-       pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
-               "framelines=%u\n", p->index,
-               (unsigned long long)p->id, p->name,
-               p->frameperiod.numerator,
-               p->frameperiod.denominator,
-               p->framelines);
-}
-
-static void v4l_print_std(const void *arg, bool write_only)
-{
-       pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg);
-}
-
-static void v4l_print_hw_freq_seek(const void *arg, bool write_only)
-{
-       const struct v4l2_hw_freq_seek *p = arg;
-
-       pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
-               p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
-}
-
-static void v4l_print_requestbuffers(const void *arg, bool write_only)
-{
-       const struct v4l2_requestbuffers *p = arg;
-
-       pr_cont("count=%d, type=%s, memory=%s\n",
-               p->count,
-               prt_names(p->type, v4l2_type_names),
-               prt_names(p->memory, v4l2_memory_names));
-}
-
-static void v4l_print_buffer(const void *arg, bool write_only)
-{
-       const struct v4l2_buffer *p = arg;
-       const struct v4l2_timecode *tc = &p->timecode;
-       const struct v4l2_plane *plane;
-       int i;
-
-       pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, "
-               "flags=0x%08x, field=%s, sequence=%d, memory=%s",
-                       p->timestamp.tv_sec / 3600,
-                       (int)(p->timestamp.tv_sec / 60) % 60,
-                       (int)(p->timestamp.tv_sec % 60),
-                       (long)p->timestamp.tv_usec,
-                       p->index,
-                       prt_names(p->type, v4l2_type_names),
-                       p->flags, prt_names(p->field, v4l2_field_names),
-                       p->sequence, prt_names(p->memory, v4l2_memory_names));
-
-       if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
-               pr_cont("\n");
-               for (i = 0; i < p->length; ++i) {
-                       plane = &p->m.planes[i];
-                       printk(KERN_DEBUG
-                               "plane %d: bytesused=%d, data_offset=0x%08x "
-                               "offset/userptr=0x%lx, length=%d\n",
-                               i, plane->bytesused, plane->data_offset,
-                               plane->m.userptr, plane->length);
-               }
-       } else {
-               pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n",
-                       p->bytesused, p->m.userptr, p->length);
-       }
-
-       printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, "
-               "flags=0x%08x, frames=%d, userbits=0x%08x\n",
-                       tc->hours, tc->minutes, tc->seconds,
-                       tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
-}
-
-static void v4l_print_create_buffers(const void *arg, bool write_only)
-{
-       const struct v4l2_create_buffers *p = arg;
-
-       pr_cont("index=%d, count=%d, memory=%s, ",
-                       p->index, p->count,
-                       prt_names(p->memory, v4l2_memory_names));
-       v4l_print_format(&p->format, write_only);
-}
-
-static void v4l_print_streamparm(const void *arg, bool write_only)
-{
-       const struct v4l2_streamparm *p = arg;
-
-       pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
-
-       if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-           p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
-               const struct v4l2_captureparm *c = &p->parm.capture;
-
-               pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, "
-                       "extendedmode=%d, readbuffers=%d\n",
-                       c->capability, c->capturemode,
-                       c->timeperframe.numerator, c->timeperframe.denominator,
-                       c->extendedmode, c->readbuffers);
-       } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
-                  p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-               const struct v4l2_outputparm *c = &p->parm.output;
-
-               pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, "
-                       "extendedmode=%d, writebuffers=%d\n",
-                       c->capability, c->outputmode,
-                       c->timeperframe.numerator, c->timeperframe.denominator,
-                       c->extendedmode, c->writebuffers);
-       }
-}
-
-static void v4l_print_queryctrl(const void *arg, bool write_only)
-{
-       const struct v4l2_queryctrl *p = arg;
-
-       pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
-               "step=%d, default=%d, flags=0x%08x\n",
-                       p->id, p->type, p->name,
-                       p->minimum, p->maximum,
-                       p->step, p->default_value, p->flags);
-}
-
-static void v4l_print_querymenu(const void *arg, bool write_only)
-{
-       const struct v4l2_querymenu *p = arg;
-
-       pr_cont("id=0x%x, index=%d\n", p->id, p->index);
-}
-
-static void v4l_print_control(const void *arg, bool write_only)
-{
-       const struct v4l2_control *p = arg;
-
-       pr_cont("id=0x%x, value=%d\n", p->id, p->value);
-}
-
-static void v4l_print_ext_controls(const void *arg, bool write_only)
-{
-       const struct v4l2_ext_controls *p = arg;
-       int i;
-
-       pr_cont("class=0x%x, count=%d, error_idx=%d",
-                       p->ctrl_class, p->count, p->error_idx);
-       for (i = 0; i < p->count; i++) {
-               if (p->controls[i].size)
-                       pr_cont(", id/val=0x%x/0x%x",
-                               p->controls[i].id, p->controls[i].value);
-               else
-                       pr_cont(", id/size=0x%x/%u",
-                               p->controls[i].id, p->controls[i].size);
-       }
-       pr_cont("\n");
-}
-
-static void v4l_print_cropcap(const void *arg, bool write_only)
-{
-       const struct v4l2_cropcap *p = arg;
-
-       pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
-               "defrect wxh=%dx%d, x,y=%d,%d\n, "
-               "pixelaspect %d/%d\n",
-               prt_names(p->type, v4l2_type_names),
-               p->bounds.width, p->bounds.height,
-               p->bounds.left, p->bounds.top,
-               p->defrect.width, p->defrect.height,
-               p->defrect.left, p->defrect.top,
-               p->pixelaspect.numerator, p->pixelaspect.denominator);
-}
-
-static void v4l_print_crop(const void *arg, bool write_only)
-{
-       const struct v4l2_crop *p = arg;
-
-       pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
-               prt_names(p->type, v4l2_type_names),
-               p->c.width, p->c.height,
-               p->c.left, p->c.top);
-}
-
-static void v4l_print_selection(const void *arg, bool write_only)
-{
-       const struct v4l2_selection *p = arg;
-
-       pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
-               prt_names(p->type, v4l2_type_names),
-               p->target, p->flags,
-               p->r.width, p->r.height, p->r.left, p->r.top);
-}
-
-static void v4l_print_jpegcompression(const void *arg, bool write_only)
-{
-       const struct v4l2_jpegcompression *p = arg;
-
-       pr_cont("quality=%d, APPn=%d, APP_len=%d, "
-               "COM_len=%d, jpeg_markers=0x%x\n",
-               p->quality, p->APPn, p->APP_len,
-               p->COM_len, p->jpeg_markers);
-}
-
-static void v4l_print_enc_idx(const void *arg, bool write_only)
-{
-       const struct v4l2_enc_idx *p = arg;
-
-       pr_cont("entries=%d, entries_cap=%d\n",
-                       p->entries, p->entries_cap);
-}
-
-static void v4l_print_encoder_cmd(const void *arg, bool write_only)
-{
-       const struct v4l2_encoder_cmd *p = arg;
-
-       pr_cont("cmd=%d, flags=0x%x\n",
-                       p->cmd, p->flags);
-}
-
-static void v4l_print_decoder_cmd(const void *arg, bool write_only)
-{
-       const struct v4l2_decoder_cmd *p = arg;
-
-       pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags);
-
-       if (p->cmd == V4L2_DEC_CMD_START)
-               pr_info("speed=%d, format=%u\n",
-                               p->start.speed, p->start.format);
-       else if (p->cmd == V4L2_DEC_CMD_STOP)
-               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, ", 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_register(const void *arg, bool write_only)
-{
-       const struct v4l2_dbg_register *p = arg;
-
-       pr_cont("type=%u, ", p->match.type);
-       if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
-               pr_cont("name=%s, ", p->match.name);
-       else
-               pr_cont("addr=%u, ", p->match.addr);
-       pr_cont("reg=0x%llx, val=0x%llx\n",
-                       p->reg, p->val);
-}
-
-static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
-{
-       const struct v4l2_dv_enum_preset *p = arg;
-
-       pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
-                       p->index, p->preset, p->name, p->width, p->height);
-}
-
-static void v4l_print_dv_preset(const void *arg, bool write_only)
-{
-       const struct v4l2_dv_preset *p = arg;
-
-       pr_cont("preset=%u\n", p->preset);
-}
-
-static void v4l_print_dv_timings(const void *arg, bool write_only)
-{
-       const struct v4l2_dv_timings *p = arg;
-
-       switch (p->type) {
-       case V4L2_DV_BT_656_1120:
-               pr_cont("type=bt-656/1120, interlaced=%u, "
-                       "pixelclock=%llu, "
-                       "width=%u, height=%u, polarities=0x%x, "
-                       "hfrontporch=%u, hsync=%u, "
-                       "hbackporch=%u, vfrontporch=%u, "
-                       "vsync=%u, vbackporch=%u, "
-                       "il_vfrontporch=%u, il_vsync=%u, "
-                       "il_vbackporch=%u, standards=0x%x, flags=0x%x\n",
-                               p->bt.interlaced, p->bt.pixelclock,
-                               p->bt.width, p->bt.height,
-                               p->bt.polarities, p->bt.hfrontporch,
-                               p->bt.hsync, p->bt.hbackporch,
-                               p->bt.vfrontporch, p->bt.vsync,
-                               p->bt.vbackporch, p->bt.il_vfrontporch,
-                               p->bt.il_vsync, p->bt.il_vbackporch,
-                               p->bt.standards, p->bt.flags);
-               break;
-       default:
-               pr_cont("type=%d\n", p->type);
-               break;
-       }
-}
-
-static void v4l_print_enum_dv_timings(const void *arg, bool write_only)
-{
-       const struct v4l2_enum_dv_timings *p = arg;
-
-       pr_cont("index=%u, ", p->index);
-       v4l_print_dv_timings(&p->timings, write_only);
-}
-
-static void v4l_print_dv_timings_cap(const void *arg, bool write_only)
-{
-       const struct v4l2_dv_timings_cap *p = arg;
-
-       switch (p->type) {
-       case V4L2_DV_BT_656_1120:
-               pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, "
-                       "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n",
-                       p->bt.min_width, p->bt.max_width,
-                       p->bt.min_height, p->bt.max_height,
-                       p->bt.min_pixelclock, p->bt.max_pixelclock,
-                       p->bt.standards, p->bt.capabilities);
-               break;
-       default:
-               pr_cont("type=%u\n", p->type);
-               break;
-       }
-}
-
-static void v4l_print_frmsizeenum(const void *arg, bool write_only)
-{
-       const struct v4l2_frmsizeenum *p = arg;
-
-       pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u",
-                       p->index,
-                       (p->pixel_format & 0xff),
-                       (p->pixel_format >>  8) & 0xff,
-                       (p->pixel_format >> 16) & 0xff,
-                       (p->pixel_format >> 24) & 0xff,
-                       p->type);
-       switch (p->type) {
-       case V4L2_FRMSIZE_TYPE_DISCRETE:
-               pr_cont(" wxh=%ux%u\n",
-                       p->discrete.width, p->discrete.height);
-               break;
-       case V4L2_FRMSIZE_TYPE_STEPWISE:
-               pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n",
-                               p->stepwise.min_width,  p->stepwise.min_height,
-                               p->stepwise.step_width, p->stepwise.step_height,
-                               p->stepwise.max_width,  p->stepwise.max_height);
-               break;
-       case V4L2_FRMSIZE_TYPE_CONTINUOUS:
-               /* fall through */
-       default:
-               pr_cont("\n");
-               break;
-       }
-}
-
-static void v4l_print_frmivalenum(const void *arg, bool write_only)
-{
-       const struct v4l2_frmivalenum *p = arg;
-
-       pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u",
-                       p->index,
-                       (p->pixel_format & 0xff),
-                       (p->pixel_format >>  8) & 0xff,
-                       (p->pixel_format >> 16) & 0xff,
-                       (p->pixel_format >> 24) & 0xff,
-                       p->width, p->height, p->type);
-       switch (p->type) {
-       case V4L2_FRMIVAL_TYPE_DISCRETE:
-               pr_cont(" fps=%d/%d\n",
-                               p->discrete.numerator,
-                               p->discrete.denominator);
-               break;
-       case V4L2_FRMIVAL_TYPE_STEPWISE:
-               pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n",
-                               p->stepwise.min.numerator,
-                               p->stepwise.min.denominator,
-                               p->stepwise.max.numerator,
-                               p->stepwise.max.denominator,
-                               p->stepwise.step.numerator,
-                               p->stepwise.step.denominator);
-               break;
-       case V4L2_FRMIVAL_TYPE_CONTINUOUS:
-               /* fall through */
-       default:
-               pr_cont("\n");
-               break;
-       }
-}
-
-static void v4l_print_event(const void *arg, bool write_only)
-{
-       const struct v4l2_event *p = arg;
-       const struct v4l2_event_ctrl *c;
-
-       pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, "
-               "timestamp=%lu.%9.9lu\n",
-                       p->type, p->pending, p->sequence, p->id,
-                       p->timestamp.tv_sec, p->timestamp.tv_nsec);
-       switch (p->type) {
-       case V4L2_EVENT_VSYNC:
-               printk(KERN_DEBUG "field=%s\n",
-                       prt_names(p->u.vsync.field, v4l2_field_names));
-               break;
-       case V4L2_EVENT_CTRL:
-               c = &p->u.ctrl;
-               printk(KERN_DEBUG "changes=0x%x, type=%u, ",
-                       c->changes, c->type);
-               if (c->type == V4L2_CTRL_TYPE_INTEGER64)
-                       pr_cont("value64=%lld, ", c->value64);
-               else
-                       pr_cont("value=%d, ", c->value);
-               pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d,"
-                               " default_value=%d\n",
-                       c->flags, c->minimum, c->maximum,
-                       c->step, c->default_value);
-               break;
-       case V4L2_EVENT_FRAME_SYNC:
-               pr_cont("frame_sequence=%u\n",
-                       p->u.frame_sync.frame_sequence);
-               break;
-       }
-}
-
-static void v4l_print_event_subscription(const void *arg, bool write_only)
-{
-       const struct v4l2_event_subscription *p = arg;
-
-       pr_cont("type=0x%x, id=0x%x, flags=0x%x\n",
-                       p->type, p->id, p->flags);
-}
-
-static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only)
-{
-       const struct v4l2_sliced_vbi_cap *p = arg;
-       int i;
-
-       pr_cont("type=%s, service_set=0x%08x\n",
-                       prt_names(p->type, v4l2_type_names), p->service_set);
-       for (i = 0; i < 24; i++)
-               printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
-                               p->service_lines[0][i],
-                               p->service_lines[1][i]);
-}
-
-static void v4l_print_freq_band(const void *arg, bool write_only)
-{
-       const struct v4l2_frequency_band *p = arg;
-
-       pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, "
-                       "rangelow=%u, rangehigh=%u, modulation=0x%x\n",
-                       p->tuner, p->type, p->index,
-                       p->capability, p->rangelow,
-                       p->rangehigh, p->modulation);
-}
-
-static void v4l_print_u32(const void *arg, bool write_only)
-{
-       pr_cont("value=%u\n", *(const u32 *)arg);
-}
-
-static void v4l_print_newline(const void *arg, bool write_only)
-{
-       pr_cont("\n");
-}
-
-static void v4l_print_default(const void *arg, bool write_only)
-{
-       pr_cont("driver-specific ioctl\n");
-}
-
-static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
-{
-       __u32 i;
-
-       /* zero the reserved fields */
-       c->reserved[0] = c->reserved[1] = 0;
-       for (i = 0; i < c->count; i++)
-               c->controls[i].reserved2[0] = 0;
-
-       /* V4L2_CID_PRIVATE_BASE cannot be used as control class
-          when using extended controls.
-          Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
-          is it allowed for backwards compatibility.
-        */
-       if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
-               return 0;
-       /* Check that all controls are from the same control class. */
-       for (i = 0; i < c->count; i++) {
-               if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
-                       c->error_idx = i;
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
-{
-       if (ops == NULL)
-               return -EINVAL;
-
-       switch (type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (ops->vidioc_g_fmt_vid_cap ||
-                               ops->vidioc_g_fmt_vid_cap_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-               if (ops->vidioc_g_fmt_vid_cap_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (ops->vidioc_g_fmt_vid_overlay)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (ops->vidioc_g_fmt_vid_out ||
-                               ops->vidioc_g_fmt_vid_out_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               if (ops->vidioc_g_fmt_vid_out_mplane)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               if (ops->vidioc_g_fmt_vid_out_overlay)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (ops->vidioc_g_fmt_vbi_cap)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               if (ops->vidioc_g_fmt_vbi_out)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-               if (ops->vidioc_g_fmt_sliced_vbi_cap)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               if (ops->vidioc_g_fmt_sliced_vbi_out)
-                       return 0;
-               break;
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (ops->vidioc_g_fmt_type_private)
-                       return 0;
-               break;
-       }
-       return -EINVAL;
-}
-
-static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_capability *cap = (struct v4l2_capability *)arg;
-
-       cap->version = LINUX_VERSION_CODE;
-       return ops->vidioc_querycap(file, fh, cap);
-}
-
-static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd;
-       u32 *p = arg;
-
-       if (ops->vidioc_g_priority)
-               return ops->vidioc_g_priority(file, fh, arg);
-       vfd = video_devdata(file);
-       *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
-       return 0;
-}
-
-static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd;
-       struct v4l2_fh *vfh;
-       u32 *p = arg;
-
-       if (ops->vidioc_s_priority)
-               return ops->vidioc_s_priority(file, fh, *p);
-       vfd = video_devdata(file);
-       vfh = file->private_data;
-       return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
-}
-
-static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_input *p = arg;
-
-       /*
-        * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
-        * CAP_STD here based on ioctl handler provided by the
-        * driver. If the driver doesn't support these
-        * for a specific input, it must override these flags.
-        */
-       if (ops->vidioc_s_std)
-               p->capabilities |= V4L2_IN_CAP_STD;
-       if (ops->vidioc_s_dv_preset)
-               p->capabilities |= V4L2_IN_CAP_PRESETS;
-       if (ops->vidioc_s_dv_timings)
-               p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
-
-       return ops->vidioc_enum_input(file, fh, p);
-}
-
-static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_output *p = arg;
-
-       /*
-        * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
-        * CAP_STD here based on ioctl handler provided by the
-        * driver. If the driver doesn't support these
-        * for a specific output, it must override these flags.
-        */
-       if (ops->vidioc_s_std)
-               p->capabilities |= V4L2_OUT_CAP_STD;
-       if (ops->vidioc_s_dv_preset)
-               p->capabilities |= V4L2_OUT_CAP_PRESETS;
-       if (ops->vidioc_s_dv_timings)
-               p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
-
-       return ops->vidioc_enum_output(file, fh, p);
-}
-
-static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_fmtdesc *p = arg;
-
-       switch (p->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
-                       break;
-               return ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-               if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
-                       break;
-               return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
-                       break;
-               return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (unlikely(!ops->vidioc_enum_fmt_vid_out))
-                       break;
-               return ops->vidioc_enum_fmt_vid_out(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
-                       break;
-               return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (unlikely(!ops->vidioc_enum_fmt_type_private))
-                       break;
-               return ops->vidioc_enum_fmt_type_private(file, fh, arg);
-       }
-       return -EINVAL;
-}
-
-static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_format *p = arg;
-
-       switch (p->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (unlikely(!ops->vidioc_g_fmt_vid_cap))
-                       break;
-               return ops->vidioc_g_fmt_vid_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-               if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane))
-                       break;
-               return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (unlikely(!ops->vidioc_g_fmt_vid_overlay))
-                       break;
-               return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (unlikely(!ops->vidioc_g_fmt_vid_out))
-                       break;
-               return ops->vidioc_g_fmt_vid_out(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane))
-                       break;
-               return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay))
-                       break;
-               return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (unlikely(!ops->vidioc_g_fmt_vbi_cap))
-                       break;
-               return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               if (unlikely(!ops->vidioc_g_fmt_vbi_out))
-                       break;
-               return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-               if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap))
-                       break;
-               return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out))
-                       break;
-               return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (unlikely(!ops->vidioc_g_fmt_type_private))
-                       break;
-               return ops->vidioc_g_fmt_type_private(file, fh, arg);
-       }
-       return -EINVAL;
-}
-
-static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_format *p = arg;
-
-       switch (p->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (unlikely(!ops->vidioc_s_fmt_vid_cap))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix);
-               return ops->vidioc_s_fmt_vid_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-               if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
-               return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.win);
-               return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (unlikely(!ops->vidioc_s_fmt_vid_out))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix);
-               return ops->vidioc_s_fmt_vid_out(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
-               return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.win);
-               return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.vbi);
-               return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               if (unlikely(!ops->vidioc_s_fmt_vbi_out))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.vbi);
-               return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.sliced);
-               return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.sliced);
-               return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (unlikely(!ops->vidioc_s_fmt_type_private))
-                       break;
-               return ops->vidioc_s_fmt_type_private(file, fh, arg);
-       }
-       return -EINVAL;
-}
-
-static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_format *p = arg;
-
-       switch (p->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (unlikely(!ops->vidioc_try_fmt_vid_cap))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix);
-               return ops->vidioc_try_fmt_vid_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-               if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
-               return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.win);
-               return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               if (unlikely(!ops->vidioc_try_fmt_vid_out))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix);
-               return ops->vidioc_try_fmt_vid_out(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.pix_mp);
-               return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
-       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.win);
-               return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.vbi);
-               return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_VBI_OUTPUT:
-               if (unlikely(!ops->vidioc_try_fmt_vbi_out))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.vbi);
-               return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.sliced);
-               return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
-       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
-                       break;
-               CLEAR_AFTER_FIELD(p, fmt.sliced);
-               return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (unlikely(!ops->vidioc_try_fmt_type_private))
-                       break;
-               return ops->vidioc_try_fmt_type_private(file, fh, arg);
-       }
-       return -EINVAL;
-}
-
-static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_tuner *p = arg;
-       int err;
-
-       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-       err = ops->vidioc_g_tuner(file, fh, p);
-       if (!err)
-               p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
-       return err;
-}
-
-static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_tuner *p = arg;
-
-       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-       return ops->vidioc_s_tuner(file, fh, p);
-}
-
-static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_modulator *p = arg;
-       int err;
-
-       err = ops->vidioc_g_modulator(file, fh, p);
-       if (!err)
-               p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
-       return err;
-}
-
-static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_frequency *p = arg;
-
-       p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-       return ops->vidioc_g_frequency(file, fh, p);
-}
-
-static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_frequency *p = arg;
-       enum v4l2_tuner_type type;
-
-       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-       if (p->type != type)
-               return -EINVAL;
-       return ops->vidioc_s_frequency(file, fh, p);
-}
-
-static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_standard *p = arg;
-       v4l2_std_id id = vfd->tvnorms, curr_id = 0;
-       unsigned int index = p->index, i, j = 0;
-       const char *descr = "";
-
-       /* Return norm array in a canonical way */
-       for (i = 0; i <= index && id; i++) {
-               /* last std value in the standards array is 0, so this
-                  while always ends there since (id & 0) == 0. */
-               while ((id & standards[j].std) != standards[j].std)
-                       j++;
-               curr_id = standards[j].std;
-               descr = standards[j].descr;
-               j++;
-               if (curr_id == 0)
-                       break;
-               if (curr_id != V4L2_STD_PAL &&
-                               curr_id != V4L2_STD_SECAM &&
-                               curr_id != V4L2_STD_NTSC)
-                       id &= ~curr_id;
-       }
-       if (i <= index)
-               return -EINVAL;
-
-       v4l2_video_std_construct(p, curr_id, descr);
-       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 = 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;
-}
-
-static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       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.
-        * This means that tuners, audio and video decoders can join
-        * their efforts to improve the standards detection.
-        */
-       *p = vfd->tvnorms;
-       return ops->vidioc_querystd(file, fh, arg);
-}
-
-static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_hw_freq_seek *p = arg;
-       enum v4l2_tuner_type type;
-
-       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-               V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-       if (p->type != type)
-               return -EINVAL;
-       return ops->vidioc_s_hw_freq_seek(file, fh, p);
-}
-
-static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_requestbuffers *p = arg;
-       int ret = check_fmt(ops, p->type);
-
-       if (ret)
-               return ret;
-
-       if (p->type < V4L2_BUF_TYPE_PRIVATE)
-               CLEAR_AFTER_FIELD(p, memory);
-
-       return ops->vidioc_reqbufs(file, fh, p);
-}
-
-static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_buffer *p = arg;
-       int ret = check_fmt(ops, p->type);
-
-       return ret ? ret : ops->vidioc_querybuf(file, fh, p);
-}
-
-static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_buffer *p = arg;
-       int ret = check_fmt(ops, p->type);
-
-       return ret ? ret : ops->vidioc_qbuf(file, fh, p);
-}
-
-static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_buffer *p = arg;
-       int ret = check_fmt(ops, p->type);
-
-       return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
-}
-
-static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_create_buffers *create = arg;
-       int ret = check_fmt(ops, create->format.type);
-
-       return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
-}
-
-static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_buffer *b = arg;
-       int ret = check_fmt(ops, b->type);
-
-       return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
-}
-
-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(ops, p->type);
-
-       if (ret)
-               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 (ops->vidioc_g_std)
-               ret = ops->vidioc_g_std(file, fh, &std);
-       if (ret == 0)
-               v4l2_video_std_frame_period(std,
-                           &p->parm.capture.timeperframe);
-       return ret;
-}
-
-static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_streamparm *p = arg;
-       int ret = check_fmt(ops, p->type);
-
-       return ret ? ret : ops->vidioc_s_parm(file, fh, p);
-}
-
-static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_queryctrl *p = arg;
-       struct v4l2_fh *vfh =
-               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
-       if (vfh && vfh->ctrl_handler)
-               return v4l2_queryctrl(vfh->ctrl_handler, p);
-       if (vfd->ctrl_handler)
-               return v4l2_queryctrl(vfd->ctrl_handler, p);
-       if (ops->vidioc_queryctrl)
-               return ops->vidioc_queryctrl(file, fh, p);
-       return -ENOTTY;
-}
-
-static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_querymenu *p = arg;
-       struct v4l2_fh *vfh =
-               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
-       if (vfh && vfh->ctrl_handler)
-               return v4l2_querymenu(vfh->ctrl_handler, p);
-       if (vfd->ctrl_handler)
-               return v4l2_querymenu(vfd->ctrl_handler, p);
-       if (ops->vidioc_querymenu)
-               return ops->vidioc_querymenu(file, fh, p);
-       return -ENOTTY;
-}
-
-static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_control *p = arg;
-       struct v4l2_fh *vfh =
-               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-       struct v4l2_ext_controls ctrls;
-       struct v4l2_ext_control ctrl;
-
-       if (vfh && vfh->ctrl_handler)
-               return v4l2_g_ctrl(vfh->ctrl_handler, p);
-       if (vfd->ctrl_handler)
-               return v4l2_g_ctrl(vfd->ctrl_handler, p);
-       if (ops->vidioc_g_ctrl)
-               return ops->vidioc_g_ctrl(file, fh, p);
-       if (ops->vidioc_g_ext_ctrls == NULL)
-               return -ENOTTY;
-
-       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
-       ctrls.count = 1;
-       ctrls.controls = &ctrl;
-       ctrl.id = p->id;
-       ctrl.value = p->value;
-       if (check_ext_ctrls(&ctrls, 1)) {
-               int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
-
-               if (ret == 0)
-                       p->value = ctrl.value;
-               return ret;
-       }
-       return -EINVAL;
-}
-
-static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_control *p = arg;
-       struct v4l2_fh *vfh =
-               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-       struct v4l2_ext_controls ctrls;
-       struct v4l2_ext_control ctrl;
-
-       if (vfh && vfh->ctrl_handler)
-               return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
-       if (vfd->ctrl_handler)
-               return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
-       if (ops->vidioc_s_ctrl)
-               return ops->vidioc_s_ctrl(file, fh, p);
-       if (ops->vidioc_s_ext_ctrls == NULL)
-               return -ENOTTY;
-
-       ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
-       ctrls.count = 1;
-       ctrls.controls = &ctrl;
-       ctrl.id = p->id;
-       ctrl.value = p->value;
-       if (check_ext_ctrls(&ctrls, 1))
-               return ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
-       return -EINVAL;
-}
-
-static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_ext_controls *p = arg;
-       struct v4l2_fh *vfh =
-               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
-       p->error_idx = p->count;
-       if (vfh && vfh->ctrl_handler)
-               return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
-       if (vfd->ctrl_handler)
-               return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
-       if (ops->vidioc_g_ext_ctrls == NULL)
-               return -ENOTTY;
-       return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
-                                       -EINVAL;
-}
-
-static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_ext_controls *p = arg;
-       struct v4l2_fh *vfh =
-               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
-       p->error_idx = p->count;
-       if (vfh && vfh->ctrl_handler)
-               return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
-       if (vfd->ctrl_handler)
-               return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
-       if (ops->vidioc_s_ext_ctrls == NULL)
-               return -ENOTTY;
-       return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
-                                       -EINVAL;
-}
-
-static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_ext_controls *p = arg;
-       struct v4l2_fh *vfh =
-               test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
-       p->error_idx = p->count;
-       if (vfh && vfh->ctrl_handler)
-               return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
-       if (vfd->ctrl_handler)
-               return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
-       if (ops->vidioc_try_ext_ctrls == NULL)
-               return -ENOTTY;
-       return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
-                                       -EINVAL;
-}
-
-static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_crop *p = arg;
-       struct v4l2_selection s = {
-               .type = p->type,
-       };
-       int ret;
-
-       if (ops->vidioc_g_crop)
-               return ops->vidioc_g_crop(file, fh, p);
-       /* simulate capture crop using selection api */
-
-       /* crop means compose for output devices */
-       if (V4L2_TYPE_IS_OUTPUT(p->type))
-               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
-       else
-               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
-       ret = ops->vidioc_g_selection(file, fh, &s);
-
-       /* copying results to old structure on success */
-       if (!ret)
-               p->c = s.r;
-       return ret;
-}
-
-static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_crop *p = arg;
-       struct v4l2_selection s = {
-               .type = p->type,
-               .r = p->c,
-       };
-
-       if (ops->vidioc_s_crop)
-               return ops->vidioc_s_crop(file, fh, p);
-       /* simulate capture crop using selection api */
-
-       /* crop means compose for output devices */
-       if (V4L2_TYPE_IS_OUTPUT(p->type))
-               s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
-       else
-               s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
-       return ops->vidioc_s_selection(file, fh, &s);
-}
-
-static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_cropcap *p = arg;
-       struct v4l2_selection s = { .type = p->type };
-       int ret;
-
-       if (ops->vidioc_cropcap)
-               return ops->vidioc_cropcap(file, fh, p);
-
-       /* obtaining bounds */
-       if (V4L2_TYPE_IS_OUTPUT(p->type))
-               s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
-       else
-               s.target = V4L2_SEL_TGT_CROP_BOUNDS;
-
-       ret = ops->vidioc_g_selection(file, fh, &s);
-       if (ret)
-               return ret;
-       p->bounds = s.r;
-
-       /* obtaining defrect */
-       if (V4L2_TYPE_IS_OUTPUT(p->type))
-               s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
-       else
-               s.target = V4L2_SEL_TGT_CROP_DEFAULT;
-
-       ret = ops->vidioc_g_selection(file, fh, &s);
-       if (ret)
-               return ret;
-       p->defrect = s.r;
-
-       /* setting trivial pixelaspect */
-       p->pixelaspect.numerator = 1;
-       p->pixelaspect.denominator = 1;
-       return 0;
-}
-
-static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       int ret;
-
-       if (vfd->v4l2_dev)
-               pr_info("%s: =================  START STATUS  =================\n",
-                       vfd->v4l2_dev->name);
-       ret = ops->vidioc_log_status(file, fh);
-       if (vfd->v4l2_dev)
-               pr_info("%s: ==================  END STATUS  ==================\n",
-                       vfd->v4l2_dev->name);
-       return ret;
-}
-
-static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       struct v4l2_dbg_register *p = arg;
-
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-       return ops->vidioc_g_register(file, fh, p);
-#else
-       return -ENOTTY;
-#endif
-}
-
-static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       struct v4l2_dbg_register *p = arg;
-
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-       return ops->vidioc_s_register(file, fh, p);
-#else
-       return -ENOTTY;
-#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;
-       return ops->vidioc_g_chip_ident(file, fh, p);
-}
-
-static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
-}
-
-static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       return ops->vidioc_subscribe_event(fh, arg);
-}
-
-static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       return ops->vidioc_unsubscribe_event(fh, arg);
-}
-
-static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct v4l2_sliced_vbi_cap *p = arg;
-
-       /* Clear up to type, everything after type is zeroed already */
-       memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
-
-       return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
-}
-
-static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       struct v4l2_frequency_band *p = arg;
-       enum v4l2_tuner_type type;
-       int err;
-
-       type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
-                       V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-
-       if (type != p->type)
-               return -EINVAL;
-       if (ops->vidioc_enum_freq_bands)
-               return ops->vidioc_enum_freq_bands(file, fh, p);
-       if (ops->vidioc_g_tuner) {
-               struct v4l2_tuner t = {
-                       .index = p->tuner,
-                       .type = type,
-               };
-
-               err = ops->vidioc_g_tuner(file, fh, &t);
-               if (err)
-                       return err;
-               p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
-               p->rangelow = t.rangelow;
-               p->rangehigh = t.rangehigh;
-               p->modulation = (type == V4L2_TUNER_RADIO) ?
-                       V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
-               return 0;
-       }
-       if (ops->vidioc_g_modulator) {
-               struct v4l2_modulator m = {
-                       .index = p->tuner,
-               };
-
-               if (type != V4L2_TUNER_RADIO)
-                       return -EINVAL;
-               err = ops->vidioc_g_modulator(file, fh, &m);
-               if (err)
-                       return err;
-               p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
-               p->rangelow = m.rangelow;
-               p->rangehigh = m.rangehigh;
-               p->modulation = (type == V4L2_TUNER_RADIO) ?
-                       V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
-               return 0;
-       }
-       return -ENOTTY;
-}
-
-struct v4l2_ioctl_info {
-       unsigned int ioctl;
-       u32 flags;
-       const char * const name;
-       union {
-               u32 offset;
-               int (*func)(const struct v4l2_ioctl_ops *ops,
-                               struct file *file, void *fh, void *p);
-       } u;
-       void (*debug)(const void *arg, bool write_only);
-};
-
-/* This control needs a priority check */
-#define INFO_FL_PRIO   (1 << 0)
-/* This control can be valid if the filehandle passes a control handler. */
-#define INFO_FL_CTRL   (1 << 1)
-/* This is a standard ioctl, no need for special code */
-#define INFO_FL_STD    (1 << 2)
-/* This is ioctl has its own function */
-#define INFO_FL_FUNC   (1 << 3)
-/* Queuing ioctl */
-#define INFO_FL_QUEUE  (1 << 4)
-/* Zero struct from after the field to the end */
-#define INFO_FL_CLEAR(v4l2_struct, field)                      \
-       ((offsetof(struct v4l2_struct, field) +                 \
-         sizeof(((struct v4l2_struct *)0)->field)) << 16)
-#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
-
-#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags)                        \
-       [_IOC_NR(_ioctl)] = {                                           \
-               .ioctl = _ioctl,                                        \
-               .flags = _flags | INFO_FL_STD,                          \
-               .name = #_ioctl,                                        \
-               .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc),   \
-               .debug = _debug,                                        \
-       }
-
-#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags)                  \
-       [_IOC_NR(_ioctl)] = {                                           \
-               .ioctl = _ioctl,                                        \
-               .flags = _flags | INFO_FL_FUNC,                         \
-               .name = #_ioctl,                                        \
-               .u.func = _func,                                        \
-               .debug = _debug,                                        \
-       }
-
-static struct v4l2_ioctl_info v4l2_ioctls[] = {
-       IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
-       IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
-       IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
-       IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
-       IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
-       IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
-       IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
-       IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
-       IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
-       IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
-       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_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)),
-       IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
-       IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
-       IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
-       IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
-       IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
-       IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
-       IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
-       IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
-       IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
-       IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
-       IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
-       IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
-       IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
-       IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
-       IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
-       IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
-       IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
-       IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0),
-       IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO),
-       IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
-       IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
-       IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
-       IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
-       IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
-       IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
-       IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
-       IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
-       IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
-       IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
-       IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
-       IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
-       IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
-       IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
-       IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
-       IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
-       IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
-       IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
-       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_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
-       IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
-       IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
-       IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
-       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),
-       IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
-       IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
-       IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
-       IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
-       IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
-       IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
-       IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
-       IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
-       IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
-};
-#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
-
-bool v4l2_is_known_ioctl(unsigned int cmd)
-{
-       if (_IOC_NR(cmd) >= V4L2_IOCTLS)
-               return false;
-       return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
-}
-
-struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
-{
-       if (_IOC_NR(cmd) >= V4L2_IOCTLS)
-               return vdev->lock;
-       if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
-               return NULL;
-       if (vdev->queue && vdev->queue->lock &&
-                       (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
-               return vdev->queue->lock;
-       return vdev->lock;
-}
-
-/* Common ioctl debug function. This function can be used by
-   external ioctl messages as well as internal V4L ioctl */
-void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
-{
-       const char *dir, *type;
-
-       if (prefix)
-               printk(KERN_DEBUG "%s: ", prefix);
-
-       switch (_IOC_TYPE(cmd)) {
-       case 'd':
-               type = "v4l2_int";
-               break;
-       case 'V':
-               if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
-                       type = "v4l2";
-                       break;
-               }
-               pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
-               return;
-       default:
-               type = "unknown";
-               break;
-       }
-
-       switch (_IOC_DIR(cmd)) {
-       case _IOC_NONE:              dir = "--"; break;
-       case _IOC_READ:              dir = "r-"; break;
-       case _IOC_WRITE:             dir = "-w"; break;
-       case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
-       default:                     dir = "*ERR*"; break;
-       }
-       pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
-               type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
-}
-EXPORT_SYMBOL(v4l_printk_ioctl);
-
-static long __video_do_ioctl(struct file *file,
-               unsigned int cmd, void *arg)
-{
-       struct video_device *vfd = video_devdata(file);
-       const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
-       bool write_only = false;
-       struct v4l2_ioctl_info default_info;
-       const struct v4l2_ioctl_info *info;
-       void *fh = file->private_data;
-       struct v4l2_fh *vfh = NULL;
-       int use_fh_prio = 0;
-       int debug = vfd->debug;
-       long ret = -ENOTTY;
-
-       if (ops == NULL) {
-               pr_warn("%s: has no ioctl_ops.\n",
-                               video_device_node_name(vfd));
-               return ret;
-       }
-
-       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
-               vfh = file->private_data;
-               use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
-       }
-
-       if (v4l2_is_known_ioctl(cmd)) {
-               info = &v4l2_ioctls[_IOC_NR(cmd)];
-
-               if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
-                   !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
-                       goto done;
-
-               if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
-                       ret = v4l2_prio_check(vfd->prio, vfh->prio);
-                       if (ret)
-                               goto done;
-               }
-       } else {
-               default_info.ioctl = cmd;
-               default_info.flags = 0;
-               default_info.debug = v4l_print_default;
-               info = &default_info;
-       }
-
-       write_only = _IOC_DIR(cmd) == _IOC_WRITE;
-       if (write_only && debug > V4L2_DEBUG_IOCTL) {
-               v4l_printk_ioctl(video_device_node_name(vfd), cmd);
-               pr_cont(": ");
-               info->debug(arg, write_only);
-       }
-       if (info->flags & INFO_FL_STD) {
-               typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
-               const void *p = vfd->ioctl_ops;
-               const vidioc_op *vidioc = p + info->u.offset;
-
-               ret = (*vidioc)(file, fh, arg);
-       } else if (info->flags & INFO_FL_FUNC) {
-               ret = info->u.func(ops, file, fh, arg);
-       } else if (!ops->vidioc_default) {
-               ret = -ENOTTY;
-       } else {
-               ret = ops->vidioc_default(file, fh,
-                       use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
-                       cmd, arg);
-       }
-
-done:
-       if (debug) {
-               if (write_only && debug > V4L2_DEBUG_IOCTL) {
-                       if (ret < 0)
-                               printk(KERN_DEBUG "%s: error %ld\n",
-                                       video_device_node_name(vfd), ret);
-                       return ret;
-               }
-               v4l_printk_ioctl(video_device_node_name(vfd), cmd);
-               if (ret < 0)
-                       pr_cont(": error %ld\n", ret);
-               else if (debug == V4L2_DEBUG_IOCTL)
-                       pr_cont("\n");
-               else if (_IOC_DIR(cmd) == _IOC_NONE)
-                       info->debug(arg, write_only);
-               else {
-                       pr_cont(": ");
-                       info->debug(arg, write_only);
-               }
-       }
-
-       return ret;
-}
-
-static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
-                           void * __user *user_ptr, void ***kernel_ptr)
-{
-       int ret = 0;
-
-       switch (cmd) {
-       case VIDIOC_QUERYBUF:
-       case VIDIOC_QBUF:
-       case VIDIOC_DQBUF: {
-               struct v4l2_buffer *buf = parg;
-
-               if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) {
-                       if (buf->length > VIDEO_MAX_PLANES) {
-                               ret = -EINVAL;
-                               break;
-                       }
-                       *user_ptr = (void __user *)buf->m.planes;
-                       *kernel_ptr = (void *)&buf->m.planes;
-                       *array_size = sizeof(struct v4l2_plane) * buf->length;
-                       ret = 1;
-               }
-               break;
-       }
-
-       case VIDIOC_S_EXT_CTRLS:
-       case VIDIOC_G_EXT_CTRLS:
-       case VIDIOC_TRY_EXT_CTRLS: {
-               struct v4l2_ext_controls *ctrls = parg;
-
-               if (ctrls->count != 0) {
-                       if (ctrls->count > V4L2_CID_MAX_CTRLS) {
-                               ret = -EINVAL;
-                               break;
-                       }
-                       *user_ptr = (void __user *)ctrls->controls;
-                       *kernel_ptr = (void *)&ctrls->controls;
-                       *array_size = sizeof(struct v4l2_ext_control)
-                                   * ctrls->count;
-                       ret = 1;
-               }
-               break;
-       }
-       }
-
-       return ret;
-}
-
-long
-video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
-              v4l2_kioctl func)
-{
-       char    sbuf[128];
-       void    *mbuf = NULL;
-       void    *parg = (void *)arg;
-       long    err  = -EINVAL;
-       bool    has_array_args;
-       size_t  array_size = 0;
-       void __user *user_ptr = NULL;
-       void    **kernel_ptr = NULL;
-
-       /*  Copy arguments into temp kernel buffer  */
-       if (_IOC_DIR(cmd) != _IOC_NONE) {
-               if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
-                       parg = sbuf;
-               } else {
-                       /* too big to allocate from stack */
-                       mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
-                       if (NULL == mbuf)
-                               return -ENOMEM;
-                       parg = mbuf;
-               }
-
-               err = -EFAULT;
-               if (_IOC_DIR(cmd) & _IOC_WRITE) {
-                       unsigned int n = _IOC_SIZE(cmd);
-
-                       /*
-                        * In some cases, only a few fields are used as input,
-                        * i.e. when the app sets "index" and then the driver
-                        * fills in the rest of the structure for the thing
-                        * with that index.  We only need to copy up the first
-                        * non-input field.
-                        */
-                       if (v4l2_is_known_ioctl(cmd)) {
-                               u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
-                               if (flags & INFO_FL_CLEAR_MASK)
-                                       n = (flags & INFO_FL_CLEAR_MASK) >> 16;
-                       }
-
-                       if (copy_from_user(parg, (void __user *)arg, n))
-                               goto out;
-
-                       /* zero out anything we don't copy from userspace */
-                       if (n < _IOC_SIZE(cmd))
-                               memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
-               } else {
-                       /* read-only ioctl */
-                       memset(parg, 0, _IOC_SIZE(cmd));
-               }
-       }
-
-       err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
-       if (err < 0)
-               goto out;
-       has_array_args = err;
-
-       if (has_array_args) {
-               /*
-                * When adding new types of array args, make sure that the
-                * parent argument to ioctl (which contains the pointer to the
-                * array) fits into sbuf (so that mbuf will still remain
-                * unused up to here).
-                */
-               mbuf = kmalloc(array_size, GFP_KERNEL);
-               err = -ENOMEM;
-               if (NULL == mbuf)
-                       goto out_array_args;
-               err = -EFAULT;
-               if (copy_from_user(mbuf, user_ptr, array_size))
-                       goto out_array_args;
-               *kernel_ptr = mbuf;
-       }
-
-       /* Handles IOCTL */
-       err = func(file, cmd, parg);
-       if (err == -ENOIOCTLCMD)
-               err = -ENOTTY;
-
-       if (has_array_args) {
-               *kernel_ptr = user_ptr;
-               if (copy_to_user(user_ptr, mbuf, array_size))
-                       err = -EFAULT;
-               goto out_array_args;
-       }
-       /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid
-          results that must be returned. */
-       if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS)
-               goto out;
-
-out_array_args:
-       /*  Copy results into user buffer  */
-       switch (_IOC_DIR(cmd)) {
-       case _IOC_READ:
-       case (_IOC_WRITE | _IOC_READ):
-               if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
-                       err = -EFAULT;
-               break;
-       }
-
-out:
-       kfree(mbuf);
-       return err;
-}
-EXPORT_SYMBOL(video_usercopy);
-
-long video_ioctl2(struct file *file,
-              unsigned int cmd, unsigned long arg)
-{
-       return video_usercopy(file, cmd, arg, __video_do_ioctl);
-}
-EXPORT_SYMBOL(video_ioctl2);
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
deleted file mode 100644 (file)
index 97b4831..0000000
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Memory-to-memory device framework for Video for Linux 2 and videobuf.
- *
- * Helper functions for devices that use videobuf buffers for both their
- * source and destination.
- *
- * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
- * Pawel Osciak, <pawel@osciak.com>
- * Marek Szyprowski, <m.szyprowski@samsung.com>
- *
- * 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.
- */
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <media/videobuf2-core.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
-MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
-MODULE_LICENSE("GPL");
-
-static bool debug;
-module_param(debug, bool, 0644);
-
-#define dprintk(fmt, arg...)                                           \
-       do {                                                            \
-               if (debug)                                              \
-                       printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\
-       } while (0)
-
-
-/* Instance is already queued on the job_queue */
-#define TRANS_QUEUED           (1 << 0)
-/* Instance is currently running in hardware */
-#define TRANS_RUNNING          (1 << 1)
-
-
-/* Offset base for buffers on the destination queue - used to distinguish
- * between source and destination buffers when mmapping - they receive the same
- * offsets but for different queues */
-#define DST_QUEUE_OFF_BASE     (1 << 30)
-
-
-/**
- * struct v4l2_m2m_dev - per-device context
- * @curr_ctx:          currently running instance
- * @job_queue:         instances queued to run
- * @job_spinlock:      protects job_queue
- * @m2m_ops:           driver callbacks
- */
-struct v4l2_m2m_dev {
-       struct v4l2_m2m_ctx     *curr_ctx;
-
-       struct list_head        job_queue;
-       spinlock_t              job_spinlock;
-
-       struct v4l2_m2m_ops     *m2m_ops;
-};
-
-static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
-                                               enum v4l2_buf_type type)
-{
-       if (V4L2_TYPE_IS_OUTPUT(type))
-               return &m2m_ctx->out_q_ctx;
-       else
-               return &m2m_ctx->cap_q_ctx;
-}
-
-/**
- * v4l2_m2m_get_vq() - return vb2_queue for the given type
- */
-struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
-                                      enum v4l2_buf_type type)
-{
-       struct v4l2_m2m_queue_ctx *q_ctx;
-
-       q_ctx = get_queue_ctx(m2m_ctx, type);
-       if (!q_ctx)
-               return NULL;
-
-       return &q_ctx->q;
-}
-EXPORT_SYMBOL(v4l2_m2m_get_vq);
-
-/**
- * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers
- */
-void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
-{
-       struct v4l2_m2m_buffer *b = NULL;
-       unsigned long flags;
-
-       spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
-
-       if (list_empty(&q_ctx->rdy_queue)) {
-               spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
-               return NULL;
-       }
-
-       b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
-       spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
-       return &b->vb;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
-
-/**
- * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and
- * return it
- */
-void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
-{
-       struct v4l2_m2m_buffer *b = NULL;
-       unsigned long flags;
-
-       spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
-       if (list_empty(&q_ctx->rdy_queue)) {
-               spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
-               return NULL;
-       }
-       b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
-       list_del(&b->list);
-       q_ctx->num_rdy--;
-       spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
-
-       return &b->vb;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
-
-/*
- * Scheduling handlers
- */
-
-/**
- * v4l2_m2m_get_curr_priv() - return driver private data for the currently
- * running instance or NULL if no instance is running
- */
-void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev)
-{
-       unsigned long flags;
-       void *ret = NULL;
-
-       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
-       if (m2m_dev->curr_ctx)
-               ret = m2m_dev->curr_ctx->priv;
-       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-
-       return ret;
-}
-EXPORT_SYMBOL(v4l2_m2m_get_curr_priv);
-
-/**
- * v4l2_m2m_try_run() - select next job to perform and run it if possible
- *
- * Get next transaction (if present) from the waiting jobs list and run it.
- */
-static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
-       if (NULL != m2m_dev->curr_ctx) {
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-               dprintk("Another instance is running, won't run now\n");
-               return;
-       }
-
-       if (list_empty(&m2m_dev->job_queue)) {
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-               dprintk("No job pending\n");
-               return;
-       }
-
-       m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next,
-                                  struct v4l2_m2m_ctx, queue);
-       m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING;
-       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-
-       m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv);
-}
-
-/**
- * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to
- * the pending job queue and add it if so.
- * @m2m_ctx:   m2m context assigned to the instance to be checked
- *
- * There are three basic requirements an instance has to meet to be able to run:
- * 1) at least one source buffer has to be queued,
- * 2) at least one destination buffer has to be queued,
- * 3) streaming has to be on.
- *
- * There may also be additional, custom requirements. In such case the driver
- * should supply a custom callback (job_ready in v4l2_m2m_ops) that should
- * return 1 if the instance is ready.
- * An example of the above could be an instance that requires more than one
- * src/dst buffer per transaction.
- */
-static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
-{
-       struct v4l2_m2m_dev *m2m_dev;
-       unsigned long flags_job, flags;
-
-       m2m_dev = m2m_ctx->m2m_dev;
-       dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
-
-       if (!m2m_ctx->out_q_ctx.q.streaming
-           || !m2m_ctx->cap_q_ctx.q.streaming) {
-               dprintk("Streaming needs to be on for both queues\n");
-               return;
-       }
-
-       spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
-       if (m2m_ctx->job_flags & TRANS_QUEUED) {
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
-               dprintk("On job queue already\n");
-               return;
-       }
-
-       spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
-       if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) {
-               spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
-               dprintk("No input buffers available\n");
-               return;
-       }
-       if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) {
-               spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
-               dprintk("No output buffers available\n");
-               return;
-       }
-       spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
-
-       if (m2m_dev->m2m_ops->job_ready
-               && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
-               dprintk("Driver not ready\n");
-               return;
-       }
-
-       list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
-       m2m_ctx->job_flags |= TRANS_QUEUED;
-
-       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
-
-       v4l2_m2m_try_run(m2m_dev);
-}
-
-/**
- * v4l2_m2m_job_finish() - inform the framework that a job has been finished
- * and have it clean up
- *
- * Called by a driver to yield back the device after it has finished with it.
- * Should be called as soon as possible after reaching a state which allows
- * other instances to take control of the device.
- *
- * This function has to be called only after device_run() callback has been
- * called on the driver. To prevent recursion, it should not be called directly
- * from the device_run() callback though.
- */
-void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
-                        struct v4l2_m2m_ctx *m2m_ctx)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
-       if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-               dprintk("Called by an instance not currently running\n");
-               return;
-       }
-
-       list_del(&m2m_dev->curr_ctx->queue);
-       m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
-       wake_up(&m2m_dev->curr_ctx->finished);
-       m2m_dev->curr_ctx = NULL;
-
-       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-
-       /* This instance might have more buffers ready, but since we do not
-        * allow more than one job on the job_queue per instance, each has
-        * to be scheduled separately after the previous one finishes. */
-       v4l2_m2m_try_schedule(m2m_ctx);
-       v4l2_m2m_try_run(m2m_dev);
-}
-EXPORT_SYMBOL(v4l2_m2m_job_finish);
-
-/**
- * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
- */
-int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                    struct v4l2_requestbuffers *reqbufs)
-{
-       struct vb2_queue *vq;
-
-       vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
-       return vb2_reqbufs(vq, reqbufs);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
-
-/**
- * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer
- *
- * See v4l2_m2m_mmap() documentation for details.
- */
-int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                     struct v4l2_buffer *buf)
-{
-       struct vb2_queue *vq;
-       int ret = 0;
-       unsigned int i;
-
-       vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-       ret = vb2_querybuf(vq, buf);
-
-       /* Adjust MMAP memory offsets for the CAPTURE queue */
-       if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) {
-               if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
-                       for (i = 0; i < buf->length; ++i)
-                               buf->m.planes[i].m.mem_offset
-                                       += DST_QUEUE_OFF_BASE;
-               } else {
-                       buf->m.offset += DST_QUEUE_OFF_BASE;
-               }
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
-
-/**
- * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on
- * the type
- */
-int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                 struct v4l2_buffer *buf)
-{
-       struct vb2_queue *vq;
-       int ret;
-
-       vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-       ret = vb2_qbuf(vq, buf);
-       if (!ret)
-               v4l2_m2m_try_schedule(m2m_ctx);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
-
-/**
- * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on
- * the type
- */
-int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                  struct v4l2_buffer *buf)
-{
-       struct vb2_queue *vq;
-
-       vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-       return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
-
-/**
- * v4l2_m2m_streamon() - turn on streaming for a video queue
- */
-int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                     enum v4l2_buf_type type)
-{
-       struct vb2_queue *vq;
-       int ret;
-
-       vq = v4l2_m2m_get_vq(m2m_ctx, type);
-       ret = vb2_streamon(vq, type);
-       if (!ret)
-               v4l2_m2m_try_schedule(m2m_ctx);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
-
-/**
- * v4l2_m2m_streamoff() - turn off streaming for a video queue
- */
-int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                      enum v4l2_buf_type type)
-{
-       struct vb2_queue *vq;
-
-       vq = v4l2_m2m_get_vq(m2m_ctx, type);
-       return vb2_streamoff(vq, type);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
-
-/**
- * v4l2_m2m_poll() - poll replacement, for destination buffers only
- *
- * Call from the driver's poll() function. Will poll both queues. If a buffer
- * is available to dequeue (with dqbuf) from the source queue, this will
- * indicate that a non-blocking write can be performed, while read will be
- * returned in case of the destination queue.
- */
-unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                          struct poll_table_struct *wait)
-{
-       struct video_device *vfd = video_devdata(file);
-       unsigned long req_events = poll_requested_events(wait);
-       struct vb2_queue *src_q, *dst_q;
-       struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
-       unsigned int rc = 0;
-       unsigned long flags;
-
-       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
-               struct v4l2_fh *fh = file->private_data;
-
-               if (v4l2_event_pending(fh))
-                       rc = POLLPRI;
-               else if (req_events & POLLPRI)
-                       poll_wait(file, &fh->wait, wait);
-               if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM)))
-                       return rc;
-       }
-
-       src_q = v4l2_m2m_get_src_vq(m2m_ctx);
-       dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
-
-       /*
-        * There has to be at least one buffer queued on each queued_list, which
-        * means either in driver already or waiting for driver to claim it
-        * and start processing.
-        */
-       if ((!src_q->streaming || list_empty(&src_q->queued_list))
-               && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
-               rc |= POLLERR;
-               goto end;
-       }
-
-       if (m2m_ctx->m2m_dev->m2m_ops->unlock)
-               m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv);
-
-       poll_wait(file, &src_q->done_wq, wait);
-       poll_wait(file, &dst_q->done_wq, wait);
-
-       if (m2m_ctx->m2m_dev->m2m_ops->lock)
-               m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv);
-
-       spin_lock_irqsave(&src_q->done_lock, flags);
-       if (!list_empty(&src_q->done_list))
-               src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
-                                               done_entry);
-       if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
-                       || src_vb->state == VB2_BUF_STATE_ERROR))
-               rc |= POLLOUT | POLLWRNORM;
-       spin_unlock_irqrestore(&src_q->done_lock, flags);
-
-       spin_lock_irqsave(&dst_q->done_lock, flags);
-       if (!list_empty(&dst_q->done_list))
-               dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
-                                               done_entry);
-       if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
-                       || dst_vb->state == VB2_BUF_STATE_ERROR))
-               rc |= POLLIN | POLLRDNORM;
-       spin_unlock_irqrestore(&dst_q->done_lock, flags);
-
-end:
-       return rc;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
-
-/**
- * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer
- *
- * Call from driver's mmap() function. Will handle mmap() for both queues
- * seamlessly for videobuffer, which will receive normal per-queue offsets and
- * proper videobuf queue pointers. The differentiation is made outside videobuf
- * by adding a predefined offset to buffers from one of the queues and
- * subtracting it before passing it back to videobuf. Only drivers (and
- * thus applications) receive modified offsets.
- */
-int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
-                        struct vm_area_struct *vma)
-{
-       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-       struct vb2_queue *vq;
-
-       if (offset < DST_QUEUE_OFF_BASE) {
-               vq = v4l2_m2m_get_src_vq(m2m_ctx);
-       } else {
-               vq = v4l2_m2m_get_dst_vq(m2m_ctx);
-               vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
-       }
-
-       return vb2_mmap(vq, vma);
-}
-EXPORT_SYMBOL(v4l2_m2m_mmap);
-
-/**
- * v4l2_m2m_init() - initialize per-driver m2m data
- *
- * Usually called from driver's probe() function.
- */
-struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops)
-{
-       struct v4l2_m2m_dev *m2m_dev;
-
-       if (!m2m_ops)
-               return ERR_PTR(-EINVAL);
-
-       BUG_ON(!m2m_ops->device_run);
-       BUG_ON(!m2m_ops->job_abort);
-
-       m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
-       if (!m2m_dev)
-               return ERR_PTR(-ENOMEM);
-
-       m2m_dev->curr_ctx = NULL;
-       m2m_dev->m2m_ops = m2m_ops;
-       INIT_LIST_HEAD(&m2m_dev->job_queue);
-       spin_lock_init(&m2m_dev->job_spinlock);
-
-       return m2m_dev;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_init);
-
-/**
- * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
- *
- * Usually called from driver's remove() function.
- */
-void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
-{
-       kfree(m2m_dev);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_release);
-
-/**
- * v4l2_m2m_ctx_init() - allocate and initialize a m2m context
- * @priv - driver's instance private data
- * @m2m_dev - a previously initialized m2m_dev struct
- * @vq_init - a callback for queue type-specific initialization function to be
- * used for initializing videobuf_queues
- *
- * Usually called from driver's open() function.
- */
-struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
-               void *drv_priv,
-               int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq))
-{
-       struct v4l2_m2m_ctx *m2m_ctx;
-       struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx;
-       int ret;
-
-       m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
-       if (!m2m_ctx)
-               return ERR_PTR(-ENOMEM);
-
-       m2m_ctx->priv = drv_priv;
-       m2m_ctx->m2m_dev = m2m_dev;
-       init_waitqueue_head(&m2m_ctx->finished);
-
-       out_q_ctx = &m2m_ctx->out_q_ctx;
-       cap_q_ctx = &m2m_ctx->cap_q_ctx;
-
-       INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
-       INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
-       spin_lock_init(&out_q_ctx->rdy_spinlock);
-       spin_lock_init(&cap_q_ctx->rdy_spinlock);
-
-       INIT_LIST_HEAD(&m2m_ctx->queue);
-
-       ret = queue_init(drv_priv, &out_q_ctx->q, &cap_q_ctx->q);
-
-       if (ret)
-               goto err;
-
-       return m2m_ctx;
-err:
-       kfree(m2m_ctx);
-       return ERR_PTR(ret);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
-
-/**
- * v4l2_m2m_ctx_release() - release m2m context
- *
- * Usually called from driver's release() function.
- */
-void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
-{
-       struct v4l2_m2m_dev *m2m_dev;
-       unsigned long flags;
-
-       m2m_dev = m2m_ctx->m2m_dev;
-
-       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
-       if (m2m_ctx->job_flags & TRANS_RUNNING) {
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-               m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
-               dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
-               wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING));
-       } else if (m2m_ctx->job_flags & TRANS_QUEUED) {
-               list_del(&m2m_ctx->queue);
-               m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-               dprintk("m2m_ctx: %p had been on queue and was removed\n",
-                       m2m_ctx);
-       } else {
-               /* Do nothing, was not on queue/running */
-               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-       }
-
-       vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
-       vb2_queue_release(&m2m_ctx->out_q_ctx.q);
-
-       kfree(m2m_ctx);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release);
-
-/**
- * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list.
- *
- * Call from buf_queue(), videobuf_queue_ops callback.
- */
-void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb)
-{
-       struct v4l2_m2m_buffer *b = container_of(vb, struct v4l2_m2m_buffer, vb);
-       struct v4l2_m2m_queue_ctx *q_ctx;
-       unsigned long flags;
-
-       q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type);
-       if (!q_ctx)
-               return;
-
-       spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
-       list_add_tail(&b->list, &q_ctx->rdy_queue);
-       q_ctx->num_rdy++;
-       spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
-
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
deleted file mode 100644 (file)
index 9182f81..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * V4L2 sub-device
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- *         Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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.
- *
- * 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 <linux/ioctl.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/videodev2.h>
-#include <linux/export.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
-{
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
-       fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL);
-       if (fh->pad == NULL)
-               return -ENOMEM;
-#endif
-       return 0;
-}
-
-static void subdev_fh_free(struct v4l2_subdev_fh *fh)
-{
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
-       kfree(fh->pad);
-       fh->pad = NULL;
-#endif
-}
-
-static int subdev_open(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
-       struct v4l2_subdev_fh *subdev_fh;
-#if defined(CONFIG_MEDIA_CONTROLLER)
-       struct media_entity *entity = NULL;
-#endif
-       int ret;
-
-       subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL);
-       if (subdev_fh == NULL)
-               return -ENOMEM;
-
-       ret = subdev_fh_init(subdev_fh, sd);
-       if (ret) {
-               kfree(subdev_fh);
-               return ret;
-       }
-
-       v4l2_fh_init(&subdev_fh->vfh, vdev);
-       v4l2_fh_add(&subdev_fh->vfh);
-       file->private_data = &subdev_fh->vfh;
-#if defined(CONFIG_MEDIA_CONTROLLER)
-       if (sd->v4l2_dev->mdev) {
-               entity = media_entity_get(&sd->entity);
-               if (!entity) {
-                       ret = -EBUSY;
-                       goto err;
-               }
-       }
-#endif
-
-       if (sd->internal_ops && sd->internal_ops->open) {
-               ret = sd->internal_ops->open(sd, subdev_fh);
-               if (ret < 0)
-                       goto err;
-       }
-
-       return 0;
-
-err:
-#if defined(CONFIG_MEDIA_CONTROLLER)
-       if (entity)
-               media_entity_put(entity);
-#endif
-       v4l2_fh_del(&subdev_fh->vfh);
-       v4l2_fh_exit(&subdev_fh->vfh);
-       subdev_fh_free(subdev_fh);
-       kfree(subdev_fh);
-
-       return ret;
-}
-
-static int subdev_close(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
-       struct v4l2_fh *vfh = file->private_data;
-       struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
-
-       if (sd->internal_ops && sd->internal_ops->close)
-               sd->internal_ops->close(sd, subdev_fh);
-#if defined(CONFIG_MEDIA_CONTROLLER)
-       if (sd->v4l2_dev->mdev)
-               media_entity_put(&sd->entity);
-#endif
-       v4l2_fh_del(vfh);
-       v4l2_fh_exit(vfh);
-       subdev_fh_free(subdev_fh);
-       kfree(subdev_fh);
-       file->private_data = NULL;
-
-       return 0;
-}
-
-static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
-       struct v4l2_fh *vfh = file->private_data;
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
-       struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
-#endif
-
-       switch (cmd) {
-       case VIDIOC_QUERYCTRL:
-               return v4l2_queryctrl(vfh->ctrl_handler, arg);
-
-       case VIDIOC_QUERYMENU:
-               return v4l2_querymenu(vfh->ctrl_handler, arg);
-
-       case VIDIOC_G_CTRL:
-               return v4l2_g_ctrl(vfh->ctrl_handler, arg);
-
-       case VIDIOC_S_CTRL:
-               return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
-
-       case VIDIOC_G_EXT_CTRLS:
-               return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
-
-       case VIDIOC_S_EXT_CTRLS:
-               return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
-
-       case VIDIOC_TRY_EXT_CTRLS:
-               return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
-
-       case VIDIOC_DQEVENT:
-               if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
-                       return -ENOIOCTLCMD;
-
-               return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
-
-       case VIDIOC_SUBSCRIBE_EVENT:
-               return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
-
-       case VIDIOC_UNSUBSCRIBE_EVENT:
-               return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       case VIDIOC_DBG_G_REGISTER:
-       {
-               struct v4l2_dbg_register *p = arg;
-
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-               return v4l2_subdev_call(sd, core, g_register, p);
-       }
-       case VIDIOC_DBG_S_REGISTER:
-       {
-               struct v4l2_dbg_register *p = arg;
-
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-               return v4l2_subdev_call(sd, core, s_register, p);
-       }
-#endif
-
-       case VIDIOC_LOG_STATUS: {
-               int ret;
-
-               pr_info("%s: =================  START STATUS  =================\n",
-                       sd->name);
-               ret = v4l2_subdev_call(sd, core, log_status);
-               pr_info("%s: ==================  END STATUS  ==================\n",
-                       sd->name);
-               return ret;
-       }
-
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
-       case VIDIOC_SUBDEV_G_FMT: {
-               struct v4l2_subdev_format *format = arg;
-
-               if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
-                   format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
-                       return -EINVAL;
-
-               if (format->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format);
-       }
-
-       case VIDIOC_SUBDEV_S_FMT: {
-               struct v4l2_subdev_format *format = arg;
-
-               if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
-                   format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
-                       return -EINVAL;
-
-               if (format->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format);
-       }
-
-       case VIDIOC_SUBDEV_G_CROP: {
-               struct v4l2_subdev_crop *crop = arg;
-               struct v4l2_subdev_selection sel;
-               int rval;
-
-               if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
-                   crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
-                       return -EINVAL;
-
-               if (crop->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
-               if (rval != -ENOIOCTLCMD)
-                       return rval;
-
-               memset(&sel, 0, sizeof(sel));
-               sel.which = crop->which;
-               sel.pad = crop->pad;
-               sel.target = V4L2_SEL_TGT_CROP;
-
-               rval = v4l2_subdev_call(
-                       sd, pad, get_selection, subdev_fh, &sel);
-
-               crop->rect = sel.r;
-
-               return rval;
-       }
-
-       case VIDIOC_SUBDEV_S_CROP: {
-               struct v4l2_subdev_crop *crop = arg;
-               struct v4l2_subdev_selection sel;
-               int rval;
-
-               if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
-                   crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
-                       return -EINVAL;
-
-               if (crop->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
-               if (rval != -ENOIOCTLCMD)
-                       return rval;
-
-               memset(&sel, 0, sizeof(sel));
-               sel.which = crop->which;
-               sel.pad = crop->pad;
-               sel.target = V4L2_SEL_TGT_CROP;
-               sel.r = crop->rect;
-
-               rval = v4l2_subdev_call(
-                       sd, pad, set_selection, subdev_fh, &sel);
-
-               crop->rect = sel.r;
-
-               return rval;
-       }
-
-       case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
-               struct v4l2_subdev_mbus_code_enum *code = arg;
-
-               if (code->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh,
-                                       code);
-       }
-
-       case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
-               struct v4l2_subdev_frame_size_enum *fse = arg;
-
-               if (fse->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
-                                       fse);
-       }
-
-       case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
-               return v4l2_subdev_call(sd, video, g_frame_interval, arg);
-
-       case VIDIOC_SUBDEV_S_FRAME_INTERVAL:
-               return v4l2_subdev_call(sd, video, s_frame_interval, arg);
-
-       case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
-               struct v4l2_subdev_frame_interval_enum *fie = arg;
-
-               if (fie->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
-                                       fie);
-       }
-
-       case VIDIOC_SUBDEV_G_SELECTION: {
-               struct v4l2_subdev_selection *sel = arg;
-
-               if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
-                   sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
-                       return -EINVAL;
-
-               if (sel->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               return v4l2_subdev_call(
-                       sd, pad, get_selection, subdev_fh, sel);
-       }
-
-       case VIDIOC_SUBDEV_S_SELECTION: {
-               struct v4l2_subdev_selection *sel = arg;
-
-               if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
-                   sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
-                       return -EINVAL;
-
-               if (sel->pad >= sd->entity.num_pads)
-                       return -EINVAL;
-
-               return v4l2_subdev_call(
-                       sd, pad, set_selection, subdev_fh, sel);
-       }
-#endif
-       default:
-               return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
-       }
-
-       return 0;
-}
-
-static long subdev_ioctl(struct file *file, unsigned int cmd,
-       unsigned long arg)
-{
-       return video_usercopy(file, cmd, arg, subdev_do_ioctl);
-}
-
-static unsigned int subdev_poll(struct file *file, poll_table *wait)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
-       struct v4l2_fh *fh = file->private_data;
-
-       if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
-               return POLLERR;
-
-       poll_wait(file, &fh->wait, wait);
-
-       if (v4l2_event_pending(fh))
-               return POLLPRI;
-
-       return 0;
-}
-
-const struct v4l2_file_operations v4l2_subdev_fops = {
-       .owner = THIS_MODULE,
-       .open = subdev_open,
-       .unlocked_ioctl = subdev_ioctl,
-       .release = subdev_close,
-       .poll = subdev_poll,
-};
-
-#ifdef CONFIG_MEDIA_CONTROLLER
-int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
-                                     struct media_link *link,
-                                     struct v4l2_subdev_format *source_fmt,
-                                     struct v4l2_subdev_format *sink_fmt)
-{
-       if (source_fmt->format.width != sink_fmt->format.width
-           || source_fmt->format.height != sink_fmt->format.height
-           || source_fmt->format.code != sink_fmt->format.code)
-               return -EINVAL;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
-
-static int
-v4l2_subdev_link_validate_get_format(struct media_pad *pad,
-                                    struct v4l2_subdev_format *fmt)
-{
-       switch (media_entity_type(pad->entity)) {
-       case MEDIA_ENT_T_V4L2_SUBDEV:
-               fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
-               fmt->pad = pad->index;
-               return v4l2_subdev_call(media_entity_to_v4l2_subdev(
-                                               pad->entity),
-                                       pad, get_fmt, NULL, fmt);
-       default:
-               WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n",
-                    media_entity_type(pad->entity), pad->entity->name);
-               /* Fall through */
-       case MEDIA_ENT_T_DEVNODE_V4L:
-               return -EINVAL;
-       }
-}
-
-int v4l2_subdev_link_validate(struct media_link *link)
-{
-       struct v4l2_subdev *sink;
-       struct v4l2_subdev_format sink_fmt, source_fmt;
-       int rval;
-
-       rval = v4l2_subdev_link_validate_get_format(
-               link->source, &source_fmt);
-       if (rval < 0)
-               return 0;
-
-       rval = v4l2_subdev_link_validate_get_format(
-               link->sink, &sink_fmt);
-       if (rval < 0)
-               return 0;
-
-       sink = media_entity_to_v4l2_subdev(link->sink->entity);
-
-       rval = v4l2_subdev_call(sink, pad, link_validate, link,
-                               &source_fmt, &sink_fmt);
-       if (rval != -ENOIOCTLCMD)
-               return rval;
-
-       return v4l2_subdev_link_validate_default(
-               sink, link, &source_fmt, &sink_fmt);
-}
-EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
-#endif /* CONFIG_MEDIA_CONTROLLER */
-
-void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
-{
-       INIT_LIST_HEAD(&sd->list);
-       BUG_ON(!ops);
-       sd->ops = ops;
-       sd->v4l2_dev = NULL;
-       sd->flags = 0;
-       sd->name[0] = '\0';
-       sd->grp_id = 0;
-       sd->dev_priv = NULL;
-       sd->host_priv = NULL;
-#if defined(CONFIG_MEDIA_CONTROLLER)
-       sd->entity.name = sd->name;
-       sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
-#endif
-}
-EXPORT_SYMBOL(v4l2_subdev_init);
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
deleted file mode 100644 (file)
index bf7a326..0000000
+++ /dev/null
@@ -1,1189 +0,0 @@
-/*
- * generic helper functions for handling video4linux capture buffers
- *
- * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
- *
- * Highly based on video-buf written originally by:
- * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
- * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
- * (c) 2006 Ted Walther and John Sokol
- *
- * 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
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-
-#include <media/videobuf-core.h>
-
-#define MAGIC_BUFFER 0x20070728
-#define MAGIC_CHECK(is, should)                                                \
-       do {                                                            \
-               if (unlikely((is) != (should))) {                       \
-                       printk(KERN_ERR                                 \
-                               "magic mismatch: %x (expected %x)\n",   \
-                                       is, should);                    \
-                       BUG();                                          \
-               }                                                       \
-       } while (0)
-
-static int debug;
-module_param(debug, int, 0644);
-
-MODULE_DESCRIPTION("helper module to manage video4linux buffers");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
-
-#define dprintk(level, fmt, arg...)                                    \
-       do {                                                            \
-               if (debug >= level)                                     \
-                       printk(KERN_DEBUG "vbuf: " fmt, ## arg);        \
-       } while (0)
-
-/* --------------------------------------------------------------------- */
-
-#define CALL(q, f, arg...)                                             \
-       ((q->int_ops->f) ? q->int_ops->f(arg) : 0)
-
-struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q)
-{
-       struct videobuf_buffer *vb;
-
-       BUG_ON(q->msize < sizeof(*vb));
-
-       if (!q->int_ops || !q->int_ops->alloc_vb) {
-               printk(KERN_ERR "No specific ops defined!\n");
-               BUG();
-       }
-
-       vb = q->int_ops->alloc_vb(q->msize);
-       if (NULL != vb) {
-               init_waitqueue_head(&vb->done);
-               vb->magic = MAGIC_BUFFER;
-       }
-
-       return vb;
-}
-EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
-
-static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
-       unsigned long flags;
-       bool rc;
-
-       spin_lock_irqsave(q->irqlock, flags);
-       rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED;
-       spin_unlock_irqrestore(q->irqlock, flags);
-       return rc;
-};
-
-int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb,
-               int non_blocking, int intr)
-{
-       bool is_ext_locked;
-       int ret = 0;
-
-       MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
-
-       if (non_blocking) {
-               if (is_state_active_or_queued(q, vb))
-                       return 0;
-               return -EAGAIN;
-       }
-
-       is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock);
-
-       /* Release vdev lock to prevent this wait from blocking outside access to
-          the device. */
-       if (is_ext_locked)
-               mutex_unlock(q->ext_lock);
-       if (intr)
-               ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb));
-       else
-               wait_event(vb->done, is_state_active_or_queued(q, vb));
-       /* Relock */
-       if (is_ext_locked)
-               mutex_lock(q->ext_lock);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_waiton);
-
-int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
-                   struct v4l2_framebuffer *fbuf)
-{
-       MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       return CALL(q, iolock, q, vb, fbuf);
-}
-EXPORT_SYMBOL_GPL(videobuf_iolock);
-
-void *videobuf_queue_to_vaddr(struct videobuf_queue *q,
-                             struct videobuf_buffer *buf)
-{
-       if (q->int_ops->vaddr)
-               return q->int_ops->vaddr(buf);
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr);
-
-/* --------------------------------------------------------------------- */
-
-
-void videobuf_queue_core_init(struct videobuf_queue *q,
-                        const struct videobuf_queue_ops *ops,
-                        struct device *dev,
-                        spinlock_t *irqlock,
-                        enum v4l2_buf_type type,
-                        enum v4l2_field field,
-                        unsigned int msize,
-                        void *priv,
-                        struct videobuf_qtype_ops *int_ops,
-                        struct mutex *ext_lock)
-{
-       BUG_ON(!q);
-       memset(q, 0, sizeof(*q));
-       q->irqlock   = irqlock;
-       q->ext_lock  = ext_lock;
-       q->dev       = dev;
-       q->type      = type;
-       q->field     = field;
-       q->msize     = msize;
-       q->ops       = ops;
-       q->priv_data = priv;
-       q->int_ops   = int_ops;
-
-       /* All buffer operations are mandatory */
-       BUG_ON(!q->ops->buf_setup);
-       BUG_ON(!q->ops->buf_prepare);
-       BUG_ON(!q->ops->buf_queue);
-       BUG_ON(!q->ops->buf_release);
-
-       /* Lock is mandatory for queue_cancel to work */
-       BUG_ON(!irqlock);
-
-       /* Having implementations for abstract methods are mandatory */
-       BUG_ON(!q->int_ops);
-
-       mutex_init(&q->vb_lock);
-       init_waitqueue_head(&q->wait);
-       INIT_LIST_HEAD(&q->stream);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
-
-/* Locking: Only usage in bttv unsafe find way to remove */
-int videobuf_queue_is_busy(struct videobuf_queue *q)
-{
-       int i;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       if (q->streaming) {
-               dprintk(1, "busy: streaming active\n");
-               return 1;
-       }
-       if (q->reading) {
-               dprintk(1, "busy: pending read #1\n");
-               return 1;
-       }
-       if (q->read_buf) {
-               dprintk(1, "busy: pending read #2\n");
-               return 1;
-       }
-       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-               if (NULL == q->bufs[i])
-                       continue;
-               if (q->bufs[i]->map) {
-                       dprintk(1, "busy: buffer #%d mapped\n", i);
-                       return 1;
-               }
-               if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
-                       dprintk(1, "busy: buffer #%d queued\n", i);
-                       return 1;
-               }
-               if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {
-                       dprintk(1, "busy: buffer #%d avtive\n", i);
-                       return 1;
-               }
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);
-
-/**
- * __videobuf_free() - free all the buffers and their control structures
- *
- * This function can only be called if streaming/reading is off, i.e. no buffers
- * are under control of the driver.
- */
-/* Locking: Caller holds q->vb_lock */
-static int __videobuf_free(struct videobuf_queue *q)
-{
-       int i;
-
-       dprintk(1, "%s\n", __func__);
-       if (!q)
-               return 0;
-
-       if (q->streaming || q->reading) {
-               dprintk(1, "Cannot free buffers when streaming or reading\n");
-               return -EBUSY;
-       }
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       for (i = 0; i < VIDEO_MAX_FRAME; i++)
-               if (q->bufs[i] && q->bufs[i]->map) {
-                       dprintk(1, "Cannot free mmapped buffers\n");
-                       return -EBUSY;
-               }
-
-       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-               if (NULL == q->bufs[i])
-                       continue;
-               q->ops->buf_release(q, q->bufs[i]);
-               kfree(q->bufs[i]);
-               q->bufs[i] = NULL;
-       }
-
-       return 0;
-}
-
-/* Locking: Caller holds q->vb_lock */
-void videobuf_queue_cancel(struct videobuf_queue *q)
-{
-       unsigned long flags = 0;
-       int i;
-
-       q->streaming = 0;
-       q->reading  = 0;
-       wake_up_interruptible_sync(&q->wait);
-
-       /* remove queued buffers from list */
-       spin_lock_irqsave(q->irqlock, flags);
-       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-               if (NULL == q->bufs[i])
-                       continue;
-               if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
-                       list_del(&q->bufs[i]->queue);
-                       q->bufs[i]->state = VIDEOBUF_ERROR;
-                       wake_up_all(&q->bufs[i]->done);
-               }
-       }
-       spin_unlock_irqrestore(q->irqlock, flags);
-
-       /* free all buffers + clear queue */
-       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-               if (NULL == q->bufs[i])
-                       continue;
-               q->ops->buf_release(q, q->bufs[i]);
-       }
-       INIT_LIST_HEAD(&q->stream);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
-
-/* --------------------------------------------------------------------- */
-
-/* Locking: Caller holds q->vb_lock */
-enum v4l2_field videobuf_next_field(struct videobuf_queue *q)
-{
-       enum v4l2_field field = q->field;
-
-       BUG_ON(V4L2_FIELD_ANY == field);
-
-       if (V4L2_FIELD_ALTERNATE == field) {
-               if (V4L2_FIELD_TOP == q->last) {
-                       field   = V4L2_FIELD_BOTTOM;
-                       q->last = V4L2_FIELD_BOTTOM;
-               } else {
-                       field   = V4L2_FIELD_TOP;
-                       q->last = V4L2_FIELD_TOP;
-               }
-       }
-       return field;
-}
-EXPORT_SYMBOL_GPL(videobuf_next_field);
-
-/* Locking: Caller holds q->vb_lock */
-static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
-                           struct videobuf_buffer *vb, enum v4l2_buf_type type)
-{
-       MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       b->index    = vb->i;
-       b->type     = type;
-
-       b->memory   = vb->memory;
-       switch (b->memory) {
-       case V4L2_MEMORY_MMAP:
-               b->m.offset  = vb->boff;
-               b->length    = vb->bsize;
-               break;
-       case V4L2_MEMORY_USERPTR:
-               b->m.userptr = vb->baddr;
-               b->length    = vb->bsize;
-               break;
-       case V4L2_MEMORY_OVERLAY:
-               b->m.offset  = vb->boff;
-               break;
-       }
-
-       b->flags    = 0;
-       if (vb->map)
-               b->flags |= V4L2_BUF_FLAG_MAPPED;
-
-       switch (vb->state) {
-       case VIDEOBUF_PREPARED:
-       case VIDEOBUF_QUEUED:
-       case VIDEOBUF_ACTIVE:
-               b->flags |= V4L2_BUF_FLAG_QUEUED;
-               break;
-       case VIDEOBUF_ERROR:
-               b->flags |= V4L2_BUF_FLAG_ERROR;
-               /* fall through */
-       case VIDEOBUF_DONE:
-               b->flags |= V4L2_BUF_FLAG_DONE;
-               break;
-       case VIDEOBUF_NEEDS_INIT:
-       case VIDEOBUF_IDLE:
-               /* nothing */
-               break;
-       }
-
-       b->field     = vb->field;
-       b->timestamp = vb->ts;
-       b->bytesused = vb->size;
-       b->sequence  = vb->field_count >> 1;
-}
-
-int videobuf_mmap_free(struct videobuf_queue *q)
-{
-       int ret;
-       videobuf_queue_lock(q);
-       ret = __videobuf_free(q);
-       videobuf_queue_unlock(q);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_mmap_free);
-
-/* Locking: Caller holds q->vb_lock */
-int __videobuf_mmap_setup(struct videobuf_queue *q,
-                       unsigned int bcount, unsigned int bsize,
-                       enum v4l2_memory memory)
-{
-       unsigned int i;
-       int err;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       err = __videobuf_free(q);
-       if (0 != err)
-               return err;
-
-       /* Allocate and initialize buffers */
-       for (i = 0; i < bcount; i++) {
-               q->bufs[i] = videobuf_alloc_vb(q);
-
-               if (NULL == q->bufs[i])
-                       break;
-
-               q->bufs[i]->i      = i;
-               q->bufs[i]->memory = memory;
-               q->bufs[i]->bsize  = bsize;
-               switch (memory) {
-               case V4L2_MEMORY_MMAP:
-                       q->bufs[i]->boff = PAGE_ALIGN(bsize) * i;
-                       break;
-               case V4L2_MEMORY_USERPTR:
-               case V4L2_MEMORY_OVERLAY:
-                       /* nothing */
-                       break;
-               }
-       }
-
-       if (!i)
-               return -ENOMEM;
-
-       dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize);
-
-       return i;
-}
-EXPORT_SYMBOL_GPL(__videobuf_mmap_setup);
-
-int videobuf_mmap_setup(struct videobuf_queue *q,
-                       unsigned int bcount, unsigned int bsize,
-                       enum v4l2_memory memory)
-{
-       int ret;
-       videobuf_queue_lock(q);
-       ret = __videobuf_mmap_setup(q, bcount, bsize, memory);
-       videobuf_queue_unlock(q);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
-
-int videobuf_reqbufs(struct videobuf_queue *q,
-                struct v4l2_requestbuffers *req)
-{
-       unsigned int size, count;
-       int retval;
-
-       if (req->count < 1) {
-               dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
-               return -EINVAL;
-       }
-
-       if (req->memory != V4L2_MEMORY_MMAP     &&
-           req->memory != V4L2_MEMORY_USERPTR  &&
-           req->memory != V4L2_MEMORY_OVERLAY) {
-               dprintk(1, "reqbufs: memory type invalid\n");
-               return -EINVAL;
-       }
-
-       videobuf_queue_lock(q);
-       if (req->type != q->type) {
-               dprintk(1, "reqbufs: queue type invalid\n");
-               retval = -EINVAL;
-               goto done;
-       }
-
-       if (q->streaming) {
-               dprintk(1, "reqbufs: streaming already exists\n");
-               retval = -EBUSY;
-               goto done;
-       }
-       if (!list_empty(&q->stream)) {
-               dprintk(1, "reqbufs: stream running\n");
-               retval = -EBUSY;
-               goto done;
-       }
-
-       count = req->count;
-       if (count > VIDEO_MAX_FRAME)
-               count = VIDEO_MAX_FRAME;
-       size = 0;
-       q->ops->buf_setup(q, &count, &size);
-       dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n",
-               count, size,
-               (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT));
-
-       retval = __videobuf_mmap_setup(q, count, size, req->memory);
-       if (retval < 0) {
-               dprintk(1, "reqbufs: mmap setup returned %d\n", retval);
-               goto done;
-       }
-
-       req->count = retval;
-       retval = 0;
-
- done:
-       videobuf_queue_unlock(q);
-       return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_reqbufs);
-
-int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
-{
-       int ret = -EINVAL;
-
-       videobuf_queue_lock(q);
-       if (unlikely(b->type != q->type)) {
-               dprintk(1, "querybuf: Wrong type.\n");
-               goto done;
-       }
-       if (unlikely(b->index >= VIDEO_MAX_FRAME)) {
-               dprintk(1, "querybuf: index out of range.\n");
-               goto done;
-       }
-       if (unlikely(NULL == q->bufs[b->index])) {
-               dprintk(1, "querybuf: buffer is null.\n");
-               goto done;
-       }
-
-       videobuf_status(q, b, q->bufs[b->index], q->type);
-
-       ret = 0;
-done:
-       videobuf_queue_unlock(q);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_querybuf);
-
-int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
-{
-       struct videobuf_buffer *buf;
-       enum v4l2_field field;
-       unsigned long flags = 0;
-       int retval;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       if (b->memory == V4L2_MEMORY_MMAP)
-               down_read(&current->mm->mmap_sem);
-
-       videobuf_queue_lock(q);
-       retval = -EBUSY;
-       if (q->reading) {
-               dprintk(1, "qbuf: Reading running...\n");
-               goto done;
-       }
-       retval = -EINVAL;
-       if (b->type != q->type) {
-               dprintk(1, "qbuf: Wrong type.\n");
-               goto done;
-       }
-       if (b->index >= VIDEO_MAX_FRAME) {
-               dprintk(1, "qbuf: index out of range.\n");
-               goto done;
-       }
-       buf = q->bufs[b->index];
-       if (NULL == buf) {
-               dprintk(1, "qbuf: buffer is null.\n");
-               goto done;
-       }
-       MAGIC_CHECK(buf->magic, MAGIC_BUFFER);
-       if (buf->memory != b->memory) {
-               dprintk(1, "qbuf: memory type is wrong.\n");
-               goto done;
-       }
-       if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) {
-               dprintk(1, "qbuf: buffer is already queued or active.\n");
-               goto done;
-       }
-
-       switch (b->memory) {
-       case V4L2_MEMORY_MMAP:
-               if (0 == buf->baddr) {
-                       dprintk(1, "qbuf: mmap requested "
-                                  "but buffer addr is zero!\n");
-                       goto done;
-               }
-               if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT
-                   || q->type == V4L2_BUF_TYPE_VBI_OUTPUT
-                   || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
-                       buf->size = b->bytesused;
-                       buf->field = b->field;
-                       buf->ts = b->timestamp;
-               }
-               break;
-       case V4L2_MEMORY_USERPTR:
-               if (b->length < buf->bsize) {
-                       dprintk(1, "qbuf: buffer length is not enough\n");
-                       goto done;
-               }
-               if (VIDEOBUF_NEEDS_INIT != buf->state &&
-                   buf->baddr != b->m.userptr)
-                       q->ops->buf_release(q, buf);
-               buf->baddr = b->m.userptr;
-               break;
-       case V4L2_MEMORY_OVERLAY:
-               buf->boff = b->m.offset;
-               break;
-       default:
-               dprintk(1, "qbuf: wrong memory type\n");
-               goto done;
-       }
-
-       dprintk(1, "qbuf: requesting next field\n");
-       field = videobuf_next_field(q);
-       retval = q->ops->buf_prepare(q, buf, field);
-       if (0 != retval) {
-               dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);
-               goto done;
-       }
-
-       list_add_tail(&buf->stream, &q->stream);
-       if (q->streaming) {
-               spin_lock_irqsave(q->irqlock, flags);
-               q->ops->buf_queue(q, buf);
-               spin_unlock_irqrestore(q->irqlock, flags);
-       }
-       dprintk(1, "qbuf: succeeded\n");
-       retval = 0;
-       wake_up_interruptible_sync(&q->wait);
-
-done:
-       videobuf_queue_unlock(q);
-
-       if (b->memory == V4L2_MEMORY_MMAP)
-               up_read(&current->mm->mmap_sem);
-
-       return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_qbuf);
-
-/* Locking: Caller holds q->vb_lock */
-static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
-{
-       int retval;
-
-checks:
-       if (!q->streaming) {
-               dprintk(1, "next_buffer: Not streaming\n");
-               retval = -EINVAL;
-               goto done;
-       }
-
-       if (list_empty(&q->stream)) {
-               if (noblock) {
-                       retval = -EAGAIN;
-                       dprintk(2, "next_buffer: no buffers to dequeue\n");
-                       goto done;
-               } else {
-                       dprintk(2, "next_buffer: waiting on buffer\n");
-
-                       /* Drop lock to avoid deadlock with qbuf */
-                       videobuf_queue_unlock(q);
-
-                       /* Checking list_empty and streaming is safe without
-                        * locks because we goto checks to validate while
-                        * holding locks before proceeding */
-                       retval = wait_event_interruptible(q->wait,
-                               !list_empty(&q->stream) || !q->streaming);
-                       videobuf_queue_lock(q);
-
-                       if (retval)
-                               goto done;
-
-                       goto checks;
-               }
-       }
-
-       retval = 0;
-
-done:
-       return retval;
-}
-
-/* Locking: Caller holds q->vb_lock */
-static int stream_next_buffer(struct videobuf_queue *q,
-                       struct videobuf_buffer **vb, int nonblocking)
-{
-       int retval;
-       struct videobuf_buffer *buf = NULL;
-
-       retval = stream_next_buffer_check_queue(q, nonblocking);
-       if (retval)
-               goto done;
-
-       buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-       retval = videobuf_waiton(q, buf, nonblocking, 1);
-       if (retval < 0)
-               goto done;
-
-       *vb = buf;
-done:
-       return retval;
-}
-
-int videobuf_dqbuf(struct videobuf_queue *q,
-                  struct v4l2_buffer *b, int nonblocking)
-{
-       struct videobuf_buffer *buf = NULL;
-       int retval;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       memset(b, 0, sizeof(*b));
-       videobuf_queue_lock(q);
-
-       retval = stream_next_buffer(q, &buf, nonblocking);
-       if (retval < 0) {
-               dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
-               goto done;
-       }
-
-       switch (buf->state) {
-       case VIDEOBUF_ERROR:
-               dprintk(1, "dqbuf: state is error\n");
-               break;
-       case VIDEOBUF_DONE:
-               dprintk(1, "dqbuf: state is done\n");
-               break;
-       default:
-               dprintk(1, "dqbuf: state invalid\n");
-               retval = -EINVAL;
-               goto done;
-       }
-       CALL(q, sync, q, buf);
-       videobuf_status(q, b, buf, q->type);
-       list_del(&buf->stream);
-       buf->state = VIDEOBUF_IDLE;
-       b->flags &= ~V4L2_BUF_FLAG_DONE;
-done:
-       videobuf_queue_unlock(q);
-       return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_dqbuf);
-
-int videobuf_streamon(struct videobuf_queue *q)
-{
-       struct videobuf_buffer *buf;
-       unsigned long flags = 0;
-       int retval;
-
-       videobuf_queue_lock(q);
-       retval = -EBUSY;
-       if (q->reading)
-               goto done;
-       retval = 0;
-       if (q->streaming)
-               goto done;
-       q->streaming = 1;
-       spin_lock_irqsave(q->irqlock, flags);
-       list_for_each_entry(buf, &q->stream, stream)
-               if (buf->state == VIDEOBUF_PREPARED)
-                       q->ops->buf_queue(q, buf);
-       spin_unlock_irqrestore(q->irqlock, flags);
-
-       wake_up_interruptible_sync(&q->wait);
-done:
-       videobuf_queue_unlock(q);
-       return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_streamon);
-
-/* Locking: Caller holds q->vb_lock */
-static int __videobuf_streamoff(struct videobuf_queue *q)
-{
-       if (!q->streaming)
-               return -EINVAL;
-
-       videobuf_queue_cancel(q);
-
-       return 0;
-}
-
-int videobuf_streamoff(struct videobuf_queue *q)
-{
-       int retval;
-
-       videobuf_queue_lock(q);
-       retval = __videobuf_streamoff(q);
-       videobuf_queue_unlock(q);
-
-       return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_streamoff);
-
-/* Locking: Caller holds q->vb_lock */
-static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
-                                     char __user *data,
-                                     size_t count, loff_t *ppos)
-{
-       enum v4l2_field field;
-       unsigned long flags = 0;
-       int retval;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       /* setup stuff */
-       q->read_buf = videobuf_alloc_vb(q);
-       if (NULL == q->read_buf)
-               return -ENOMEM;
-
-       q->read_buf->memory = V4L2_MEMORY_USERPTR;
-       q->read_buf->baddr  = (unsigned long)data;
-       q->read_buf->bsize  = count;
-
-       field = videobuf_next_field(q);
-       retval = q->ops->buf_prepare(q, q->read_buf, field);
-       if (0 != retval)
-               goto done;
-
-       /* start capture & wait */
-       spin_lock_irqsave(q->irqlock, flags);
-       q->ops->buf_queue(q, q->read_buf);
-       spin_unlock_irqrestore(q->irqlock, flags);
-       retval = videobuf_waiton(q, q->read_buf, 0, 0);
-       if (0 == retval) {
-               CALL(q, sync, q, q->read_buf);
-               if (VIDEOBUF_ERROR == q->read_buf->state)
-                       retval = -EIO;
-               else
-                       retval = q->read_buf->size;
-       }
-
-done:
-       /* cleanup */
-       q->ops->buf_release(q, q->read_buf);
-       kfree(q->read_buf);
-       q->read_buf = NULL;
-       return retval;
-}
-
-static int __videobuf_copy_to_user(struct videobuf_queue *q,
-                                  struct videobuf_buffer *buf,
-                                  char __user *data, size_t count,
-                                  int nonblocking)
-{
-       void *vaddr = CALL(q, vaddr, buf);
-
-       /* copy to userspace */
-       if (count > buf->size - q->read_off)
-               count = buf->size - q->read_off;
-
-       if (copy_to_user(data, vaddr + q->read_off, count))
-               return -EFAULT;
-
-       return count;
-}
-
-static int __videobuf_copy_stream(struct videobuf_queue *q,
-                                 struct videobuf_buffer *buf,
-                                 char __user *data, size_t count, size_t pos,
-                                 int vbihack, int nonblocking)
-{
-       unsigned int *fc = CALL(q, vaddr, buf);
-
-       if (vbihack) {
-               /* dirty, undocumented hack -- pass the frame counter
-                       * within the last four bytes of each vbi data block.
-                       * We need that one to maintain backward compatibility
-                       * to all vbi decoding software out there ... */
-               fc += (buf->size >> 2) - 1;
-               *fc = buf->field_count >> 1;
-               dprintk(1, "vbihack: %d\n", *fc);
-       }
-
-       /* copy stuff using the common method */
-       count = __videobuf_copy_to_user(q, buf, data, count, nonblocking);
-
-       if ((count == -EFAULT) && (pos == 0))
-               return -EFAULT;
-
-       return count;
-}
-
-ssize_t videobuf_read_one(struct videobuf_queue *q,
-                         char __user *data, size_t count, loff_t *ppos,
-                         int nonblocking)
-{
-       enum v4l2_field field;
-       unsigned long flags = 0;
-       unsigned size = 0, nbufs = 1;
-       int retval;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       videobuf_queue_lock(q);
-
-       q->ops->buf_setup(q, &nbufs, &size);
-
-       if (NULL == q->read_buf  &&
-           count >= size        &&
-           !nonblocking) {
-               retval = videobuf_read_zerocopy(q, data, count, ppos);
-               if (retval >= 0  ||  retval == -EIO)
-                       /* ok, all done */
-                       goto done;
-               /* fallback to kernel bounce buffer on failures */
-       }
-
-       if (NULL == q->read_buf) {
-               /* need to capture a new frame */
-               retval = -ENOMEM;
-               q->read_buf = videobuf_alloc_vb(q);
-
-               dprintk(1, "video alloc=0x%p\n", q->read_buf);
-               if (NULL == q->read_buf)
-                       goto done;
-               q->read_buf->memory = V4L2_MEMORY_USERPTR;
-               q->read_buf->bsize = count; /* preferred size */
-               field = videobuf_next_field(q);
-               retval = q->ops->buf_prepare(q, q->read_buf, field);
-
-               if (0 != retval) {
-                       kfree(q->read_buf);
-                       q->read_buf = NULL;
-                       goto done;
-               }
-
-               spin_lock_irqsave(q->irqlock, flags);
-               q->ops->buf_queue(q, q->read_buf);
-               spin_unlock_irqrestore(q->irqlock, flags);
-
-               q->read_off = 0;
-       }
-
-       /* wait until capture is done */
-       retval = videobuf_waiton(q, q->read_buf, nonblocking, 1);
-       if (0 != retval)
-               goto done;
-
-       CALL(q, sync, q, q->read_buf);
-
-       if (VIDEOBUF_ERROR == q->read_buf->state) {
-               /* catch I/O errors */
-               q->ops->buf_release(q, q->read_buf);
-               kfree(q->read_buf);
-               q->read_buf = NULL;
-               retval = -EIO;
-               goto done;
-       }
-
-       /* Copy to userspace */
-       retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking);
-       if (retval < 0)
-               goto done;
-
-       q->read_off += retval;
-       if (q->read_off == q->read_buf->size) {
-               /* all data copied, cleanup */
-               q->ops->buf_release(q, q->read_buf);
-               kfree(q->read_buf);
-               q->read_buf = NULL;
-       }
-
-done:
-       videobuf_queue_unlock(q);
-       return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_read_one);
-
-/* Locking: Caller holds q->vb_lock */
-static int __videobuf_read_start(struct videobuf_queue *q)
-{
-       enum v4l2_field field;
-       unsigned long flags = 0;
-       unsigned int count = 0, size = 0;
-       int err, i;
-
-       q->ops->buf_setup(q, &count, &size);
-       if (count < 2)
-               count = 2;
-       if (count > VIDEO_MAX_FRAME)
-               count = VIDEO_MAX_FRAME;
-       size = PAGE_ALIGN(size);
-
-       err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR);
-       if (err < 0)
-               return err;
-
-       count = err;
-
-       for (i = 0; i < count; i++) {
-               field = videobuf_next_field(q);
-               err = q->ops->buf_prepare(q, q->bufs[i], field);
-               if (err)
-                       return err;
-               list_add_tail(&q->bufs[i]->stream, &q->stream);
-       }
-       spin_lock_irqsave(q->irqlock, flags);
-       for (i = 0; i < count; i++)
-               q->ops->buf_queue(q, q->bufs[i]);
-       spin_unlock_irqrestore(q->irqlock, flags);
-       q->reading = 1;
-       return 0;
-}
-
-static void __videobuf_read_stop(struct videobuf_queue *q)
-{
-       int i;
-
-       videobuf_queue_cancel(q);
-       __videobuf_free(q);
-       INIT_LIST_HEAD(&q->stream);
-       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-               if (NULL == q->bufs[i])
-                       continue;
-               kfree(q->bufs[i]);
-               q->bufs[i] = NULL;
-       }
-       q->read_buf = NULL;
-}
-
-int videobuf_read_start(struct videobuf_queue *q)
-{
-       int rc;
-
-       videobuf_queue_lock(q);
-       rc = __videobuf_read_start(q);
-       videobuf_queue_unlock(q);
-
-       return rc;
-}
-EXPORT_SYMBOL_GPL(videobuf_read_start);
-
-void videobuf_read_stop(struct videobuf_queue *q)
-{
-       videobuf_queue_lock(q);
-       __videobuf_read_stop(q);
-       videobuf_queue_unlock(q);
-}
-EXPORT_SYMBOL_GPL(videobuf_read_stop);
-
-void videobuf_stop(struct videobuf_queue *q)
-{
-       videobuf_queue_lock(q);
-
-       if (q->streaming)
-               __videobuf_streamoff(q);
-
-       if (q->reading)
-               __videobuf_read_stop(q);
-
-       videobuf_queue_unlock(q);
-}
-EXPORT_SYMBOL_GPL(videobuf_stop);
-
-ssize_t videobuf_read_stream(struct videobuf_queue *q,
-                            char __user *data, size_t count, loff_t *ppos,
-                            int vbihack, int nonblocking)
-{
-       int rc, retval;
-       unsigned long flags = 0;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       dprintk(2, "%s\n", __func__);
-       videobuf_queue_lock(q);
-       retval = -EBUSY;
-       if (q->streaming)
-               goto done;
-       if (!q->reading) {
-               retval = __videobuf_read_start(q);
-               if (retval < 0)
-                       goto done;
-       }
-
-       retval = 0;
-       while (count > 0) {
-               /* get / wait for data */
-               if (NULL == q->read_buf) {
-                       q->read_buf = list_entry(q->stream.next,
-                                                struct videobuf_buffer,
-                                                stream);
-                       list_del(&q->read_buf->stream);
-                       q->read_off = 0;
-               }
-               rc = videobuf_waiton(q, q->read_buf, nonblocking, 1);
-               if (rc < 0) {
-                       if (0 == retval)
-                               retval = rc;
-                       break;
-               }
-
-               if (q->read_buf->state == VIDEOBUF_DONE) {
-                       rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count,
-                                       retval, vbihack, nonblocking);
-                       if (rc < 0) {
-                               retval = rc;
-                               break;
-                       }
-                       retval      += rc;
-                       count       -= rc;
-                       q->read_off += rc;
-               } else {
-                       /* some error */
-                       q->read_off = q->read_buf->size;
-                       if (0 == retval)
-                               retval = -EIO;
-               }
-
-               /* requeue buffer when done with copying */
-               if (q->read_off == q->read_buf->size) {
-                       list_add_tail(&q->read_buf->stream,
-                                     &q->stream);
-                       spin_lock_irqsave(q->irqlock, flags);
-                       q->ops->buf_queue(q, q->read_buf);
-                       spin_unlock_irqrestore(q->irqlock, flags);
-                       q->read_buf = NULL;
-               }
-               if (retval < 0)
-                       break;
-       }
-
-done:
-       videobuf_queue_unlock(q);
-       return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_read_stream);
-
-unsigned int videobuf_poll_stream(struct file *file,
-                                 struct videobuf_queue *q,
-                                 poll_table *wait)
-{
-       unsigned long req_events = poll_requested_events(wait);
-       struct videobuf_buffer *buf = NULL;
-       unsigned int rc = 0;
-
-       videobuf_queue_lock(q);
-       if (q->streaming) {
-               if (!list_empty(&q->stream))
-                       buf = list_entry(q->stream.next,
-                                        struct videobuf_buffer, stream);
-       } else if (req_events & (POLLIN | POLLRDNORM)) {
-               if (!q->reading)
-                       __videobuf_read_start(q);
-               if (!q->reading) {
-                       rc = POLLERR;
-               } else if (NULL == q->read_buf) {
-                       q->read_buf = list_entry(q->stream.next,
-                                                struct videobuf_buffer,
-                                                stream);
-                       list_del(&q->read_buf->stream);
-                       q->read_off = 0;
-               }
-               buf = q->read_buf;
-       }
-       if (!buf)
-               rc = POLLERR;
-
-       if (0 == rc) {
-               poll_wait(file, &buf->done, wait);
-               if (buf->state == VIDEOBUF_DONE ||
-                   buf->state == VIDEOBUF_ERROR) {
-                       switch (q->type) {
-                       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-                       case V4L2_BUF_TYPE_VBI_OUTPUT:
-                       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-                               rc = POLLOUT | POLLWRNORM;
-                               break;
-                       default:
-                               rc = POLLIN | POLLRDNORM;
-                               break;
-                       }
-               }
-       }
-       videobuf_queue_unlock(q);
-       return rc;
-}
-EXPORT_SYMBOL_GPL(videobuf_poll_stream);
-
-int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma)
-{
-       int rc = -EINVAL;
-       int i;
-
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
-               dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n");
-               return -EINVAL;
-       }
-
-       videobuf_queue_lock(q);
-       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-               struct videobuf_buffer *buf = q->bufs[i];
-
-               if (buf && buf->memory == V4L2_MEMORY_MMAP &&
-                               buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) {
-                       rc = CALL(q, mmap_mapper, q, buf, vma);
-                       break;
-               }
-       }
-       videobuf_queue_unlock(q);
-
-       return rc;
-}
-EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
deleted file mode 100644 (file)
index 3a43ba0..0000000
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * helper functions for physically contiguous capture buffers
- *
- * The functions support hardware lacking scatter gather support
- * (i.e. the buffers must be linear in physical memory)
- *
- * Copyright (c) 2008 Magnus Damm
- *
- * Based on videobuf-vmalloc.c,
- * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
- *
- * 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
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/dma-mapping.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <media/videobuf-dma-contig.h>
-
-struct videobuf_dma_contig_memory {
-       u32 magic;
-       void *vaddr;
-       dma_addr_t dma_handle;
-       bool cached;
-       unsigned long size;
-};
-
-#define MAGIC_DC_MEM 0x0733ac61
-#define MAGIC_CHECK(is, should)                                                    \
-       if (unlikely((is) != (should))) {                                   \
-               pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
-               BUG();                                                      \
-       }
-
-static int __videobuf_dc_alloc(struct device *dev,
-                              struct videobuf_dma_contig_memory *mem,
-                              unsigned long size, gfp_t flags)
-{
-       mem->size = size;
-       if (mem->cached) {
-               mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA);
-               if (mem->vaddr) {
-                       int err;
-
-                       mem->dma_handle = dma_map_single(dev, mem->vaddr,
-                                                        mem->size,
-                                                        DMA_FROM_DEVICE);
-                       err = dma_mapping_error(dev, mem->dma_handle);
-                       if (err) {
-                               dev_err(dev, "dma_map_single failed\n");
-
-                               free_pages_exact(mem->vaddr, mem->size);
-                               mem->vaddr = NULL;
-                               return err;
-                       }
-               }
-       } else
-               mem->vaddr = dma_alloc_coherent(dev, mem->size,
-                                               &mem->dma_handle, flags);
-
-       if (!mem->vaddr) {
-               dev_err(dev, "memory alloc size %ld failed\n", mem->size);
-               return -ENOMEM;
-       }
-
-       dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
-
-       return 0;
-}
-
-static void __videobuf_dc_free(struct device *dev,
-                              struct videobuf_dma_contig_memory *mem)
-{
-       if (mem->cached) {
-               if (!mem->vaddr)
-                       return;
-               dma_unmap_single(dev, mem->dma_handle, mem->size,
-                                DMA_FROM_DEVICE);
-               free_pages_exact(mem->vaddr, mem->size);
-       } else
-               dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
-
-       mem->vaddr = NULL;
-}
-
-static void videobuf_vm_open(struct vm_area_struct *vma)
-{
-       struct videobuf_mapping *map = vma->vm_private_data;
-
-       dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
-               map, map->count, vma->vm_start, vma->vm_end);
-
-       map->count++;
-}
-
-static void videobuf_vm_close(struct vm_area_struct *vma)
-{
-       struct videobuf_mapping *map = vma->vm_private_data;
-       struct videobuf_queue *q = map->q;
-       int i;
-
-       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) {
-               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)
-                       videobuf_queue_cancel(q);
-
-               for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-                       if (NULL == q->bufs[i])
-                               continue;
-
-                       if (q->bufs[i]->map != map)
-                               continue;
-
-                       mem = q->bufs[i]->priv;
-                       if (mem) {
-                               /* This callback is called only if kernel has
-                                  allocated memory and this memory is mmapped.
-                                  In this case, memory should be freed,
-                                  in order to do memory unmap.
-                                */
-
-                               MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-                               /* vfree is not atomic - can't be
-                                  called with IRQ's disabled
-                                */
-                               dev_dbg(q->dev, "buf[%d] freeing %p\n",
-                                       i, mem->vaddr);
-
-                               __videobuf_dc_free(q->dev, mem);
-                               mem->vaddr = NULL;
-                       }
-
-                       q->bufs[i]->map = NULL;
-                       q->bufs[i]->baddr = 0;
-               }
-
-               kfree(map);
-
-               videobuf_queue_unlock(q);
-       }
-}
-
-static const struct vm_operations_struct videobuf_vm_ops = {
-       .open   = videobuf_vm_open,
-       .close  = videobuf_vm_close,
-};
-
-/**
- * videobuf_dma_contig_user_put() - reset pointer to user space buffer
- * @mem: per-buffer private videobuf-dma-contig data
- *
- * This function resets the user space pointer
- */
-static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
-{
-       mem->dma_handle = 0;
-       mem->size = 0;
-}
-
-/**
- * videobuf_dma_contig_user_get() - setup user space memory pointer
- * @mem: per-buffer private videobuf-dma-contig data
- * @vb: video buffer to map
- *
- * This function validates and sets up a pointer to user space memory.
- * Only physically contiguous pfn-mapped memory is accepted.
- *
- * Returns 0 if successful.
- */
-static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
-                                       struct videobuf_buffer *vb)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-       unsigned long prev_pfn, this_pfn;
-       unsigned long pages_done, user_address;
-       unsigned int offset;
-       int ret;
-
-       offset = vb->baddr & ~PAGE_MASK;
-       mem->size = PAGE_ALIGN(vb->size + offset);
-       ret = -EINVAL;
-
-       down_read(&mm->mmap_sem);
-
-       vma = find_vma(mm, vb->baddr);
-       if (!vma)
-               goto out_up;
-
-       if ((vb->baddr + mem->size) > vma->vm_end)
-               goto out_up;
-
-       pages_done = 0;
-       prev_pfn = 0; /* kill warning */
-       user_address = vb->baddr;
-
-       while (pages_done < (mem->size >> PAGE_SHIFT)) {
-               ret = follow_pfn(vma, user_address, &this_pfn);
-               if (ret)
-                       break;
-
-               if (pages_done == 0)
-                       mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
-               else if (this_pfn != (prev_pfn + 1))
-                       ret = -EFAULT;
-
-               if (ret)
-                       break;
-
-               prev_pfn = this_pfn;
-               user_address += PAGE_SIZE;
-               pages_done++;
-       }
-
-out_up:
-       up_read(&current->mm->mmap_sem);
-
-       return ret;
-}
-
-static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached)
-{
-       struct videobuf_dma_contig_memory *mem;
-       struct videobuf_buffer *vb;
-
-       vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
-       if (vb) {
-               vb->priv = ((char *)vb) + size;
-               mem = vb->priv;
-               mem->magic = MAGIC_DC_MEM;
-               mem->cached = cached;
-       }
-
-       return vb;
-}
-
-static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size)
-{
-       return __videobuf_alloc_vb(size, false);
-}
-
-static struct videobuf_buffer *__videobuf_alloc_cached(size_t size)
-{
-       return __videobuf_alloc_vb(size, true);
-}
-
-static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_contig_memory *mem = buf->priv;
-
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-       return mem->vaddr;
-}
-
-static int __videobuf_iolock(struct videobuf_queue *q,
-                            struct videobuf_buffer *vb,
-                            struct v4l2_framebuffer *fbuf)
-{
-       struct videobuf_dma_contig_memory *mem = vb->priv;
-
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-       switch (vb->memory) {
-       case V4L2_MEMORY_MMAP:
-               dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
-
-               /* All handling should be done by __videobuf_mmap_mapper() */
-               if (!mem->vaddr) {
-                       dev_err(q->dev, "memory is not alloced/mmapped.\n");
-                       return -EINVAL;
-               }
-               break;
-       case V4L2_MEMORY_USERPTR:
-               dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
-
-               /* handle pointer from user space */
-               if (vb->baddr)
-                       return videobuf_dma_contig_user_get(mem, vb);
-
-               /* allocate memory for the read() method */
-               if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size),
-                                       GFP_KERNEL))
-                       return -ENOMEM;
-               break;
-       case V4L2_MEMORY_OVERLAY:
-       default:
-               dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int __videobuf_sync(struct videobuf_queue *q,
-                          struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_contig_memory *mem = buf->priv;
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-       dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size,
-                               DMA_FROM_DEVICE);
-
-       return 0;
-}
-
-static int __videobuf_mmap_mapper(struct videobuf_queue *q,
-                                 struct videobuf_buffer *buf,
-                                 struct vm_area_struct *vma)
-{
-       struct videobuf_dma_contig_memory *mem;
-       struct videobuf_mapping *map;
-       int retval;
-       unsigned long size;
-       unsigned long pos, start = vma->vm_start;
-       struct page *page;
-
-       dev_dbg(q->dev, "%s\n", __func__);
-
-       /* create mapping + update buffer list */
-       map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
-       if (!map)
-               return -ENOMEM;
-
-       buf->map = map;
-       map->q = q;
-
-       buf->baddr = vma->vm_start;
-
-       mem = buf->priv;
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-       if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize),
-                               GFP_KERNEL | __GFP_COMP))
-               goto error;
-
-       /* Try to remap memory */
-
-       size = vma->vm_end - vma->vm_start;
-       size = (size < mem->size) ? size : mem->size;
-
-       if (!mem->cached) {
-               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);
-               if (retval) {
-                       dev_err(q->dev, "mmap: remap failed with error %d. ",
-                                                               retval);
-                       dma_free_coherent(q->dev, mem->size,
-                                       mem->vaddr, mem->dma_handle);
-                       goto error;
-               }
-       } else {
-               pos = (unsigned long)mem->vaddr;
-
-               while (size > 0) {
-                       page = virt_to_page((void *)pos);
-                       if (NULL == page) {
-                               dev_err(q->dev, "mmap: virt_to_page failed\n");
-                               __videobuf_dc_free(q->dev, mem);
-                               goto error;
-                       }
-                       retval = vm_insert_page(vma, start, page);
-                       if (retval) {
-                               dev_err(q->dev, "mmap: insert failed with error %d\n",
-                                       retval);
-                               __videobuf_dc_free(q->dev, mem);
-                               goto error;
-                       }
-                       start += PAGE_SIZE;
-                       pos += PAGE_SIZE;
-
-                       if (size > PAGE_SIZE)
-                               size -= PAGE_SIZE;
-                       else
-                               size = 0;
-               }
-       }
-
-       vma->vm_ops = &videobuf_vm_ops;
-       vma->vm_flags |= VM_DONTEXPAND;
-       vma->vm_private_data = map;
-
-       dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
-               map, q, vma->vm_start, vma->vm_end,
-               (long int)buf->bsize, vma->vm_pgoff, buf->i);
-
-       videobuf_vm_open(vma);
-
-       return 0;
-
-error:
-       kfree(map);
-       return -ENOMEM;
-}
-
-static struct videobuf_qtype_ops qops = {
-       .magic          = MAGIC_QTYPE_OPS,
-       .alloc_vb       = __videobuf_alloc_uncached,
-       .iolock         = __videobuf_iolock,
-       .mmap_mapper    = __videobuf_mmap_mapper,
-       .vaddr          = __videobuf_to_vaddr,
-};
-
-static struct videobuf_qtype_ops qops_cached = {
-       .magic          = MAGIC_QTYPE_OPS,
-       .alloc_vb       = __videobuf_alloc_cached,
-       .iolock         = __videobuf_iolock,
-       .sync           = __videobuf_sync,
-       .mmap_mapper    = __videobuf_mmap_mapper,
-       .vaddr          = __videobuf_to_vaddr,
-};
-
-void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
-                                   const struct videobuf_queue_ops *ops,
-                                   struct device *dev,
-                                   spinlock_t *irqlock,
-                                   enum v4l2_buf_type type,
-                                   enum v4l2_field field,
-                                   unsigned int msize,
-                                   void *priv,
-                                   struct mutex *ext_lock)
-{
-       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
-                                priv, &qops, ext_lock);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
-
-void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q,
-                                          const struct videobuf_queue_ops *ops,
-                                          struct device *dev,
-                                          spinlock_t *irqlock,
-                                          enum v4l2_buf_type type,
-                                          enum v4l2_field field,
-                                          unsigned int msize,
-                                          void *priv, struct mutex *ext_lock)
-{
-       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
-                                priv, &qops_cached, ext_lock);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached);
-
-dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_contig_memory *mem = buf->priv;
-
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-       return mem->dma_handle;
-}
-EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
-
-void videobuf_dma_contig_free(struct videobuf_queue *q,
-                             struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_contig_memory *mem = buf->priv;
-
-       /* mmapped memory can't be freed here, otherwise mmapped region
-          would be released, while still needed. In this case, the memory
-          release should happen inside videobuf_vm_close().
-          So, it should free memory only if the memory were allocated for
-          read() operation.
-        */
-       if (buf->memory != V4L2_MEMORY_USERPTR)
-               return;
-
-       if (!mem)
-               return;
-
-       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-       /* handle user space pointer case */
-       if (buf->baddr) {
-               videobuf_dma_contig_user_put(mem);
-               return;
-       }
-
-       /* read() method */
-       if (mem->vaddr) {
-               __videobuf_dc_free(q->dev, mem);
-               mem->vaddr = NULL;
-       }
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
-
-MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
-MODULE_AUTHOR("Magnus Damm");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
deleted file mode 100644 (file)
index f300dea..0000000
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- * helper functions for SG DMA video4linux capture buffers
- *
- * The functions expect the hardware being able to scatter gather
- * (i.e. the buffers are not linear in physical memory, but fragmented
- * into PAGE_SIZE chunks).  They also assume the driver does not need
- * to touch the video data.
- *
- * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
- *
- * Highly based on video-buf written originally by:
- * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
- * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
- * (c) 2006 Ted Walther and John Sokol
- *
- * 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
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-
-#include <linux/dma-mapping.h>
-#include <linux/vmalloc.h>
-#include <linux/pagemap.h>
-#include <linux/scatterlist.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-
-#include <media/videobuf-dma-sg.h>
-
-#define MAGIC_DMABUF 0x19721112
-#define MAGIC_SG_MEM 0x17890714
-
-#define MAGIC_CHECK(is, should)                                                \
-       if (unlikely((is) != (should))) {                               \
-               printk(KERN_ERR "magic mismatch: %x (expected %x)\n",   \
-                               is, should);                            \
-               BUG();                                                  \
-       }
-
-static int debug;
-module_param(debug, int, 0644);
-
-MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
-
-#define dprintk(level, fmt, arg...)                                    \
-       if (debug >= level)                                             \
-               printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg)
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Return a scatterlist for some page-aligned vmalloc()'ed memory
- * block (NULL on errors).  Memory for the scatterlist is allocated
- * using kmalloc.  The caller must free the memory.
- */
-static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt,
-                                                 int nr_pages)
-{
-       struct scatterlist *sglist;
-       struct page *pg;
-       int i;
-
-       sglist = vzalloc(nr_pages * sizeof(*sglist));
-       if (NULL == sglist)
-               return NULL;
-       sg_init_table(sglist, nr_pages);
-       for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
-               pg = vmalloc_to_page(virt);
-               if (NULL == pg)
-                       goto err;
-               BUG_ON(PageHighMem(pg));
-               sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
-       }
-       return sglist;
-
-err:
-       vfree(sglist);
-       return NULL;
-}
-
-/*
- * Return a scatterlist for a an array of userpages (NULL on errors).
- * Memory for the scatterlist is allocated using kmalloc.  The caller
- * must free the memory.
- */
-static struct scatterlist *videobuf_pages_to_sg(struct page **pages,
-                                       int nr_pages, int offset, size_t size)
-{
-       struct scatterlist *sglist;
-       int i;
-
-       if (NULL == pages[0])
-               return NULL;
-       sglist = vmalloc(nr_pages * sizeof(*sglist));
-       if (NULL == sglist)
-               return NULL;
-       sg_init_table(sglist, nr_pages);
-
-       if (PageHighMem(pages[0]))
-               /* DMA to highmem pages might not work */
-               goto highmem;
-       sg_set_page(&sglist[0], pages[0],
-                       min_t(size_t, PAGE_SIZE - offset, size), offset);
-       size -= min_t(size_t, PAGE_SIZE - offset, size);
-       for (i = 1; i < nr_pages; i++) {
-               if (NULL == pages[i])
-                       goto nopage;
-               if (PageHighMem(pages[i]))
-                       goto highmem;
-               sg_set_page(&sglist[i], pages[i], min_t(size_t, PAGE_SIZE, size), 0);
-               size -= min_t(size_t, PAGE_SIZE, size);
-       }
-       return sglist;
-
-nopage:
-       dprintk(2, "sgl: oops - no page\n");
-       vfree(sglist);
-       return NULL;
-
-highmem:
-       dprintk(2, "sgl: oops - highmem page\n");
-       vfree(sglist);
-       return NULL;
-}
-
-/* --------------------------------------------------------------------- */
-
-struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_sg_memory *mem = buf->priv;
-       BUG_ON(!mem);
-
-       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
-       return &mem->dma;
-}
-EXPORT_SYMBOL_GPL(videobuf_to_dma);
-
-void videobuf_dma_init(struct videobuf_dmabuf *dma)
-{
-       memset(dma, 0, sizeof(*dma));
-       dma->magic = MAGIC_DMABUF;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_init);
-
-static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
-                       int direction, unsigned long data, unsigned long size)
-{
-       unsigned long first, last;
-       int err, rw = 0;
-
-       dma->direction = direction;
-       switch (dma->direction) {
-       case DMA_FROM_DEVICE:
-               rw = READ;
-               break;
-       case DMA_TO_DEVICE:
-               rw = WRITE;
-               break;
-       default:
-               BUG();
-       }
-
-       first = (data          & PAGE_MASK) >> PAGE_SHIFT;
-       last  = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;
-       dma->offset = data & ~PAGE_MASK;
-       dma->size = size;
-       dma->nr_pages = last-first+1;
-       dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL);
-       if (NULL == dma->pages)
-               return -ENOMEM;
-
-       dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
-               data, size, dma->nr_pages);
-
-       err = get_user_pages(current, current->mm,
-                            data & PAGE_MASK, dma->nr_pages,
-                            rw == READ, 1, /* force */
-                            dma->pages, NULL);
-
-       if (err != dma->nr_pages) {
-               dma->nr_pages = (err >= 0) ? err : 0;
-               dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages);
-               return err < 0 ? err : -EINVAL;
-       }
-       return 0;
-}
-
-int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
-                          unsigned long data, unsigned long size)
-{
-       int ret;
-
-       down_read(&current->mm->mmap_sem);
-       ret = videobuf_dma_init_user_locked(dma, direction, data, size);
-       up_read(&current->mm->mmap_sem);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
-
-int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
-                            int nr_pages)
-{
-       dprintk(1, "init kernel [%d pages]\n", nr_pages);
-
-       dma->direction = direction;
-       dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
-       if (NULL == dma->vaddr) {
-               dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
-               return -ENOMEM;
-       }
-
-       dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
-                               (unsigned long)dma->vaddr,
-                               nr_pages << PAGE_SHIFT);
-
-       memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
-       dma->nr_pages = nr_pages;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
-
-int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
-                             dma_addr_t addr, int nr_pages)
-{
-       dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n",
-               nr_pages, (unsigned long)addr);
-       dma->direction = direction;
-
-       if (0 == addr)
-               return -EINVAL;
-
-       dma->bus_addr = addr;
-       dma->nr_pages = nr_pages;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay);
-
-int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
-{
-       MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
-       BUG_ON(0 == dma->nr_pages);
-
-       if (dma->pages) {
-               dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,
-                                                  dma->offset, dma->size);
-       }
-       if (dma->vaddr) {
-               dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr,
-                                                    dma->nr_pages);
-       }
-       if (dma->bus_addr) {
-               dma->sglist = vmalloc(sizeof(*dma->sglist));
-               if (NULL != dma->sglist) {
-                       dma->sglen = 1;
-                       sg_dma_address(&dma->sglist[0]) = dma->bus_addr
-                                                       & PAGE_MASK;
-                       dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK;
-                       sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE;
-               }
-       }
-       if (NULL == dma->sglist) {
-               dprintk(1, "scatterlist is NULL\n");
-               return -ENOMEM;
-       }
-       if (!dma->bus_addr) {
-               dma->sglen = dma_map_sg(dev, dma->sglist,
-                                       dma->nr_pages, dma->direction);
-               if (0 == dma->sglen) {
-                       printk(KERN_WARNING
-                              "%s: videobuf_map_sg failed\n", __func__);
-                       vfree(dma->sglist);
-                       dma->sglist = NULL;
-                       dma->sglen = 0;
-                       return -ENOMEM;
-               }
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_map);
-
-int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
-{
-       MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
-
-       if (!dma->sglen)
-               return 0;
-
-       dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction);
-
-       vfree(dma->sglist);
-       dma->sglist = NULL;
-       dma->sglen = 0;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_unmap);
-
-int videobuf_dma_free(struct videobuf_dmabuf *dma)
-{
-       int i;
-       MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
-       BUG_ON(dma->sglen);
-
-       if (dma->pages) {
-               for (i = 0; i < dma->nr_pages; i++)
-                       page_cache_release(dma->pages[i]);
-               kfree(dma->pages);
-               dma->pages = NULL;
-       }
-
-       vfree(dma->vaddr);
-       dma->vaddr = NULL;
-
-       if (dma->bus_addr)
-               dma->bus_addr = 0;
-       dma->direction = DMA_NONE;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_free);
-
-/* --------------------------------------------------------------------- */
-
-static void videobuf_vm_open(struct vm_area_struct *vma)
-{
-       struct videobuf_mapping *map = vma->vm_private_data;
-
-       dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map,
-               map->count, vma->vm_start, vma->vm_end);
-
-       map->count++;
-}
-
-static void videobuf_vm_close(struct vm_area_struct *vma)
-{
-       struct videobuf_mapping *map = vma->vm_private_data;
-       struct videobuf_queue *q = map->q;
-       struct videobuf_dma_sg_memory *mem;
-       int i;
-
-       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) {
-               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;
-                       mem = q->bufs[i]->priv;
-                       if (!mem)
-                               continue;
-
-                       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
-                       if (q->bufs[i]->map != map)
-                               continue;
-                       q->bufs[i]->map   = NULL;
-                       q->bufs[i]->baddr = 0;
-                       q->ops->buf_release(q, q->bufs[i]);
-               }
-               videobuf_queue_unlock(q);
-               kfree(map);
-       }
-       return;
-}
-
-/*
- * Get a anonymous page for the mapping.  Make sure we can DMA to that
- * memory location with 32bit PCI devices (i.e. don't use highmem for
- * now ...).  Bounce buffers don't work very well for the data rates
- * video capture has.
- */
-static int videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
-{
-       struct page *page;
-
-       dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n",
-               (unsigned long)vmf->virtual_address,
-               vma->vm_start, vma->vm_end);
-
-       page = alloc_page(GFP_USER | __GFP_DMA32);
-       if (!page)
-               return VM_FAULT_OOM;
-       clear_user_highpage(page, (unsigned long)vmf->virtual_address);
-       vmf->page = page;
-
-       return 0;
-}
-
-static const struct vm_operations_struct videobuf_vm_ops = {
-       .open   = videobuf_vm_open,
-       .close  = videobuf_vm_close,
-       .fault  = videobuf_vm_fault,
-};
-
-/* ---------------------------------------------------------------------
- * SG handlers for the generic methods
- */
-
-/* Allocated area consists on 3 parts:
-       struct video_buffer
-       struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
-       struct videobuf_dma_sg_memory
- */
-
-static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
-{
-       struct videobuf_dma_sg_memory *mem;
-       struct videobuf_buffer *vb;
-
-       vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
-       if (!vb)
-               return vb;
-
-       mem = vb->priv = ((char *)vb) + size;
-       mem->magic = MAGIC_SG_MEM;
-
-       videobuf_dma_init(&mem->dma);
-
-       dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
-               __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
-               mem, (long)sizeof(*mem));
-
-       return vb;
-}
-
-static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_sg_memory *mem = buf->priv;
-       BUG_ON(!mem);
-
-       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
-       return mem->dma.vaddr;
-}
-
-static int __videobuf_iolock(struct videobuf_queue *q,
-                            struct videobuf_buffer *vb,
-                            struct v4l2_framebuffer *fbuf)
-{
-       int err, pages;
-       dma_addr_t bus;
-       struct videobuf_dma_sg_memory *mem = vb->priv;
-       BUG_ON(!mem);
-
-       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
-       switch (vb->memory) {
-       case V4L2_MEMORY_MMAP:
-       case V4L2_MEMORY_USERPTR:
-               if (0 == vb->baddr) {
-                       /* no userspace addr -- kernel bounce buffer */
-                       pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
-                       err = videobuf_dma_init_kernel(&mem->dma,
-                                                      DMA_FROM_DEVICE,
-                                                      pages);
-                       if (0 != err)
-                               return err;
-               } else if (vb->memory == V4L2_MEMORY_USERPTR) {
-                       /* dma directly to userspace */
-                       err = videobuf_dma_init_user(&mem->dma,
-                                                    DMA_FROM_DEVICE,
-                                                    vb->baddr, vb->bsize);
-                       if (0 != err)
-                               return err;
-               } else {
-                       /* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP
-                       buffers can only be called from videobuf_qbuf
-                       we take current->mm->mmap_sem there, to prevent
-                       locking inversion, so don't take it here */
-
-                       err = videobuf_dma_init_user_locked(&mem->dma,
-                                                     DMA_FROM_DEVICE,
-                                                     vb->baddr, vb->bsize);
-                       if (0 != err)
-                               return err;
-               }
-               break;
-       case V4L2_MEMORY_OVERLAY:
-               if (NULL == fbuf)
-                       return -EINVAL;
-               /* FIXME: need sanity checks for vb->boff */
-               /*
-                * Using a double cast to avoid compiler warnings when
-                * building for PAE. Compiler doesn't like direct casting
-                * of a 32 bit ptr to 64 bit integer.
-                */
-               bus   = (dma_addr_t)(unsigned long)fbuf->base + vb->boff;
-               pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
-               err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE,
-                                               bus, pages);
-               if (0 != err)
-                       return err;
-               break;
-       default:
-               BUG();
-       }
-       err = videobuf_dma_map(q->dev, &mem->dma);
-       if (0 != err)
-               return err;
-
-       return 0;
-}
-
-static int __videobuf_sync(struct videobuf_queue *q,
-                          struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_sg_memory *mem = buf->priv;
-       BUG_ON(!mem || !mem->dma.sglen);
-
-       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-       MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF);
-
-       dma_sync_sg_for_cpu(q->dev, mem->dma.sglist,
-                           mem->dma.sglen, mem->dma.direction);
-
-       return 0;
-}
-
-static int __videobuf_mmap_mapper(struct videobuf_queue *q,
-                                 struct videobuf_buffer *buf,
-                                 struct vm_area_struct *vma)
-{
-       struct videobuf_dma_sg_memory *mem = buf->priv;
-       struct videobuf_mapping *map;
-       unsigned int first, last, size = 0, i;
-       int retval;
-
-       retval = -EINVAL;
-
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
-       /* look for first buffer to map */
-       for (first = 0; first < VIDEO_MAX_FRAME; first++) {
-               if (buf == q->bufs[first]) {
-                       size = PAGE_ALIGN(q->bufs[first]->bsize);
-                       break;
-               }
-       }
-
-       /* paranoia, should never happen since buf is always valid. */
-       if (!size) {
-               dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n",
-                               (vma->vm_pgoff << PAGE_SHIFT));
-               goto done;
-       }
-
-       last = first;
-
-       /* create mapping + update buffer list */
-       retval = -ENOMEM;
-       map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
-       if (NULL == map)
-               goto done;
-
-       size = 0;
-       for (i = first; i <= last; i++) {
-               if (NULL == q->bufs[i])
-                       continue;
-               q->bufs[i]->map   = map;
-               q->bufs[i]->baddr = vma->vm_start + size;
-               size += PAGE_ALIGN(q->bufs[i]->bsize);
-       }
-
-       map->count    = 1;
-       map->q        = q;
-       vma->vm_ops   = &videobuf_vm_ops;
-       vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
-       vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
-       vma->vm_private_data = map;
-       dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n",
-               map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last);
-       retval = 0;
-
-done:
-       return retval;
-}
-
-static struct videobuf_qtype_ops sg_ops = {
-       .magic        = MAGIC_QTYPE_OPS,
-
-       .alloc_vb     = __videobuf_alloc_vb,
-       .iolock       = __videobuf_iolock,
-       .sync         = __videobuf_sync,
-       .mmap_mapper  = __videobuf_mmap_mapper,
-       .vaddr        = __videobuf_to_vaddr,
-};
-
-void *videobuf_sg_alloc(size_t size)
-{
-       struct videobuf_queue q;
-
-       /* Required to make generic handler to call __videobuf_alloc */
-       q.int_ops = &sg_ops;
-
-       q.msize = size;
-
-       return videobuf_alloc_vb(&q);
-}
-EXPORT_SYMBOL_GPL(videobuf_sg_alloc);
-
-void videobuf_queue_sg_init(struct videobuf_queue *q,
-                        const struct videobuf_queue_ops *ops,
-                        struct device *dev,
-                        spinlock_t *irqlock,
-                        enum v4l2_buf_type type,
-                        enum v4l2_field field,
-                        unsigned int msize,
-                        void *priv,
-                        struct mutex *ext_lock)
-{
-       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
-                                priv, &sg_ops, ext_lock);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);
-
diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c
deleted file mode 100644 (file)
index b7efa45..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- *
- * some helper function for simple DVB cards which simply DMA the
- * complete transport stream and let the computer sort everything else
- * (i.e. we are using the software demux, ...).  Also uses the
- * video-buf to manage DMA buffers.
- *
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/kthread.h>
-#include <linux/file.h>
-#include <linux/slab.h>
-
-#include <linux/freezer.h>
-
-#include <media/videobuf-core.h>
-#include <media/videobuf-dvb.h>
-
-/* ------------------------------------------------------------------ */
-
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"enable debug messages");
-
-#define dprintk(fmt, arg...)   if (debug)                      \
-       printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg)
-
-/* ------------------------------------------------------------------ */
-
-static int videobuf_dvb_thread(void *data)
-{
-       struct videobuf_dvb *dvb = data;
-       struct videobuf_buffer *buf;
-       unsigned long flags;
-       void *outp;
-
-       dprintk("dvb thread started\n");
-       set_freezable();
-       videobuf_read_start(&dvb->dvbq);
-
-       for (;;) {
-               /* fetch next buffer */
-               buf = list_entry(dvb->dvbq.stream.next,
-                                struct videobuf_buffer, stream);
-               list_del(&buf->stream);
-               videobuf_waiton(&dvb->dvbq, buf, 0, 1);
-
-               /* no more feeds left or stop_feed() asked us to quit */
-               if (0 == dvb->nfeeds)
-                       break;
-               if (kthread_should_stop())
-                       break;
-               try_to_freeze();
-
-               /* feed buffer data to demux */
-               outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf);
-
-               if (buf->state == VIDEOBUF_DONE)
-                       dvb_dmx_swfilter(&dvb->demux, outp,
-                                        buf->size);
-
-               /* requeue buffer */
-               list_add_tail(&buf->stream,&dvb->dvbq.stream);
-               spin_lock_irqsave(dvb->dvbq.irqlock,flags);
-               dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf);
-               spin_unlock_irqrestore(dvb->dvbq.irqlock,flags);
-       }
-
-       videobuf_read_stop(&dvb->dvbq);
-       dprintk("dvb thread stopped\n");
-
-       /* Hmm, linux becomes *very* unhappy without this ... */
-       while (!kthread_should_stop()) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule();
-       }
-       return 0;
-}
-
-static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed)
-{
-       struct dvb_demux *demux  = feed->demux;
-       struct videobuf_dvb *dvb = demux->priv;
-       int rc;
-
-       if (!demux->dmx.frontend)
-               return -EINVAL;
-
-       mutex_lock(&dvb->lock);
-       dvb->nfeeds++;
-       rc = dvb->nfeeds;
-
-       if (NULL != dvb->thread)
-               goto out;
-       dvb->thread = kthread_run(videobuf_dvb_thread,
-                                 dvb, "%s dvb", dvb->name);
-       if (IS_ERR(dvb->thread)) {
-               rc = PTR_ERR(dvb->thread);
-               dvb->thread = NULL;
-       }
-
-out:
-       mutex_unlock(&dvb->lock);
-       return rc;
-}
-
-static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed)
-{
-       struct dvb_demux *demux  = feed->demux;
-       struct videobuf_dvb *dvb = demux->priv;
-       int err = 0;
-
-       mutex_lock(&dvb->lock);
-       dvb->nfeeds--;
-       if (0 == dvb->nfeeds  &&  NULL != dvb->thread) {
-               err = kthread_stop(dvb->thread);
-               dvb->thread = NULL;
-       }
-       mutex_unlock(&dvb->lock);
-       return err;
-}
-
-static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
-                         struct module *module,
-                         void *adapter_priv,
-                         struct device *device,
-                         char *adapter_name,
-                         short *adapter_nr,
-                         int mfe_shared)
-{
-       int result;
-
-       mutex_init(&fe->lock);
-
-       /* register adapter */
-       result = dvb_register_adapter(&fe->adapter, adapter_name, module,
-               device, adapter_nr);
-       if (result < 0) {
-               printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
-                      adapter_name, result);
-       }
-       fe->adapter.priv = adapter_priv;
-       fe->adapter.mfe_shared = mfe_shared;
-
-       return result;
-}
-
-static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter,
-       struct videobuf_dvb *dvb)
-{
-       int result;
-
-       /* register frontend */
-       result = dvb_register_frontend(adapter, dvb->frontend);
-       if (result < 0) {
-               printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
-                      dvb->name, result);
-               goto fail_frontend;
-       }
-
-       /* register demux stuff */
-       dvb->demux.dmx.capabilities =
-               DMX_TS_FILTERING | DMX_SECTION_FILTERING |
-               DMX_MEMORY_BASED_FILTERING;
-       dvb->demux.priv       = dvb;
-       dvb->demux.filternum  = 256;
-       dvb->demux.feednum    = 256;
-       dvb->demux.start_feed = videobuf_dvb_start_feed;
-       dvb->demux.stop_feed  = videobuf_dvb_stop_feed;
-       result = dvb_dmx_init(&dvb->demux);
-       if (result < 0) {
-               printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
-                      dvb->name, result);
-               goto fail_dmx;
-       }
-
-       dvb->dmxdev.filternum    = 256;
-       dvb->dmxdev.demux        = &dvb->demux.dmx;
-       dvb->dmxdev.capabilities = 0;
-       result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
-
-       if (result < 0) {
-               printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
-                      dvb->name, result);
-               goto fail_dmxdev;
-       }
-
-       dvb->fe_hw.source = DMX_FRONTEND_0;
-       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-       if (result < 0) {
-               printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
-                      dvb->name, result);
-               goto fail_fe_hw;
-       }
-
-       dvb->fe_mem.source = DMX_MEMORY_FE;
-       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-       if (result < 0) {
-               printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
-                      dvb->name, result);
-               goto fail_fe_mem;
-       }
-
-       result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-       if (result < 0) {
-               printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
-                      dvb->name, result);
-               goto fail_fe_conn;
-       }
-
-       /* register network adapter */
-       result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
-       if (result < 0) {
-               printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n",
-                      dvb->name, result);
-               goto fail_fe_conn;
-       }
-       return 0;
-
-fail_fe_conn:
-       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-fail_fe_mem:
-       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-fail_fe_hw:
-       dvb_dmxdev_release(&dvb->dmxdev);
-fail_dmxdev:
-       dvb_dmx_release(&dvb->demux);
-fail_dmx:
-       dvb_unregister_frontend(dvb->frontend);
-fail_frontend:
-       dvb_frontend_detach(dvb->frontend);
-       dvb->frontend = NULL;
-
-       return result;
-}
-
-/* ------------------------------------------------------------------ */
-/* Register a single adapter and one or more frontends */
-int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
-                         struct module *module,
-                         void *adapter_priv,
-                         struct device *device,
-                         short *adapter_nr,
-                         int mfe_shared)
-{
-       struct list_head *list, *q;
-       struct videobuf_dvb_frontend *fe;
-       int res;
-
-       fe = videobuf_dvb_get_frontend(f, 1);
-       if (!fe) {
-               printk(KERN_WARNING "Unable to register the adapter which has no frontends\n");
-               return -EINVAL;
-       }
-
-       /* Bring up the adapter */
-       res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
-               fe->dvb.name, adapter_nr, mfe_shared);
-       if (res < 0) {
-               printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
-               return res;
-       }
-
-       /* Attach all of the frontends to the adapter */
-       mutex_lock(&f->lock);
-       list_for_each_safe(list, q, &f->felist) {
-               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
-               res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb);
-               if (res < 0) {
-                       printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n",
-                               fe->dvb.name, res);
-                       goto err;
-               }
-       }
-       mutex_unlock(&f->lock);
-       return 0;
-
-err:
-       mutex_unlock(&f->lock);
-       videobuf_dvb_unregister_bus(f);
-       return res;
-}
-EXPORT_SYMBOL(videobuf_dvb_register_bus);
-
-void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f)
-{
-       videobuf_dvb_dealloc_frontends(f);
-
-       dvb_unregister_adapter(&f->adapter);
-}
-EXPORT_SYMBOL(videobuf_dvb_unregister_bus);
-
-struct videobuf_dvb_frontend *videobuf_dvb_get_frontend(
-       struct videobuf_dvb_frontends *f, int id)
-{
-       struct list_head *list, *q;
-       struct videobuf_dvb_frontend *fe, *ret = NULL;
-
-       mutex_lock(&f->lock);
-
-       list_for_each_safe(list, q, &f->felist) {
-               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
-               if (fe->id == id) {
-                       ret = fe;
-                       break;
-               }
-       }
-
-       mutex_unlock(&f->lock);
-
-       return ret;
-}
-EXPORT_SYMBOL(videobuf_dvb_get_frontend);
-
-int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f,
-       struct dvb_frontend *p)
-{
-       struct list_head *list, *q;
-       struct videobuf_dvb_frontend *fe = NULL;
-       int ret = 0;
-
-       mutex_lock(&f->lock);
-
-       list_for_each_safe(list, q, &f->felist) {
-               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
-               if (fe->dvb.frontend == p) {
-                       ret = fe->id;
-                       break;
-               }
-       }
-
-       mutex_unlock(&f->lock);
-
-       return ret;
-}
-EXPORT_SYMBOL(videobuf_dvb_find_frontend);
-
-struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend(
-       struct videobuf_dvb_frontends *f, int id)
-{
-       struct videobuf_dvb_frontend *fe;
-
-       fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL);
-       if (fe == NULL)
-               goto fail_alloc;
-
-       fe->id = id;
-       mutex_init(&fe->dvb.lock);
-
-       mutex_lock(&f->lock);
-       list_add_tail(&fe->felist, &f->felist);
-       mutex_unlock(&f->lock);
-
-fail_alloc:
-       return fe;
-}
-EXPORT_SYMBOL(videobuf_dvb_alloc_frontend);
-
-void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f)
-{
-       struct list_head *list, *q;
-       struct videobuf_dvb_frontend *fe;
-
-       mutex_lock(&f->lock);
-       list_for_each_safe(list, q, &f->felist) {
-               fe = list_entry(list, struct videobuf_dvb_frontend, felist);
-               if (fe->dvb.net.dvbdev) {
-                       dvb_net_release(&fe->dvb.net);
-                       fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
-                               &fe->dvb.fe_mem);
-                       fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
-                               &fe->dvb.fe_hw);
-                       dvb_dmxdev_release(&fe->dvb.dmxdev);
-                       dvb_dmx_release(&fe->dvb.demux);
-                       dvb_unregister_frontend(fe->dvb.frontend);
-               }
-               if (fe->dvb.frontend)
-                       /* always allocated, may have been reset */
-                       dvb_frontend_detach(fe->dvb.frontend);
-               list_del(list); /* remove list entry */
-               kfree(fe);      /* free frontend allocation */
-       }
-       mutex_unlock(&f->lock);
-}
-EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends);
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c
deleted file mode 100644 (file)
index df14258..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * helper functions for vmalloc video4linux capture buffers
- *
- * The functions expect the hardware being able to scatter gather
- * (i.e. the buffers are not linear in physical memory, but fragmented
- * into PAGE_SIZE chunks).  They also assume the driver does not need
- * to touch the video data.
- *
- * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
- *
- * 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
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-
-#include <linux/pci.h>
-#include <linux/vmalloc.h>
-#include <linux/pagemap.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-
-#include <media/videobuf-vmalloc.h>
-
-#define MAGIC_DMABUF   0x17760309
-#define MAGIC_VMAL_MEM 0x18221223
-
-#define MAGIC_CHECK(is, should)                                                \
-       if (unlikely((is) != (should))) {                               \
-               printk(KERN_ERR "magic mismatch: %x (expected %x)\n",   \
-                               is, should);                            \
-               BUG();                                                  \
-       }
-
-static int debug;
-module_param(debug, int, 0644);
-
-MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
-
-#define dprintk(level, fmt, arg...)                                    \
-       if (debug >= level)                                             \
-               printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
-
-
-/***************************************************************************/
-
-static void videobuf_vm_open(struct vm_area_struct *vma)
-{
-       struct videobuf_mapping *map = vma->vm_private_data;
-
-       dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
-               map->count, vma->vm_start, vma->vm_end);
-
-       map->count++;
-}
-
-static void videobuf_vm_close(struct vm_area_struct *vma)
-{
-       struct videobuf_mapping *map = vma->vm_private_data;
-       struct videobuf_queue *q = map->q;
-       int i;
-
-       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) {
-               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)
-                       videobuf_queue_cancel(q);
-
-               for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-                       if (NULL == q->bufs[i])
-                               continue;
-
-                       if (q->bufs[i]->map != map)
-                               continue;
-
-                       mem = q->bufs[i]->priv;
-                       if (mem) {
-                               /* This callback is called only if kernel has
-                                  allocated memory and this memory is mmapped.
-                                  In this case, memory should be freed,
-                                  in order to do memory unmap.
-                                */
-
-                               MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
-                               /* vfree is not atomic - can't be
-                                  called with IRQ's disabled
-                                */
-                               dprintk(1, "%s: buf[%d] freeing (%p)\n",
-                                       __func__, i, mem->vaddr);
-
-                               vfree(mem->vaddr);
-                               mem->vaddr = NULL;
-                       }
-
-                       q->bufs[i]->map   = NULL;
-                       q->bufs[i]->baddr = 0;
-               }
-
-               kfree(map);
-
-               videobuf_queue_unlock(q);
-       }
-
-       return;
-}
-
-static const struct vm_operations_struct videobuf_vm_ops = {
-       .open     = videobuf_vm_open,
-       .close    = videobuf_vm_close,
-};
-
-/* ---------------------------------------------------------------------
- * vmalloc handlers for the generic methods
- */
-
-/* Allocated area consists on 3 parts:
-       struct video_buffer
-       struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
-       struct videobuf_dma_sg_memory
- */
-
-static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
-{
-       struct videobuf_vmalloc_memory *mem;
-       struct videobuf_buffer *vb;
-
-       vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
-       if (!vb)
-               return vb;
-
-       mem = vb->priv = ((char *)vb) + size;
-       mem->magic = MAGIC_VMAL_MEM;
-
-       dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
-               __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
-               mem, (long)sizeof(*mem));
-
-       return vb;
-}
-
-static int __videobuf_iolock(struct videobuf_queue *q,
-                            struct videobuf_buffer *vb,
-                            struct v4l2_framebuffer *fbuf)
-{
-       struct videobuf_vmalloc_memory *mem = vb->priv;
-       int pages;
-
-       BUG_ON(!mem);
-
-       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
-       switch (vb->memory) {
-       case V4L2_MEMORY_MMAP:
-               dprintk(1, "%s memory method MMAP\n", __func__);
-
-               /* All handling should be done by __videobuf_mmap_mapper() */
-               if (!mem->vaddr) {
-                       printk(KERN_ERR "memory is not alloced/mmapped.\n");
-                       return -EINVAL;
-               }
-               break;
-       case V4L2_MEMORY_USERPTR:
-               pages = PAGE_ALIGN(vb->size);
-
-               dprintk(1, "%s memory method USERPTR\n", __func__);
-
-               if (vb->baddr) {
-                       printk(KERN_ERR "USERPTR is currently not supported\n");
-                       return -EINVAL;
-               }
-
-               /* The only USERPTR currently supported is the one needed for
-                * read() method.
-                */
-
-               mem->vaddr = vmalloc_user(pages);
-               if (!mem->vaddr) {
-                       printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
-                       return -ENOMEM;
-               }
-               dprintk(1, "vmalloc is at addr %p (%d pages)\n",
-                       mem->vaddr, pages);
-
-#if 0
-               int rc;
-               /* Kernel userptr is used also by read() method. In this case,
-                  there's no need to remap, since data will be copied to user
-                */
-               if (!vb->baddr)
-                       return 0;
-
-               /* FIXME: to properly support USERPTR, remap should occur.
-                  The code below won't work, since mem->vma = NULL
-                */
-               /* Try to remap memory */
-               rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0);
-               if (rc < 0) {
-                       printk(KERN_ERR "mmap: remap failed with error %d", rc);
-                       return -ENOMEM;
-               }
-#endif
-
-               break;
-       case V4L2_MEMORY_OVERLAY:
-       default:
-               dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
-
-               /* Currently, doesn't support V4L2_MEMORY_OVERLAY */
-               printk(KERN_ERR "Memory method currently unsupported.\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int __videobuf_mmap_mapper(struct videobuf_queue *q,
-                                 struct videobuf_buffer *buf,
-                                 struct vm_area_struct *vma)
-{
-       struct videobuf_vmalloc_memory *mem;
-       struct videobuf_mapping *map;
-       int retval, pages;
-
-       dprintk(1, "%s\n", __func__);
-
-       /* create mapping + update buffer list */
-       map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
-       if (NULL == map)
-               return -ENOMEM;
-
-       buf->map = map;
-       map->q     = q;
-
-       buf->baddr = vma->vm_start;
-
-       mem = buf->priv;
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
-       pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
-       mem->vaddr = vmalloc_user(pages);
-       if (!mem->vaddr) {
-               printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
-               goto error;
-       }
-       dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages);
-
-       /* Try to remap memory */
-       retval = remap_vmalloc_range(vma, mem->vaddr, 0);
-       if (retval < 0) {
-               printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
-               vfree(mem->vaddr);
-               goto error;
-       }
-
-       vma->vm_ops          = &videobuf_vm_ops;
-       vma->vm_flags       |= VM_DONTEXPAND | VM_RESERVED;
-       vma->vm_private_data = map;
-
-       dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
-               map, q, vma->vm_start, vma->vm_end,
-               (long int)buf->bsize,
-               vma->vm_pgoff, buf->i);
-
-       videobuf_vm_open(vma);
-
-       return 0;
-
-error:
-       mem = NULL;
-       kfree(map);
-       return -ENOMEM;
-}
-
-static struct videobuf_qtype_ops qops = {
-       .magic        = MAGIC_QTYPE_OPS,
-
-       .alloc_vb     = __videobuf_alloc_vb,
-       .iolock       = __videobuf_iolock,
-       .mmap_mapper  = __videobuf_mmap_mapper,
-       .vaddr        = videobuf_to_vmalloc,
-};
-
-void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
-                        const struct videobuf_queue_ops *ops,
-                        struct device *dev,
-                        spinlock_t *irqlock,
-                        enum v4l2_buf_type type,
-                        enum v4l2_field field,
-                        unsigned int msize,
-                        void *priv,
-                        struct mutex *ext_lock)
-{
-       videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
-                                priv, &qops, ext_lock);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
-
-void *videobuf_to_vmalloc(struct videobuf_buffer *buf)
-{
-       struct videobuf_vmalloc_memory *mem = buf->priv;
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
-       return mem->vaddr;
-}
-EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
-
-void videobuf_vmalloc_free(struct videobuf_buffer *buf)
-{
-       struct videobuf_vmalloc_memory *mem = buf->priv;
-
-       /* mmapped memory can't be freed here, otherwise mmapped region
-          would be released, while still needed. In this case, the memory
-          release should happen inside videobuf_vm_close().
-          So, it should free memory only if the memory were allocated for
-          read() operation.
-        */
-       if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
-               return;
-
-       if (!mem)
-               return;
-
-       MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
-       vfree(mem->vaddr);
-       mem->vaddr = NULL;
-
-       return;
-}
-EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);
-
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
deleted file mode 100644 (file)
index 4da3df6..0000000
+++ /dev/null
@@ -1,2380 +0,0 @@
-/*
- * videobuf2-core.c - V4L2 driver helper framework
- *
- * Copyright (C) 2010 Samsung Electronics
- *
- * Author: Pawel Osciak <pawel@osciak.com>
- *        Marek Szyprowski <m.szyprowski@samsung.com>
- *
- * 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.
- */
-
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/poll.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-core.h>
-
-static int debug;
-module_param(debug, int, 0644);
-
-#define dprintk(level, fmt, arg...)                                    \
-       do {                                                            \
-               if (debug >= level)                                     \
-                       printk(KERN_DEBUG "vb2: " fmt, ## arg);         \
-       } while (0)
-
-#define call_memop(q, op, args...)                                     \
-       (((q)->mem_ops->op) ?                                           \
-               ((q)->mem_ops->op(args)) : 0)
-
-#define call_qop(q, op, args...)                                       \
-       (((q)->ops->op) ? ((q)->ops->op(args)) : 0)
-
-#define V4L2_BUFFER_STATE_FLAGS        (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
-                                V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
-                                V4L2_BUF_FLAG_PREPARED)
-
-/**
- * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
- */
-static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-       void *mem_priv;
-       int plane;
-
-       /* Allocate memory for all planes in this buffer */
-       for (plane = 0; plane < vb->num_planes; ++plane) {
-               mem_priv = call_memop(q, alloc, q->alloc_ctx[plane],
-                                     q->plane_sizes[plane]);
-               if (IS_ERR_OR_NULL(mem_priv))
-                       goto free;
-
-               /* Associate allocator private data with this plane */
-               vb->planes[plane].mem_priv = mem_priv;
-               vb->v4l2_planes[plane].length = q->plane_sizes[plane];
-       }
-
-       return 0;
-free:
-       /* Free already allocated memory if one of the allocations failed */
-       for (; plane > 0; --plane) {
-               call_memop(q, put, vb->planes[plane - 1].mem_priv);
-               vb->planes[plane - 1].mem_priv = NULL;
-       }
-
-       return -ENOMEM;
-}
-
-/**
- * __vb2_buf_mem_free() - free memory of the given buffer
- */
-static void __vb2_buf_mem_free(struct vb2_buffer *vb)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-       unsigned int plane;
-
-       for (plane = 0; plane < vb->num_planes; ++plane) {
-               call_memop(q, put, vb->planes[plane].mem_priv);
-               vb->planes[plane].mem_priv = NULL;
-               dprintk(3, "Freed plane %d of buffer %d\n", plane,
-                       vb->v4l2_buf.index);
-       }
-}
-
-/**
- * __vb2_buf_userptr_put() - release userspace memory associated with
- * a USERPTR buffer
- */
-static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-       unsigned int plane;
-
-       for (plane = 0; plane < vb->num_planes; ++plane) {
-               if (vb->planes[plane].mem_priv)
-                       call_memop(q, put_userptr, vb->planes[plane].mem_priv);
-               vb->planes[plane].mem_priv = NULL;
-       }
-}
-
-/**
- * __setup_offsets() - setup unique offsets ("cookies") for every plane in
- * every buffer on the queue
- */
-static void __setup_offsets(struct vb2_queue *q, unsigned int n)
-{
-       unsigned int buffer, plane;
-       struct vb2_buffer *vb;
-       unsigned long off;
-
-       if (q->num_buffers) {
-               struct v4l2_plane *p;
-               vb = q->bufs[q->num_buffers - 1];
-               p = &vb->v4l2_planes[vb->num_planes - 1];
-               off = PAGE_ALIGN(p->m.mem_offset + p->length);
-       } else {
-               off = 0;
-       }
-
-       for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
-               vb = q->bufs[buffer];
-               if (!vb)
-                       continue;
-
-               for (plane = 0; plane < vb->num_planes; ++plane) {
-                       vb->v4l2_planes[plane].length = q->plane_sizes[plane];
-                       vb->v4l2_planes[plane].m.mem_offset = off;
-
-                       dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
-                                       buffer, plane, off);
-
-                       off += vb->v4l2_planes[plane].length;
-                       off = PAGE_ALIGN(off);
-               }
-       }
-}
-
-/**
- * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
- * video buffer memory for all buffers/planes on the queue and initializes the
- * queue
- *
- * Returns the number of buffers successfully allocated.
- */
-static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
-                            unsigned int num_buffers, unsigned int num_planes)
-{
-       unsigned int buffer;
-       struct vb2_buffer *vb;
-       int ret;
-
-       for (buffer = 0; buffer < num_buffers; ++buffer) {
-               /* Allocate videobuf buffer structures */
-               vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
-               if (!vb) {
-                       dprintk(1, "Memory alloc for buffer struct failed\n");
-                       break;
-               }
-
-               /* Length stores number of planes for multiplanar buffers */
-               if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
-                       vb->v4l2_buf.length = num_planes;
-
-               vb->state = VB2_BUF_STATE_DEQUEUED;
-               vb->vb2_queue = q;
-               vb->num_planes = num_planes;
-               vb->v4l2_buf.index = q->num_buffers + buffer;
-               vb->v4l2_buf.type = q->type;
-               vb->v4l2_buf.memory = memory;
-
-               /* Allocate video buffer memory for the MMAP type */
-               if (memory == V4L2_MEMORY_MMAP) {
-                       ret = __vb2_buf_mem_alloc(vb);
-                       if (ret) {
-                               dprintk(1, "Failed allocating memory for "
-                                               "buffer %d\n", buffer);
-                               kfree(vb);
-                               break;
-                       }
-                       /*
-                        * Call the driver-provided buffer initialization
-                        * callback, if given. An error in initialization
-                        * results in queue setup failure.
-                        */
-                       ret = call_qop(q, buf_init, vb);
-                       if (ret) {
-                               dprintk(1, "Buffer %d %p initialization"
-                                       " failed\n", buffer, vb);
-                               __vb2_buf_mem_free(vb);
-                               kfree(vb);
-                               break;
-                       }
-               }
-
-               q->bufs[q->num_buffers + buffer] = vb;
-       }
-
-       __setup_offsets(q, buffer);
-
-       dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
-                       buffer, num_planes);
-
-       return buffer;
-}
-
-/**
- * __vb2_free_mem() - release all video buffer memory for a given queue
- */
-static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
-{
-       unsigned int buffer;
-       struct vb2_buffer *vb;
-
-       for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
-            ++buffer) {
-               vb = q->bufs[buffer];
-               if (!vb)
-                       continue;
-
-               /* Free MMAP buffers or release USERPTR buffers */
-               if (q->memory == V4L2_MEMORY_MMAP)
-                       __vb2_buf_mem_free(vb);
-               else
-                       __vb2_buf_userptr_put(vb);
-       }
-}
-
-/**
- * __vb2_queue_free() - free buffers at the end of the queue - video memory and
- * related information, if no buffers are left return the queue to an
- * uninitialized state. Might be called even if the queue has already been freed.
- */
-static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
-{
-       unsigned int buffer;
-
-       /* Call driver-provided cleanup function for each buffer, if provided */
-       if (q->ops->buf_cleanup) {
-               for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
-                    ++buffer) {
-                       if (NULL == q->bufs[buffer])
-                               continue;
-                       q->ops->buf_cleanup(q->bufs[buffer]);
-               }
-       }
-
-       /* Release video buffer memory */
-       __vb2_free_mem(q, buffers);
-
-       /* Free videobuf buffers */
-       for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
-            ++buffer) {
-               kfree(q->bufs[buffer]);
-               q->bufs[buffer] = NULL;
-       }
-
-       q->num_buffers -= buffers;
-       if (!q->num_buffers)
-               q->memory = 0;
-       INIT_LIST_HEAD(&q->queued_list);
-}
-
-/**
- * __verify_planes_array() - verify that the planes array passed in struct
- * v4l2_buffer from userspace can be safely used
- */
-static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
-{
-       /* Is memory for copying plane information present? */
-       if (NULL == b->m.planes) {
-               dprintk(1, "Multi-planar buffer passed but "
-                          "planes array not provided\n");
-               return -EINVAL;
-       }
-
-       if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
-               dprintk(1, "Incorrect planes array length, "
-                          "expected %d, got %d\n", vb->num_planes, b->length);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/**
- * __buffer_in_use() - return true if the buffer is in use and
- * the queue cannot be freed (by the means of REQBUFS(0)) call
- */
-static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
-{
-       unsigned int plane;
-       for (plane = 0; plane < vb->num_planes; ++plane) {
-               void *mem_priv = vb->planes[plane].mem_priv;
-               /*
-                * If num_users() has not been provided, call_memop
-                * will return 0, apparently nobody cares about this
-                * case anyway. If num_users() returns more than 1,
-                * we are not the only user of the plane's memory.
-                */
-               if (mem_priv && call_memop(q, num_users, mem_priv) > 1)
-                       return true;
-       }
-       return false;
-}
-
-/**
- * __buffers_in_use() - return true if any buffers on the queue are in use and
- * the queue cannot be freed (by the means of REQBUFS(0)) call
- */
-static bool __buffers_in_use(struct vb2_queue *q)
-{
-       unsigned int buffer;
-       for (buffer = 0; buffer < q->num_buffers; ++buffer) {
-               if (__buffer_in_use(q, q->bufs[buffer]))
-                       return true;
-       }
-       return false;
-}
-
-/**
- * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
- * returned to userspace
- */
-static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-       int ret;
-
-       /* Copy back data such as timestamp, flags, etc. */
-       memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
-       b->reserved2 = vb->v4l2_buf.reserved2;
-       b->reserved = vb->v4l2_buf.reserved;
-
-       if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
-               ret = __verify_planes_array(vb, b);
-               if (ret)
-                       return ret;
-
-               /*
-                * Fill in plane-related data if userspace provided an array
-                * for it. The memory and size is verified above.
-                */
-               memcpy(b->m.planes, vb->v4l2_planes,
-                       b->length * sizeof(struct v4l2_plane));
-       } else {
-               /*
-                * We use length and offset in v4l2_planes array even for
-                * single-planar buffers, but userspace does not.
-                */
-               b->length = vb->v4l2_planes[0].length;
-               b->bytesused = vb->v4l2_planes[0].bytesused;
-               if (q->memory == V4L2_MEMORY_MMAP)
-                       b->m.offset = vb->v4l2_planes[0].m.mem_offset;
-               else if (q->memory == V4L2_MEMORY_USERPTR)
-                       b->m.userptr = vb->v4l2_planes[0].m.userptr;
-       }
-
-       /*
-        * Clear any buffer state related flags.
-        */
-       b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
-
-       switch (vb->state) {
-       case VB2_BUF_STATE_QUEUED:
-       case VB2_BUF_STATE_ACTIVE:
-               b->flags |= V4L2_BUF_FLAG_QUEUED;
-               break;
-       case VB2_BUF_STATE_ERROR:
-               b->flags |= V4L2_BUF_FLAG_ERROR;
-               /* fall through */
-       case VB2_BUF_STATE_DONE:
-               b->flags |= V4L2_BUF_FLAG_DONE;
-               break;
-       case VB2_BUF_STATE_PREPARED:
-               b->flags |= V4L2_BUF_FLAG_PREPARED;
-               break;
-       case VB2_BUF_STATE_DEQUEUED:
-               /* nothing */
-               break;
-       }
-
-       if (__buffer_in_use(q, vb))
-               b->flags |= V4L2_BUF_FLAG_MAPPED;
-
-       return 0;
-}
-
-/**
- * vb2_querybuf() - query video buffer information
- * @q:         videobuf queue
- * @b:         buffer struct passed from userspace to vidioc_querybuf handler
- *             in driver
- *
- * Should be called from vidioc_querybuf ioctl handler in driver.
- * This function will verify the passed v4l2_buffer structure and fill the
- * relevant information for the userspace.
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_querybuf handler in driver.
- */
-int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
-{
-       struct vb2_buffer *vb;
-
-       if (b->type != q->type) {
-               dprintk(1, "querybuf: wrong buffer type\n");
-               return -EINVAL;
-       }
-
-       if (b->index >= q->num_buffers) {
-               dprintk(1, "querybuf: buffer index out of range\n");
-               return -EINVAL;
-       }
-       vb = q->bufs[b->index];
-
-       return __fill_v4l2_buffer(vb, b);
-}
-EXPORT_SYMBOL(vb2_querybuf);
-
-/**
- * __verify_userptr_ops() - verify that all memory operations required for
- * USERPTR queue type have been provided
- */
-static int __verify_userptr_ops(struct vb2_queue *q)
-{
-       if (!(q->io_modes & VB2_USERPTR) || !q->mem_ops->get_userptr ||
-           !q->mem_ops->put_userptr)
-               return -EINVAL;
-
-       return 0;
-}
-
-/**
- * __verify_mmap_ops() - verify that all memory operations required for
- * MMAP queue type have been provided
- */
-static int __verify_mmap_ops(struct vb2_queue *q)
-{
-       if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc ||
-           !q->mem_ops->put || !q->mem_ops->mmap)
-               return -EINVAL;
-
-       return 0;
-}
-
-/**
- * __verify_memory_type() - Check whether the memory type and buffer type
- * passed to a buffer operation are compatible with the queue.
- */
-static int __verify_memory_type(struct vb2_queue *q,
-               enum v4l2_memory memory, enum v4l2_buf_type type)
-{
-       if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) {
-               dprintk(1, "reqbufs: unsupported memory type\n");
-               return -EINVAL;
-       }
-
-       if (type != q->type) {
-               dprintk(1, "reqbufs: requested type is incorrect\n");
-               return -EINVAL;
-       }
-
-       /*
-        * Make sure all the required memory ops for given memory type
-        * are available.
-        */
-       if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
-               dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
-               return -EINVAL;
-       }
-
-       if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
-               dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
-               return -EINVAL;
-       }
-
-       /*
-        * Place the busy tests at the end: -EBUSY can be ignored when
-        * create_bufs is called with count == 0, but count == 0 should still
-        * do the memory and type validation.
-        */
-       if (q->fileio) {
-               dprintk(1, "reqbufs: file io in progress\n");
-               return -EBUSY;
-       }
-       return 0;
-}
-
-/**
- * __reqbufs() - Initiate streaming
- * @q:         videobuf2 queue
- * @req:       struct passed from userspace to vidioc_reqbufs handler in driver
- *
- * Should be called from vidioc_reqbufs ioctl handler of a driver.
- * This function:
- * 1) verifies streaming parameters passed from the userspace,
- * 2) sets up the queue,
- * 3) negotiates number of buffers and planes per buffer with the driver
- *    to be used during streaming,
- * 4) allocates internal buffer structures (struct vb2_buffer), according to
- *    the agreed parameters,
- * 5) for MMAP memory type, allocates actual video memory, using the
- *    memory handling/allocation routines provided during queue initialization
- *
- * If req->count is 0, all the memory will be freed instead.
- * If the queue has been allocated previously (by a previous vb2_reqbufs) call
- * and the queue is not busy, memory will be reallocated.
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_reqbufs handler in driver.
- */
-static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
-{
-       unsigned int num_buffers, allocated_buffers, num_planes = 0;
-       int ret;
-
-       if (q->streaming) {
-               dprintk(1, "reqbufs: streaming active\n");
-               return -EBUSY;
-       }
-
-       if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
-               /*
-                * We already have buffers allocated, so first check if they
-                * are not in use and can be freed.
-                */
-               if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
-                       dprintk(1, "reqbufs: memory in use, cannot free\n");
-                       return -EBUSY;
-               }
-
-               __vb2_queue_free(q, q->num_buffers);
-
-               /*
-                * In case of REQBUFS(0) return immediately without calling
-                * driver's queue_setup() callback and allocating resources.
-                */
-               if (req->count == 0)
-                       return 0;
-       }
-
-       /*
-        * Make sure the requested values and current defaults are sane.
-        */
-       num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
-       memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
-       memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
-       q->memory = req->memory;
-
-       /*
-        * Ask the driver how many buffers and planes per buffer it requires.
-        * Driver also sets the size and allocator context for each plane.
-        */
-       ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
-                      q->plane_sizes, q->alloc_ctx);
-       if (ret)
-               return ret;
-
-       /* Finally, allocate buffers and video memory */
-       ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
-       if (ret == 0) {
-               dprintk(1, "Memory allocation failed\n");
-               return -ENOMEM;
-       }
-
-       allocated_buffers = ret;
-
-       /*
-        * Check if driver can handle the allocated number of buffers.
-        */
-       if (allocated_buffers < num_buffers) {
-               num_buffers = allocated_buffers;
-
-               ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
-                              &num_planes, q->plane_sizes, q->alloc_ctx);
-
-               if (!ret && allocated_buffers < num_buffers)
-                       ret = -ENOMEM;
-
-               /*
-                * Either the driver has accepted a smaller number of buffers,
-                * or .queue_setup() returned an error
-                */
-       }
-
-       q->num_buffers = allocated_buffers;
-
-       if (ret < 0) {
-               __vb2_queue_free(q, allocated_buffers);
-               return ret;
-       }
-
-       /*
-        * Return the number of successfully allocated buffers
-        * to the userspace.
-        */
-       req->count = allocated_buffers;
-
-       return 0;
-}
-
-/**
- * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and
- * type values.
- * @q:         videobuf2 queue
- * @req:       struct passed from userspace to vidioc_reqbufs handler in driver
- */
-int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
-{
-       int ret = __verify_memory_type(q, req->memory, req->type);
-
-       return ret ? ret : __reqbufs(q, req);
-}
-EXPORT_SYMBOL_GPL(vb2_reqbufs);
-
-/**
- * __create_bufs() - Allocate buffers and any required auxiliary structs
- * @q:         videobuf2 queue
- * @create:    creation parameters, passed from userspace to vidioc_create_bufs
- *             handler in driver
- *
- * Should be called from vidioc_create_bufs ioctl handler of a driver.
- * This function:
- * 1) verifies parameter sanity
- * 2) calls the .queue_setup() queue operation
- * 3) performs any necessary memory allocations
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_create_bufs handler in driver.
- */
-static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
-{
-       unsigned int num_planes = 0, num_buffers, allocated_buffers;
-       int ret;
-
-       if (q->num_buffers == VIDEO_MAX_FRAME) {
-               dprintk(1, "%s(): maximum number of buffers already allocated\n",
-                       __func__);
-               return -ENOBUFS;
-       }
-
-       if (!q->num_buffers) {
-               memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
-               memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
-               q->memory = create->memory;
-       }
-
-       num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
-
-       /*
-        * Ask the driver, whether the requested number of buffers, planes per
-        * buffer and their sizes are acceptable
-        */
-       ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
-                      &num_planes, q->plane_sizes, q->alloc_ctx);
-       if (ret)
-               return ret;
-
-       /* Finally, allocate buffers and video memory */
-       ret = __vb2_queue_alloc(q, create->memory, num_buffers,
-                               num_planes);
-       if (ret == 0) {
-               dprintk(1, "Memory allocation failed\n");
-               return -ENOMEM;
-       }
-
-       allocated_buffers = ret;
-
-       /*
-        * Check if driver can handle the so far allocated number of buffers.
-        */
-       if (ret < num_buffers) {
-               num_buffers = ret;
-
-               /*
-                * q->num_buffers contains the total number of buffers, that the
-                * queue driver has set up
-                */
-               ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
-                              &num_planes, q->plane_sizes, q->alloc_ctx);
-
-               if (!ret && allocated_buffers < num_buffers)
-                       ret = -ENOMEM;
-
-               /*
-                * Either the driver has accepted a smaller number of buffers,
-                * or .queue_setup() returned an error
-                */
-       }
-
-       q->num_buffers += allocated_buffers;
-
-       if (ret < 0) {
-               __vb2_queue_free(q, allocated_buffers);
-               return -ENOMEM;
-       }
-
-       /*
-        * Return the number of successfully allocated buffers
-        * to the userspace.
-        */
-       create->count = allocated_buffers;
-
-       return 0;
-}
-
-/**
- * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the
- * memory and type values.
- * @q:         videobuf2 queue
- * @create:    creation parameters, passed from userspace to vidioc_create_bufs
- *             handler in driver
- */
-int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
-{
-       int ret = __verify_memory_type(q, create->memory, create->format.type);
-
-       create->index = q->num_buffers;
-       if (create->count == 0)
-               return ret != -EBUSY ? ret : 0;
-       return ret ? ret : __create_bufs(q, create);
-}
-EXPORT_SYMBOL_GPL(vb2_create_bufs);
-
-/**
- * vb2_plane_vaddr() - Return a kernel virtual address of a given plane
- * @vb:                vb2_buffer to which the plane in question belongs to
- * @plane_no:  plane number for which the address is to be returned
- *
- * This function returns a kernel virtual address of a given plane if
- * such a mapping exist, NULL otherwise.
- */
-void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-
-       if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
-               return NULL;
-
-       return call_memop(q, vaddr, vb->planes[plane_no].mem_priv);
-
-}
-EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
-
-/**
- * vb2_plane_cookie() - Return allocator specific cookie for the given plane
- * @vb:                vb2_buffer to which the plane in question belongs to
- * @plane_no:  plane number for which the cookie is to be returned
- *
- * This function returns an allocator specific cookie for a given plane if
- * available, NULL otherwise. The allocator should provide some simple static
- * inline function, which would convert this cookie to the allocator specific
- * type that can be used directly by the driver to access the buffer. This can
- * be for example physical address, pointer to scatter list or IOMMU mapping.
- */
-void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-
-       if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
-               return NULL;
-
-       return call_memop(q, cookie, vb->planes[plane_no].mem_priv);
-}
-EXPORT_SYMBOL_GPL(vb2_plane_cookie);
-
-/**
- * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
- * @vb:                vb2_buffer returned from the driver
- * @state:     either VB2_BUF_STATE_DONE if the operation finished successfully
- *             or VB2_BUF_STATE_ERROR if the operation finished with an error
- *
- * This function should be called by the driver after a hardware operation on
- * a buffer is finished and the buffer may be returned to userspace. The driver
- * cannot use this buffer anymore until it is queued back to it by videobuf
- * by the means of buf_queue callback. Only buffers previously queued to the
- * driver by buf_queue can be passed to this function.
- */
-void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-       unsigned long flags;
-
-       if (vb->state != VB2_BUF_STATE_ACTIVE)
-               return;
-
-       if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
-               return;
-
-       dprintk(4, "Done processing on buffer %d, state: %d\n",
-                       vb->v4l2_buf.index, vb->state);
-
-       /* Add the buffer to the done buffers list */
-       spin_lock_irqsave(&q->done_lock, flags);
-       vb->state = state;
-       list_add_tail(&vb->done_entry, &q->done_list);
-       atomic_dec(&q->queued_count);
-       spin_unlock_irqrestore(&q->done_lock, flags);
-
-       /* Inform any processes that may be waiting for buffers */
-       wake_up(&q->done_wq);
-}
-EXPORT_SYMBOL_GPL(vb2_buffer_done);
-
-/**
- * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
- * a v4l2_buffer by the userspace
- */
-static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
-                               struct v4l2_plane *v4l2_planes)
-{
-       unsigned int plane;
-       int ret;
-
-       if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
-               /*
-                * Verify that the userspace gave us a valid array for
-                * plane information.
-                */
-               ret = __verify_planes_array(vb, b);
-               if (ret)
-                       return ret;
-
-               /* Fill in driver-provided information for OUTPUT types */
-               if (V4L2_TYPE_IS_OUTPUT(b->type)) {
-                       /*
-                        * Will have to go up to b->length when API starts
-                        * accepting variable number of planes.
-                        */
-                       for (plane = 0; plane < vb->num_planes; ++plane) {
-                               v4l2_planes[plane].bytesused =
-                                       b->m.planes[plane].bytesused;
-                               v4l2_planes[plane].data_offset =
-                                       b->m.planes[plane].data_offset;
-                       }
-               }
-
-               if (b->memory == V4L2_MEMORY_USERPTR) {
-                       for (plane = 0; plane < vb->num_planes; ++plane) {
-                               v4l2_planes[plane].m.userptr =
-                                       b->m.planes[plane].m.userptr;
-                               v4l2_planes[plane].length =
-                                       b->m.planes[plane].length;
-                       }
-               }
-       } else {
-               /*
-                * Single-planar buffers do not use planes array,
-                * so fill in relevant v4l2_buffer struct fields instead.
-                * In videobuf we use our internal V4l2_planes struct for
-                * single-planar buffers as well, for simplicity.
-                */
-               if (V4L2_TYPE_IS_OUTPUT(b->type))
-                       v4l2_planes[0].bytesused = b->bytesused;
-
-               if (b->memory == V4L2_MEMORY_USERPTR) {
-                       v4l2_planes[0].m.userptr = b->m.userptr;
-                       v4l2_planes[0].length = b->length;
-               }
-       }
-
-       vb->v4l2_buf.field = b->field;
-       vb->v4l2_buf.timestamp = b->timestamp;
-       vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
-
-       return 0;
-}
-
-/**
- * __qbuf_userptr() - handle qbuf of a USERPTR buffer
- */
-static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
-{
-       struct v4l2_plane planes[VIDEO_MAX_PLANES];
-       struct vb2_queue *q = vb->vb2_queue;
-       void *mem_priv;
-       unsigned int plane;
-       int ret;
-       int write = !V4L2_TYPE_IS_OUTPUT(q->type);
-
-       /* Verify and copy relevant information provided by the userspace */
-       ret = __fill_vb2_buffer(vb, b, planes);
-       if (ret)
-               return ret;
-
-       for (plane = 0; plane < vb->num_planes; ++plane) {
-               /* Skip the plane if already verified */
-               if (vb->v4l2_planes[plane].m.userptr &&
-                   vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
-                   && vb->v4l2_planes[plane].length == planes[plane].length)
-                       continue;
-
-               dprintk(3, "qbuf: userspace address for plane %d changed, "
-                               "reacquiring memory\n", plane);
-
-               /* Check if the provided plane buffer is large enough */
-               if (planes[plane].length < q->plane_sizes[plane]) {
-                       ret = -EINVAL;
-                       goto err;
-               }
-
-               /* Release previously acquired memory if present */
-               if (vb->planes[plane].mem_priv)
-                       call_memop(q, put_userptr, vb->planes[plane].mem_priv);
-
-               vb->planes[plane].mem_priv = NULL;
-               vb->v4l2_planes[plane].m.userptr = 0;
-               vb->v4l2_planes[plane].length = 0;
-
-               /* Acquire each plane's memory */
-               mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
-                                     planes[plane].m.userptr,
-                                     planes[plane].length, write);
-               if (IS_ERR_OR_NULL(mem_priv)) {
-                       dprintk(1, "qbuf: failed acquiring userspace "
-                                               "memory for plane %d\n", plane);
-                       ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
-                       goto err;
-               }
-               vb->planes[plane].mem_priv = mem_priv;
-       }
-
-       /*
-        * Call driver-specific initialization on the newly acquired buffer,
-        * if provided.
-        */
-       ret = call_qop(q, buf_init, vb);
-       if (ret) {
-               dprintk(1, "qbuf: buffer initialization failed\n");
-               goto err;
-       }
-
-       /*
-        * Now that everything is in order, copy relevant information
-        * provided by userspace.
-        */
-       for (plane = 0; plane < vb->num_planes; ++plane)
-               vb->v4l2_planes[plane] = planes[plane];
-
-       return 0;
-err:
-       /* In case of errors, release planes that were already acquired */
-       for (plane = 0; plane < vb->num_planes; ++plane) {
-               if (vb->planes[plane].mem_priv)
-                       call_memop(q, put_userptr, vb->planes[plane].mem_priv);
-               vb->planes[plane].mem_priv = NULL;
-               vb->v4l2_planes[plane].m.userptr = 0;
-               vb->v4l2_planes[plane].length = 0;
-       }
-
-       return ret;
-}
-
-/**
- * __qbuf_mmap() - handle qbuf of an MMAP buffer
- */
-static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
-{
-       return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
-}
-
-/**
- * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
- */
-static void __enqueue_in_driver(struct vb2_buffer *vb)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-
-       vb->state = VB2_BUF_STATE_ACTIVE;
-       atomic_inc(&q->queued_count);
-       q->ops->buf_queue(vb);
-}
-
-static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
-{
-       struct vb2_queue *q = vb->vb2_queue;
-       int ret;
-
-       switch (q->memory) {
-       case V4L2_MEMORY_MMAP:
-               ret = __qbuf_mmap(vb, b);
-               break;
-       case V4L2_MEMORY_USERPTR:
-               ret = __qbuf_userptr(vb, b);
-               break;
-       default:
-               WARN(1, "Invalid queue type\n");
-               ret = -EINVAL;
-       }
-
-       if (!ret)
-               ret = call_qop(q, buf_prepare, vb);
-       if (ret)
-               dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
-       else
-               vb->state = VB2_BUF_STATE_PREPARED;
-
-       return ret;
-}
-
-/**
- * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
- * @q:         videobuf2 queue
- * @b:         buffer structure passed from userspace to vidioc_prepare_buf
- *             handler in driver
- *
- * Should be called from vidioc_prepare_buf ioctl handler of a driver.
- * This function:
- * 1) verifies the passed buffer,
- * 2) calls buf_prepare callback in the driver (if provided), in which
- *    driver-specific buffer initialization can be performed,
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_prepare_buf handler in driver.
- */
-int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
-{
-       struct vb2_buffer *vb;
-       int ret;
-
-       if (q->fileio) {
-               dprintk(1, "%s(): file io in progress\n", __func__);
-               return -EBUSY;
-       }
-
-       if (b->type != q->type) {
-               dprintk(1, "%s(): invalid buffer type\n", __func__);
-               return -EINVAL;
-       }
-
-       if (b->index >= q->num_buffers) {
-               dprintk(1, "%s(): buffer index out of range\n", __func__);
-               return -EINVAL;
-       }
-
-       vb = q->bufs[b->index];
-       if (NULL == vb) {
-               /* Should never happen */
-               dprintk(1, "%s(): buffer is NULL\n", __func__);
-               return -EINVAL;
-       }
-
-       if (b->memory != q->memory) {
-               dprintk(1, "%s(): invalid memory type\n", __func__);
-               return -EINVAL;
-       }
-
-       if (vb->state != VB2_BUF_STATE_DEQUEUED) {
-               dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
-               return -EINVAL;
-       }
-
-       ret = __buf_prepare(vb, b);
-       if (ret < 0)
-               return ret;
-
-       __fill_v4l2_buffer(vb, b);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_prepare_buf);
-
-/**
- * vb2_qbuf() - Queue a buffer from userspace
- * @q:         videobuf2 queue
- * @b:         buffer structure passed from userspace to vidioc_qbuf handler
- *             in driver
- *
- * Should be called from vidioc_qbuf ioctl handler of a driver.
- * This function:
- * 1) verifies the passed buffer,
- * 2) if necessary, calls buf_prepare callback in the driver (if provided), in
- *    which driver-specific buffer initialization can be performed,
- * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
- *    callback for processing.
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_qbuf handler in driver.
- */
-int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
-{
-       struct rw_semaphore *mmap_sem = NULL;
-       struct vb2_buffer *vb;
-       int ret = 0;
-
-       /*
-        * In case of user pointer buffers vb2 allocator needs to get direct
-        * access to userspace pages. This requires getting read access on
-        * mmap semaphore in the current process structure. The same
-        * semaphore is taken before calling mmap operation, while both mmap
-        * and qbuf are called by the driver or v4l2 core with driver's lock
-        * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in
-        * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core
-        * release driver's lock, takes mmap_sem and then takes again driver's
-        * lock.
-        *
-        * To avoid race with other vb2 calls, which might be called after
-        * releasing driver's lock, this operation is performed at the
-        * beggining of qbuf processing. This way the queue status is
-        * consistent after getting driver's lock back.
-        */
-       if (q->memory == V4L2_MEMORY_USERPTR) {
-               mmap_sem = &current->mm->mmap_sem;
-               call_qop(q, wait_prepare, q);
-               down_read(mmap_sem);
-               call_qop(q, wait_finish, q);
-       }
-
-       if (q->fileio) {
-               dprintk(1, "qbuf: file io in progress\n");
-               ret = -EBUSY;
-               goto unlock;
-       }
-
-       if (b->type != q->type) {
-               dprintk(1, "qbuf: invalid buffer type\n");
-               ret = -EINVAL;
-               goto unlock;
-       }
-
-       if (b->index >= q->num_buffers) {
-               dprintk(1, "qbuf: buffer index out of range\n");
-               ret = -EINVAL;
-               goto unlock;
-       }
-
-       vb = q->bufs[b->index];
-       if (NULL == vb) {
-               /* Should never happen */
-               dprintk(1, "qbuf: buffer is NULL\n");
-               ret = -EINVAL;
-               goto unlock;
-       }
-
-       if (b->memory != q->memory) {
-               dprintk(1, "qbuf: invalid memory type\n");
-               ret = -EINVAL;
-               goto unlock;
-       }
-
-       switch (vb->state) {
-       case VB2_BUF_STATE_DEQUEUED:
-               ret = __buf_prepare(vb, b);
-               if (ret)
-                       goto unlock;
-       case VB2_BUF_STATE_PREPARED:
-               break;
-       default:
-               dprintk(1, "qbuf: buffer already in use\n");
-               ret = -EINVAL;
-               goto unlock;
-       }
-
-       /*
-        * Add to the queued buffers list, a buffer will stay on it until
-        * dequeued in dqbuf.
-        */
-       list_add_tail(&vb->queued_entry, &q->queued_list);
-       vb->state = VB2_BUF_STATE_QUEUED;
-
-       /*
-        * If already streaming, give the buffer to driver for processing.
-        * If not, the buffer will be given to driver on next streamon.
-        */
-       if (q->streaming)
-               __enqueue_in_driver(vb);
-
-       /* Fill buffer information for the userspace */
-       __fill_v4l2_buffer(vb, b);
-
-       dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
-unlock:
-       if (mmap_sem)
-               up_read(mmap_sem);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(vb2_qbuf);
-
-/**
- * __vb2_wait_for_done_vb() - wait for a buffer to become available
- * for dequeuing
- *
- * Will sleep if required for nonblocking == false.
- */
-static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
-{
-       /*
-        * All operations on vb_done_list are performed under done_lock
-        * spinlock protection. However, buffers may be removed from
-        * it and returned to userspace only while holding both driver's
-        * lock and the done_lock spinlock. Thus we can be sure that as
-        * long as we hold the driver's lock, the list will remain not
-        * empty if list_empty() check succeeds.
-        */
-
-       for (;;) {
-               int ret;
-
-               if (!q->streaming) {
-                       dprintk(1, "Streaming off, will not wait for buffers\n");
-                       return -EINVAL;
-               }
-
-               if (!list_empty(&q->done_list)) {
-                       /*
-                        * Found a buffer that we were waiting for.
-                        */
-                       break;
-               }
-
-               if (nonblocking) {
-                       dprintk(1, "Nonblocking and no buffers to dequeue, "
-                                                               "will not wait\n");
-                       return -EAGAIN;
-               }
-
-               /*
-                * We are streaming and blocking, wait for another buffer to
-                * become ready or for streamoff. Driver's lock is released to
-                * allow streamoff or qbuf to be called while waiting.
-                */
-               call_qop(q, wait_prepare, q);
-
-               /*
-                * All locks have been released, it is safe to sleep now.
-                */
-               dprintk(3, "Will sleep waiting for buffers\n");
-               ret = wait_event_interruptible(q->done_wq,
-                               !list_empty(&q->done_list) || !q->streaming);
-
-               /*
-                * We need to reevaluate both conditions again after reacquiring
-                * the locks or return an error if one occurred.
-                */
-               call_qop(q, wait_finish, q);
-               if (ret)
-                       return ret;
-       }
-       return 0;
-}
-
-/**
- * __vb2_get_done_vb() - get a buffer ready for dequeuing
- *
- * Will sleep if required for nonblocking == false.
- */
-static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
-                               int nonblocking)
-{
-       unsigned long flags;
-       int ret;
-
-       /*
-        * Wait for at least one buffer to become available on the done_list.
-        */
-       ret = __vb2_wait_for_done_vb(q, nonblocking);
-       if (ret)
-               return ret;
-
-       /*
-        * Driver's lock has been held since we last verified that done_list
-        * is not empty, so no need for another list_empty(done_list) check.
-        */
-       spin_lock_irqsave(&q->done_lock, flags);
-       *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
-       list_del(&(*vb)->done_entry);
-       spin_unlock_irqrestore(&q->done_lock, flags);
-
-       return 0;
-}
-
-/**
- * vb2_wait_for_all_buffers() - wait until all buffers are given back to vb2
- * @q:         videobuf2 queue
- *
- * This function will wait until all buffers that have been given to the driver
- * by buf_queue() are given back to vb2 with vb2_buffer_done(). It doesn't call
- * wait_prepare, wait_finish pair. It is intended to be called with all locks
- * taken, for example from stop_streaming() callback.
- */
-int vb2_wait_for_all_buffers(struct vb2_queue *q)
-{
-       if (!q->streaming) {
-               dprintk(1, "Streaming off, will not wait for buffers\n");
-               return -EINVAL;
-       }
-
-       wait_event(q->done_wq, !atomic_read(&q->queued_count));
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
-
-/**
- * vb2_dqbuf() - Dequeue a buffer to the userspace
- * @q:         videobuf2 queue
- * @b:         buffer structure passed from userspace to vidioc_dqbuf handler
- *             in driver
- * @nonblocking: if true, this call will not sleep waiting for a buffer if no
- *              buffers ready for dequeuing are present. Normally the driver
- *              would be passing (file->f_flags & O_NONBLOCK) here
- *
- * Should be called from vidioc_dqbuf ioctl handler of a driver.
- * This function:
- * 1) verifies the passed buffer,
- * 2) calls buf_finish callback in the driver (if provided), in which
- *    driver can perform any additional operations that may be required before
- *    returning the buffer to userspace, such as cache sync,
- * 3) the buffer struct members are filled with relevant information for
- *    the userspace.
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_dqbuf handler in driver.
- */
-int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
-{
-       struct vb2_buffer *vb = NULL;
-       int ret;
-
-       if (q->fileio) {
-               dprintk(1, "dqbuf: file io in progress\n");
-               return -EBUSY;
-       }
-
-       if (b->type != q->type) {
-               dprintk(1, "dqbuf: invalid buffer type\n");
-               return -EINVAL;
-       }
-
-       ret = __vb2_get_done_vb(q, &vb, nonblocking);
-       if (ret < 0) {
-               dprintk(1, "dqbuf: error getting next done buffer\n");
-               return ret;
-       }
-
-       ret = call_qop(q, buf_finish, vb);
-       if (ret) {
-               dprintk(1, "dqbuf: buffer finish failed\n");
-               return ret;
-       }
-
-       switch (vb->state) {
-       case VB2_BUF_STATE_DONE:
-               dprintk(3, "dqbuf: Returning done buffer\n");
-               break;
-       case VB2_BUF_STATE_ERROR:
-               dprintk(3, "dqbuf: Returning done buffer with errors\n");
-               break;
-       default:
-               dprintk(1, "dqbuf: Invalid buffer state\n");
-               return -EINVAL;
-       }
-
-       /* Fill buffer information for the userspace */
-       __fill_v4l2_buffer(vb, b);
-       /* Remove from videobuf queue */
-       list_del(&vb->queued_entry);
-
-       dprintk(1, "dqbuf of buffer %d, with state %d\n",
-                       vb->v4l2_buf.index, vb->state);
-
-       vb->state = VB2_BUF_STATE_DEQUEUED;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_dqbuf);
-
-/**
- * __vb2_queue_cancel() - cancel and stop (pause) streaming
- *
- * Removes all queued buffers from driver's queue and all buffers queued by
- * userspace from videobuf's queue. Returns to state after reqbufs.
- */
-static void __vb2_queue_cancel(struct vb2_queue *q)
-{
-       unsigned int i;
-
-       /*
-        * Tell driver to stop all transactions and release all queued
-        * buffers.
-        */
-       if (q->streaming)
-               call_qop(q, stop_streaming, q);
-       q->streaming = 0;
-
-       /*
-        * Remove all buffers from videobuf's list...
-        */
-       INIT_LIST_HEAD(&q->queued_list);
-       /*
-        * ...and done list; userspace will not receive any buffers it
-        * has not already dequeued before initiating cancel.
-        */
-       INIT_LIST_HEAD(&q->done_list);
-       atomic_set(&q->queued_count, 0);
-       wake_up_all(&q->done_wq);
-
-       /*
-        * Reinitialize all buffers for next use.
-        */
-       for (i = 0; i < q->num_buffers; ++i)
-               q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
-}
-
-/**
- * vb2_streamon - start streaming
- * @q:         videobuf2 queue
- * @type:      type argument passed from userspace to vidioc_streamon handler
- *
- * Should be called from vidioc_streamon handler of a driver.
- * This function:
- * 1) verifies current state
- * 2) passes any previously queued buffers to the driver and starts streaming
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_streamon handler in the driver.
- */
-int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
-{
-       struct vb2_buffer *vb;
-       int ret;
-
-       if (q->fileio) {
-               dprintk(1, "streamon: file io in progress\n");
-               return -EBUSY;
-       }
-
-       if (type != q->type) {
-               dprintk(1, "streamon: invalid stream type\n");
-               return -EINVAL;
-       }
-
-       if (q->streaming) {
-               dprintk(1, "streamon: already streaming\n");
-               return -EBUSY;
-       }
-
-       /*
-        * If any buffers were queued before streamon,
-        * we can now pass them to driver for processing.
-        */
-       list_for_each_entry(vb, &q->queued_list, queued_entry)
-               __enqueue_in_driver(vb);
-
-       /*
-        * Let driver notice that streaming state has been enabled.
-        */
-       ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
-       if (ret) {
-               dprintk(1, "streamon: driver refused to start streaming\n");
-               __vb2_queue_cancel(q);
-               return ret;
-       }
-
-       q->streaming = 1;
-
-       dprintk(3, "Streamon successful\n");
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_streamon);
-
-
-/**
- * vb2_streamoff - stop streaming
- * @q:         videobuf2 queue
- * @type:      type argument passed from userspace to vidioc_streamoff handler
- *
- * Should be called from vidioc_streamoff handler of a driver.
- * This function:
- * 1) verifies current state,
- * 2) stop streaming and dequeues any queued buffers, including those previously
- *    passed to the driver (after waiting for the driver to finish).
- *
- * This call can be used for pausing playback.
- * The return values from this function are intended to be directly returned
- * from vidioc_streamoff handler in the driver
- */
-int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
-{
-       if (q->fileio) {
-               dprintk(1, "streamoff: file io in progress\n");
-               return -EBUSY;
-       }
-
-       if (type != q->type) {
-               dprintk(1, "streamoff: invalid stream type\n");
-               return -EINVAL;
-       }
-
-       if (!q->streaming) {
-               dprintk(1, "streamoff: not streaming\n");
-               return -EINVAL;
-       }
-
-       /*
-        * Cancel will pause streaming and remove all buffers from the driver
-        * and videobuf, effectively returning control over them to userspace.
-        */
-       __vb2_queue_cancel(q);
-
-       dprintk(3, "Streamoff successful\n");
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_streamoff);
-
-/**
- * __find_plane_by_offset() - find plane associated with the given offset off
- */
-static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
-                       unsigned int *_buffer, unsigned int *_plane)
-{
-       struct vb2_buffer *vb;
-       unsigned int buffer, plane;
-
-       /*
-        * Go over all buffers and their planes, comparing the given offset
-        * with an offset assigned to each plane. If a match is found,
-        * return its buffer and plane numbers.
-        */
-       for (buffer = 0; buffer < q->num_buffers; ++buffer) {
-               vb = q->bufs[buffer];
-
-               for (plane = 0; plane < vb->num_planes; ++plane) {
-                       if (vb->v4l2_planes[plane].m.mem_offset == off) {
-                               *_buffer = buffer;
-                               *_plane = plane;
-                               return 0;
-                       }
-               }
-       }
-
-       return -EINVAL;
-}
-
-/**
- * vb2_mmap() - map video buffers into application address space
- * @q:         videobuf2 queue
- * @vma:       vma passed to the mmap file operation handler in the driver
- *
- * Should be called from mmap file operation handler of a driver.
- * This function maps one plane of one of the available video buffers to
- * userspace. To map whole video memory allocated on reqbufs, this function
- * has to be called once per each plane per each buffer previously allocated.
- *
- * When the userspace application calls mmap, it passes to it an offset returned
- * to it earlier by the means of vidioc_querybuf handler. That offset acts as
- * a "cookie", which is then used to identify the plane to be mapped.
- * This function finds a plane with a matching offset and a mapping is performed
- * by the means of a provided memory operation.
- *
- * The return values from this function are intended to be directly returned
- * from the mmap handler in driver.
- */
-int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
-{
-       unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
-       struct vb2_buffer *vb;
-       unsigned int buffer, plane;
-       int ret;
-
-       if (q->memory != V4L2_MEMORY_MMAP) {
-               dprintk(1, "Queue is not currently set up for mmap\n");
-               return -EINVAL;
-       }
-
-       /*
-        * Check memory area access mode.
-        */
-       if (!(vma->vm_flags & VM_SHARED)) {
-               dprintk(1, "Invalid vma flags, VM_SHARED needed\n");
-               return -EINVAL;
-       }
-       if (V4L2_TYPE_IS_OUTPUT(q->type)) {
-               if (!(vma->vm_flags & VM_WRITE)) {
-                       dprintk(1, "Invalid vma flags, VM_WRITE needed\n");
-                       return -EINVAL;
-               }
-       } else {
-               if (!(vma->vm_flags & VM_READ)) {
-                       dprintk(1, "Invalid vma flags, VM_READ needed\n");
-                       return -EINVAL;
-               }
-       }
-
-       /*
-        * Find the plane corresponding to the offset passed by userspace.
-        */
-       ret = __find_plane_by_offset(q, off, &buffer, &plane);
-       if (ret)
-               return ret;
-
-       vb = q->bufs[buffer];
-
-       ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma);
-       if (ret)
-               return ret;
-
-       dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_mmap);
-
-#ifndef CONFIG_MMU
-unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
-                                   unsigned long addr,
-                                   unsigned long len,
-                                   unsigned long pgoff,
-                                   unsigned long flags)
-{
-       unsigned long off = pgoff << PAGE_SHIFT;
-       struct vb2_buffer *vb;
-       unsigned int buffer, plane;
-       int ret;
-
-       if (q->memory != V4L2_MEMORY_MMAP) {
-               dprintk(1, "Queue is not currently set up for mmap\n");
-               return -EINVAL;
-       }
-
-       /*
-        * Find the plane corresponding to the offset passed by userspace.
-        */
-       ret = __find_plane_by_offset(q, off, &buffer, &plane);
-       if (ret)
-               return ret;
-
-       vb = q->bufs[buffer];
-
-       return (unsigned long)vb2_plane_vaddr(vb, plane);
-}
-EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
-#endif
-
-static int __vb2_init_fileio(struct vb2_queue *q, int read);
-static int __vb2_cleanup_fileio(struct vb2_queue *q);
-
-/**
- * vb2_poll() - implements poll userspace operation
- * @q:         videobuf2 queue
- * @file:      file argument passed to the poll file operation handler
- * @wait:      wait argument passed to the poll file operation handler
- *
- * This function implements poll file operation handler for a driver.
- * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will
- * be informed that the file descriptor of a video device is available for
- * reading.
- * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
- * will be reported as available for writing.
- *
- * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any
- * pending events.
- *
- * The return values from this function are intended to be directly returned
- * from poll handler in driver.
- */
-unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
-{
-       struct video_device *vfd = video_devdata(file);
-       unsigned long req_events = poll_requested_events(wait);
-       struct vb2_buffer *vb = NULL;
-       unsigned int res = 0;
-       unsigned long flags;
-
-       if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
-               struct v4l2_fh *fh = file->private_data;
-
-               if (v4l2_event_pending(fh))
-                       res = POLLPRI;
-               else if (req_events & POLLPRI)
-                       poll_wait(file, &fh->wait, wait);
-       }
-
-       /*
-        * Start file I/O emulator only if streaming API has not been used yet.
-        */
-       if (q->num_buffers == 0 && q->fileio == NULL) {
-               if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
-                               (req_events & (POLLIN | POLLRDNORM))) {
-                       if (__vb2_init_fileio(q, 1))
-                               return res | POLLERR;
-               }
-               if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
-                               (req_events & (POLLOUT | POLLWRNORM))) {
-                       if (__vb2_init_fileio(q, 0))
-                               return res | POLLERR;
-                       /*
-                        * Write to OUTPUT queue can be done immediately.
-                        */
-                       return res | POLLOUT | POLLWRNORM;
-               }
-       }
-
-       /*
-        * There is nothing to wait for if no buffers have already been queued.
-        */
-       if (list_empty(&q->queued_list))
-               return res | POLLERR;
-
-       poll_wait(file, &q->done_wq, wait);
-
-       /*
-        * Take first buffer available for dequeuing.
-        */
-       spin_lock_irqsave(&q->done_lock, flags);
-       if (!list_empty(&q->done_list))
-               vb = list_first_entry(&q->done_list, struct vb2_buffer,
-                                       done_entry);
-       spin_unlock_irqrestore(&q->done_lock, flags);
-
-       if (vb && (vb->state == VB2_BUF_STATE_DONE
-                       || vb->state == VB2_BUF_STATE_ERROR)) {
-               return (V4L2_TYPE_IS_OUTPUT(q->type)) ?
-                               res | POLLOUT | POLLWRNORM :
-                               res | POLLIN | POLLRDNORM;
-       }
-       return res;
-}
-EXPORT_SYMBOL_GPL(vb2_poll);
-
-/**
- * vb2_queue_init() - initialize a videobuf2 queue
- * @q:         videobuf2 queue; this structure should be allocated in driver
- *
- * The vb2_queue structure should be allocated by the driver. The driver is
- * responsible of clearing it's content and setting initial values for some
- * required entries before calling this function.
- * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
- * to the struct vb2_queue description in include/media/videobuf2-core.h
- * for more information.
- */
-int vb2_queue_init(struct vb2_queue *q)
-{
-       BUG_ON(!q);
-       BUG_ON(!q->ops);
-       BUG_ON(!q->mem_ops);
-       BUG_ON(!q->type);
-       BUG_ON(!q->io_modes);
-
-       BUG_ON(!q->ops->queue_setup);
-       BUG_ON(!q->ops->buf_queue);
-
-       INIT_LIST_HEAD(&q->queued_list);
-       INIT_LIST_HEAD(&q->done_list);
-       spin_lock_init(&q->done_lock);
-       init_waitqueue_head(&q->done_wq);
-
-       if (q->buf_struct_size == 0)
-               q->buf_struct_size = sizeof(struct vb2_buffer);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_queue_init);
-
-/**
- * vb2_queue_release() - stop streaming, release the queue and free memory
- * @q:         videobuf2 queue
- *
- * This function stops streaming and performs necessary clean ups, including
- * freeing video buffer memory. The driver is responsible for freeing
- * the vb2_queue structure itself.
- */
-void vb2_queue_release(struct vb2_queue *q)
-{
-       __vb2_cleanup_fileio(q);
-       __vb2_queue_cancel(q);
-       __vb2_queue_free(q, q->num_buffers);
-}
-EXPORT_SYMBOL_GPL(vb2_queue_release);
-
-/**
- * struct vb2_fileio_buf - buffer context used by file io emulator
- *
- * vb2 provides a compatibility layer and emulator of file io (read and
- * write) calls on top of streaming API. This structure is used for
- * tracking context related to the buffers.
- */
-struct vb2_fileio_buf {
-       void *vaddr;
-       unsigned int size;
-       unsigned int pos;
-       unsigned int queued:1;
-};
-
-/**
- * struct vb2_fileio_data - queue context used by file io emulator
- *
- * vb2 provides a compatibility layer and emulator of file io (read and
- * write) calls on top of streaming API. For proper operation it required
- * this structure to save the driver state between each call of the read
- * or write function.
- */
-struct vb2_fileio_data {
-       struct v4l2_requestbuffers req;
-       struct v4l2_buffer b;
-       struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
-       unsigned int index;
-       unsigned int q_count;
-       unsigned int dq_count;
-       unsigned int flags;
-};
-
-/**
- * __vb2_init_fileio() - initialize file io emulator
- * @q:         videobuf2 queue
- * @read:      mode selector (1 means read, 0 means write)
- */
-static int __vb2_init_fileio(struct vb2_queue *q, int read)
-{
-       struct vb2_fileio_data *fileio;
-       int i, ret;
-       unsigned int count = 0;
-
-       /*
-        * Sanity check
-        */
-       if ((read && !(q->io_modes & VB2_READ)) ||
-          (!read && !(q->io_modes & VB2_WRITE)))
-               BUG();
-
-       /*
-        * Check if device supports mapping buffers to kernel virtual space.
-        */
-       if (!q->mem_ops->vaddr)
-               return -EBUSY;
-
-       /*
-        * Check if streaming api has not been already activated.
-        */
-       if (q->streaming || q->num_buffers > 0)
-               return -EBUSY;
-
-       /*
-        * Start with count 1, driver can increase it in queue_setup()
-        */
-       count = 1;
-
-       dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n",
-               (read) ? "read" : "write", count, q->io_flags);
-
-       fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
-       if (fileio == NULL)
-               return -ENOMEM;
-
-       fileio->flags = q->io_flags;
-
-       /*
-        * Request buffers and use MMAP type to force driver
-        * to allocate buffers by itself.
-        */
-       fileio->req.count = count;
-       fileio->req.memory = V4L2_MEMORY_MMAP;
-       fileio->req.type = q->type;
-       ret = vb2_reqbufs(q, &fileio->req);
-       if (ret)
-               goto err_kfree;
-
-       /*
-        * Check if plane_count is correct
-        * (multiplane buffers are not supported).
-        */
-       if (q->bufs[0]->num_planes != 1) {
-               ret = -EBUSY;
-               goto err_reqbufs;
-       }
-
-       /*
-        * Get kernel address of each buffer.
-        */
-       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)
-                       goto err_reqbufs;
-               fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
-       }
-
-       /*
-        * Read mode requires pre queuing of all buffers.
-        */
-       if (read) {
-               /*
-                * Queue all buffers.
-                */
-               for (i = 0; i < q->num_buffers; i++) {
-                       struct v4l2_buffer *b = &fileio->b;
-                       memset(b, 0, sizeof(*b));
-                       b->type = q->type;
-                       b->memory = q->memory;
-                       b->index = i;
-                       ret = vb2_qbuf(q, b);
-                       if (ret)
-                               goto err_reqbufs;
-                       fileio->bufs[i].queued = 1;
-               }
-
-               /*
-                * Start streaming.
-                */
-               ret = vb2_streamon(q, q->type);
-               if (ret)
-                       goto err_reqbufs;
-       }
-
-       q->fileio = fileio;
-
-       return ret;
-
-err_reqbufs:
-       fileio->req.count = 0;
-       vb2_reqbufs(q, &fileio->req);
-
-err_kfree:
-       kfree(fileio);
-       return ret;
-}
-
-/**
- * __vb2_cleanup_fileio() - free resourced used by file io emulator
- * @q:         videobuf2 queue
- */
-static int __vb2_cleanup_fileio(struct vb2_queue *q)
-{
-       struct vb2_fileio_data *fileio = q->fileio;
-
-       if (fileio) {
-               /*
-                * Hack fileio context to enable direct calls to vb2 ioctl
-                * interface.
-                */
-               q->fileio = NULL;
-
-               vb2_streamoff(q, q->type);
-               fileio->req.count = 0;
-               vb2_reqbufs(q, &fileio->req);
-               kfree(fileio);
-               dprintk(3, "file io emulator closed\n");
-       }
-       return 0;
-}
-
-/**
- * __vb2_perform_fileio() - perform a single file io (read or write) operation
- * @q:         videobuf2 queue
- * @data:      pointed to target userspace buffer
- * @count:     number of bytes to read or write
- * @ppos:      file handle position tracking pointer
- * @nonblock:  mode selector (1 means blocking calls, 0 means nonblocking)
- * @read:      access mode selector (1 means read, 0 means write)
- */
-static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
-               loff_t *ppos, int nonblock, int read)
-{
-       struct vb2_fileio_data *fileio;
-       struct vb2_fileio_buf *buf;
-       int ret, index;
-
-       dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n",
-               read ? "read" : "write", (long)*ppos, count,
-               nonblock ? "non" : "");
-
-       if (!data)
-               return -EINVAL;
-
-       /*
-        * Initialize emulator on first call.
-        */
-       if (!q->fileio) {
-               ret = __vb2_init_fileio(q, read);
-               dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
-               if (ret)
-                       return ret;
-       }
-       fileio = q->fileio;
-
-       /*
-        * Hack fileio context to enable direct calls to vb2 ioctl interface.
-        * The pointer will be restored before returning from this function.
-        */
-       q->fileio = NULL;
-
-       index = fileio->index;
-       buf = &fileio->bufs[index];
-
-       /*
-        * Check if we need to dequeue the buffer.
-        */
-       if (buf->queued) {
-               struct vb2_buffer *vb;
-
-               /*
-                * Call vb2_dqbuf to get buffer back.
-                */
-               memset(&fileio->b, 0, sizeof(fileio->b));
-               fileio->b.type = q->type;
-               fileio->b.memory = q->memory;
-               fileio->b.index = index;
-               ret = vb2_dqbuf(q, &fileio->b, nonblock);
-               dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
-               if (ret)
-                       goto end;
-               fileio->dq_count += 1;
-
-               /*
-                * Get number of bytes filled by the driver
-                */
-               vb = q->bufs[index];
-               buf->size = vb2_get_plane_payload(vb, 0);
-               buf->queued = 0;
-       }
-
-       /*
-        * Limit count on last few bytes of the buffer.
-        */
-       if (buf->pos + count > buf->size) {
-               count = buf->size - buf->pos;
-               dprintk(5, "reducing read count: %zd\n", count);
-       }
-
-       /*
-        * Transfer data to userspace.
-        */
-       dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n",
-               count, index, buf->pos);
-       if (read)
-               ret = copy_to_user(data, buf->vaddr + buf->pos, count);
-       else
-               ret = copy_from_user(buf->vaddr + buf->pos, data, count);
-       if (ret) {
-               dprintk(3, "file io: error copying data\n");
-               ret = -EFAULT;
-               goto end;
-       }
-
-       /*
-        * Update counters.
-        */
-       buf->pos += count;
-       *ppos += count;
-
-       /*
-        * Queue next buffer if required.
-        */
-       if (buf->pos == buf->size ||
-          (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATELY))) {
-               /*
-                * Check if this is the last buffer to read.
-                */
-               if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
-                   fileio->dq_count == 1) {
-                       dprintk(3, "file io: read limit reached\n");
-                       /*
-                        * Restore fileio pointer and release the context.
-                        */
-                       q->fileio = fileio;
-                       return __vb2_cleanup_fileio(q);
-               }
-
-               /*
-                * Call vb2_qbuf and give buffer to the driver.
-                */
-               memset(&fileio->b, 0, sizeof(fileio->b));
-               fileio->b.type = q->type;
-               fileio->b.memory = q->memory;
-               fileio->b.index = index;
-               fileio->b.bytesused = buf->pos;
-               ret = vb2_qbuf(q, &fileio->b);
-               dprintk(5, "file io: vb2_dbuf result: %d\n", ret);
-               if (ret)
-                       goto end;
-
-               /*
-                * Buffer has been queued, update the status
-                */
-               buf->pos = 0;
-               buf->queued = 1;
-               buf->size = q->bufs[0]->v4l2_planes[0].length;
-               fileio->q_count += 1;
-
-               /*
-                * Switch to the next buffer
-                */
-               fileio->index = (index + 1) % q->num_buffers;
-
-               /*
-                * Start streaming if required.
-                */
-               if (!read && !q->streaming) {
-                       ret = vb2_streamon(q, q->type);
-                       if (ret)
-                               goto end;
-               }
-       }
-
-       /*
-        * Return proper number of bytes processed.
-        */
-       if (ret == 0)
-               ret = count;
-end:
-       /*
-        * Restore the fileio context and block vb2 ioctl interface.
-        */
-       q->fileio = fileio;
-       return ret;
-}
-
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
-               loff_t *ppos, int nonblocking)
-{
-       return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
-}
-EXPORT_SYMBOL_GPL(vb2_read);
-
-size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
-               loff_t *ppos, int nonblocking)
-{
-       return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
-}
-EXPORT_SYMBOL_GPL(vb2_write);
-
-
-/*
- * The following functions are not part of the vb2 core API, but are helper
- * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
- * and struct vb2_ops.
- * They contain boilerplate code that most if not all drivers have to do
- * and so they simplify the driver code.
- */
-
-/* The queue is busy if there is a owner and you are not that owner. */
-static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file)
-{
-       return vdev->queue->owner && vdev->queue->owner != file->private_data;
-}
-
-/* vb2 ioctl helpers */
-
-int vb2_ioctl_reqbufs(struct file *file, void *priv,
-                         struct v4l2_requestbuffers *p)
-{
-       struct video_device *vdev = video_devdata(file);
-       int res = __verify_memory_type(vdev->queue, p->memory, p->type);
-
-       if (res)
-               return res;
-       if (vb2_queue_is_busy(vdev, file))
-               return -EBUSY;
-       res = __reqbufs(vdev->queue, p);
-       /* If count == 0, then the owner has released all buffers and he
-          is no longer owner of the queue. Otherwise we have a new owner. */
-       if (res == 0)
-               vdev->queue->owner = p->count ? file->private_data : NULL;
-       return res;
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
-
-int vb2_ioctl_create_bufs(struct file *file, void *priv,
-                         struct v4l2_create_buffers *p)
-{
-       struct video_device *vdev = video_devdata(file);
-       int res = __verify_memory_type(vdev->queue, p->memory, p->format.type);
-
-       p->index = vdev->queue->num_buffers;
-       /* If count == 0, then just check if memory and type are valid.
-          Any -EBUSY result from __verify_memory_type can be mapped to 0. */
-       if (p->count == 0)
-               return res != -EBUSY ? res : 0;
-       if (res)
-               return res;
-       if (vb2_queue_is_busy(vdev, file))
-               return -EBUSY;
-       res = __create_bufs(vdev->queue, p);
-       if (res == 0)
-               vdev->queue->owner = file->private_data;
-       return res;
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs);
-
-int vb2_ioctl_prepare_buf(struct file *file, void *priv,
-                         struct v4l2_buffer *p)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       if (vb2_queue_is_busy(vdev, file))
-               return -EBUSY;
-       return vb2_prepare_buf(vdev->queue, p);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
-
-int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       /* No need to call vb2_queue_is_busy(), anyone can query buffers. */
-       return vb2_querybuf(vdev->queue, p);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf);
-
-int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       if (vb2_queue_is_busy(vdev, file))
-               return -EBUSY;
-       return vb2_qbuf(vdev->queue, p);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
-
-int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       if (vb2_queue_is_busy(vdev, file))
-               return -EBUSY;
-       return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
-
-int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       if (vb2_queue_is_busy(vdev, file))
-               return -EBUSY;
-       return vb2_streamon(vdev->queue, i);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_streamon);
-
-int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       if (vb2_queue_is_busy(vdev, file))
-               return -EBUSY;
-       return vb2_streamoff(vdev->queue, i);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
-
-/* v4l2_file_operations helpers */
-
-int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       return vb2_mmap(vdev->queue, vma);
-}
-EXPORT_SYMBOL_GPL(vb2_fop_mmap);
-
-int vb2_fop_release(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       if (file->private_data == vdev->queue->owner) {
-               vb2_queue_release(vdev->queue);
-               vdev->queue->owner = NULL;
-       }
-       return v4l2_fh_release(file);
-}
-EXPORT_SYMBOL_GPL(vb2_fop_release);
-
-ssize_t vb2_fop_write(struct file *file, char __user *buf,
-               size_t count, loff_t *ppos)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
-       int err = -EBUSY;
-
-       if (lock && mutex_lock_interruptible(lock))
-               return -ERESTARTSYS;
-       if (vb2_queue_is_busy(vdev, file))
-               goto exit;
-       err = vb2_write(vdev->queue, buf, count, ppos,
-                      file->f_flags & O_NONBLOCK);
-       if (err >= 0)
-               vdev->queue->owner = file->private_data;
-exit:
-       if (lock)
-               mutex_unlock(lock);
-       return err;
-}
-EXPORT_SYMBOL_GPL(vb2_fop_write);
-
-ssize_t vb2_fop_read(struct file *file, char __user *buf,
-               size_t count, loff_t *ppos)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
-       int err = -EBUSY;
-
-       if (lock && mutex_lock_interruptible(lock))
-               return -ERESTARTSYS;
-       if (vb2_queue_is_busy(vdev, file))
-               goto exit;
-       err = vb2_read(vdev->queue, buf, count, ppos,
-                      file->f_flags & O_NONBLOCK);
-       if (err >= 0)
-               vdev->queue->owner = file->private_data;
-exit:
-       if (lock)
-               mutex_unlock(lock);
-       return err;
-}
-EXPORT_SYMBOL_GPL(vb2_fop_read);
-
-unsigned int vb2_fop_poll(struct file *file, poll_table *wait)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct vb2_queue *q = vdev->queue;
-       struct mutex *lock = q->lock ? q->lock : vdev->lock;
-       unsigned long req_events = poll_requested_events(wait);
-       unsigned res;
-       void *fileio;
-       bool must_lock = false;
-
-       /* Try to be smart: only lock if polling might start fileio,
-          otherwise locking will only introduce unwanted delays. */
-       if (q->num_buffers == 0 && q->fileio == NULL) {
-               if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
-                               (req_events & (POLLIN | POLLRDNORM)))
-                       must_lock = true;
-               else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
-                               (req_events & (POLLOUT | POLLWRNORM)))
-                       must_lock = true;
-       }
-
-       /* If locking is needed, but this helper doesn't know how, then you
-          shouldn't be using this helper but you should write your own. */
-       WARN_ON(must_lock && !lock);
-
-       if (must_lock && lock && mutex_lock_interruptible(lock))
-               return POLLERR;
-
-       fileio = q->fileio;
-
-       res = vb2_poll(vdev->queue, file, wait);
-
-       /* If fileio was started, then we have a new queue owner. */
-       if (must_lock && !fileio && q->fileio)
-               q->owner = file->private_data;
-       if (must_lock && lock)
-               mutex_unlock(lock);
-       return res;
-}
-EXPORT_SYMBOL_GPL(vb2_fop_poll);
-
-#ifndef CONFIG_MMU
-unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
-               unsigned long len, unsigned long pgoff, unsigned long flags)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
-}
-EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
-#endif
-
-/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
-
-void vb2_ops_wait_prepare(struct vb2_queue *vq)
-{
-       mutex_unlock(vq->lock);
-}
-EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
-
-void vb2_ops_wait_finish(struct vb2_queue *vq)
-{
-       mutex_lock(vq->lock);
-}
-EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
-
-MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
-MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c
deleted file mode 100644 (file)
index 4b71326..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2
- *
- * Copyright (C) 2010 Samsung Electronics
- *
- * Author: Pawel Osciak <pawel@osciak.com>
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/videobuf2-memops.h>
-
-struct vb2_dc_conf {
-       struct device           *dev;
-};
-
-struct vb2_dc_buf {
-       struct vb2_dc_conf              *conf;
-       void                            *vaddr;
-       dma_addr_t                      dma_addr;
-       unsigned long                   size;
-       struct vm_area_struct           *vma;
-       atomic_t                        refcount;
-       struct vb2_vmarea_handler       handler;
-};
-
-static void vb2_dma_contig_put(void *buf_priv);
-
-static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
-{
-       struct vb2_dc_conf *conf = alloc_ctx;
-       struct vb2_dc_buf *buf;
-
-       buf = kzalloc(sizeof *buf, GFP_KERNEL);
-       if (!buf)
-               return ERR_PTR(-ENOMEM);
-
-       buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr,
-                                       GFP_KERNEL);
-       if (!buf->vaddr) {
-               dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
-                       size);
-               kfree(buf);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       buf->conf = conf;
-       buf->size = size;
-
-       buf->handler.refcount = &buf->refcount;
-       buf->handler.put = vb2_dma_contig_put;
-       buf->handler.arg = buf;
-
-       atomic_inc(&buf->refcount);
-
-       return buf;
-}
-
-static void vb2_dma_contig_put(void *buf_priv)
-{
-       struct vb2_dc_buf *buf = buf_priv;
-
-       if (atomic_dec_and_test(&buf->refcount)) {
-               dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
-                                 buf->dma_addr);
-               kfree(buf);
-       }
-}
-
-static void *vb2_dma_contig_cookie(void *buf_priv)
-{
-       struct vb2_dc_buf *buf = buf_priv;
-
-       return &buf->dma_addr;
-}
-
-static void *vb2_dma_contig_vaddr(void *buf_priv)
-{
-       struct vb2_dc_buf *buf = buf_priv;
-       if (!buf)
-               return NULL;
-
-       return buf->vaddr;
-}
-
-static unsigned int vb2_dma_contig_num_users(void *buf_priv)
-{
-       struct vb2_dc_buf *buf = buf_priv;
-
-       return atomic_read(&buf->refcount);
-}
-
-static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
-{
-       struct vb2_dc_buf *buf = buf_priv;
-
-       if (!buf) {
-               printk(KERN_ERR "No buffer to map\n");
-               return -EINVAL;
-       }
-
-       return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size,
-                                 &vb2_common_vm_ops, &buf->handler);
-}
-
-static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
-                                       unsigned long size, int write)
-{
-       struct vb2_dc_buf *buf;
-       struct vm_area_struct *vma;
-       dma_addr_t dma_addr = 0;
-       int ret;
-
-       buf = kzalloc(sizeof *buf, GFP_KERNEL);
-       if (!buf)
-               return ERR_PTR(-ENOMEM);
-
-       ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr);
-       if (ret) {
-               printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
-                               vaddr);
-               kfree(buf);
-               return ERR_PTR(ret);
-       }
-
-       buf->size = size;
-       buf->dma_addr = dma_addr;
-       buf->vma = vma;
-
-       return buf;
-}
-
-static void vb2_dma_contig_put_userptr(void *mem_priv)
-{
-       struct vb2_dc_buf *buf = mem_priv;
-
-       if (!buf)
-               return;
-
-       vb2_put_vma(buf->vma);
-       kfree(buf);
-}
-
-const struct vb2_mem_ops vb2_dma_contig_memops = {
-       .alloc          = vb2_dma_contig_alloc,
-       .put            = vb2_dma_contig_put,
-       .cookie         = vb2_dma_contig_cookie,
-       .vaddr          = vb2_dma_contig_vaddr,
-       .mmap           = vb2_dma_contig_mmap,
-       .get_userptr    = vb2_dma_contig_get_userptr,
-       .put_userptr    = vb2_dma_contig_put_userptr,
-       .num_users      = vb2_dma_contig_num_users,
-};
-EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
-
-void *vb2_dma_contig_init_ctx(struct device *dev)
-{
-       struct vb2_dc_conf *conf;
-
-       conf = kzalloc(sizeof *conf, GFP_KERNEL);
-       if (!conf)
-               return ERR_PTR(-ENOMEM);
-
-       conf->dev = dev;
-
-       return conf;
-}
-EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
-
-void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
-{
-       kfree(alloc_ctx);
-}
-EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
-
-MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
-MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c
deleted file mode 100644 (file)
index 25c3b36..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2
- *
- * Copyright (C) 2010 Samsung Electronics
- *
- * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/scatterlist.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-memops.h>
-#include <media/videobuf2-dma-sg.h>
-
-struct vb2_dma_sg_buf {
-       void                            *vaddr;
-       struct page                     **pages;
-       int                             write;
-       int                             offset;
-       struct vb2_dma_sg_desc          sg_desc;
-       atomic_t                        refcount;
-       struct vb2_vmarea_handler       handler;
-};
-
-static void vb2_dma_sg_put(void *buf_priv);
-
-static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size)
-{
-       struct vb2_dma_sg_buf *buf;
-       int i;
-
-       buf = kzalloc(sizeof *buf, GFP_KERNEL);
-       if (!buf)
-               return NULL;
-
-       buf->vaddr = NULL;
-       buf->write = 0;
-       buf->offset = 0;
-       buf->sg_desc.size = size;
-       buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-
-       buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages *
-                                     sizeof(*buf->sg_desc.sglist));
-       if (!buf->sg_desc.sglist)
-               goto fail_sglist_alloc;
-       sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
-
-       buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
-                            GFP_KERNEL);
-       if (!buf->pages)
-               goto fail_pages_array_alloc;
-
-       for (i = 0; i < buf->sg_desc.num_pages; ++i) {
-               buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
-               if (NULL == buf->pages[i])
-                       goto fail_pages_alloc;
-               sg_set_page(&buf->sg_desc.sglist[i],
-                           buf->pages[i], PAGE_SIZE, 0);
-       }
-
-       buf->handler.refcount = &buf->refcount;
-       buf->handler.put = vb2_dma_sg_put;
-       buf->handler.arg = buf;
-
-       atomic_inc(&buf->refcount);
-
-       printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n",
-               __func__, buf->sg_desc.num_pages);
-       return buf;
-
-fail_pages_alloc:
-       while (--i >= 0)
-               __free_page(buf->pages[i]);
-       kfree(buf->pages);
-
-fail_pages_array_alloc:
-       vfree(buf->sg_desc.sglist);
-
-fail_sglist_alloc:
-       kfree(buf);
-       return NULL;
-}
-
-static void vb2_dma_sg_put(void *buf_priv)
-{
-       struct vb2_dma_sg_buf *buf = buf_priv;
-       int i = buf->sg_desc.num_pages;
-
-       if (atomic_dec_and_test(&buf->refcount)) {
-               printk(KERN_DEBUG "%s: Freeing buffer of %d pages\n", __func__,
-                       buf->sg_desc.num_pages);
-               if (buf->vaddr)
-                       vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
-               vfree(buf->sg_desc.sglist);
-               while (--i >= 0)
-                       __free_page(buf->pages[i]);
-               kfree(buf->pages);
-               kfree(buf);
-       }
-}
-
-static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
-                                   unsigned long size, int write)
-{
-       struct vb2_dma_sg_buf *buf;
-       unsigned long first, last;
-       int num_pages_from_user, i;
-
-       buf = kzalloc(sizeof *buf, GFP_KERNEL);
-       if (!buf)
-               return NULL;
-
-       buf->vaddr = NULL;
-       buf->write = write;
-       buf->offset = vaddr & ~PAGE_MASK;
-       buf->sg_desc.size = size;
-
-       first = (vaddr           & PAGE_MASK) >> PAGE_SHIFT;
-       last  = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
-       buf->sg_desc.num_pages = last - first + 1;
-
-       buf->sg_desc.sglist = vzalloc(
-               buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
-       if (!buf->sg_desc.sglist)
-               goto userptr_fail_sglist_alloc;
-
-       sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
-
-       buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
-                            GFP_KERNEL);
-       if (!buf->pages)
-               goto userptr_fail_pages_array_alloc;
-
-       num_pages_from_user = get_user_pages(current, current->mm,
-                                            vaddr & PAGE_MASK,
-                                            buf->sg_desc.num_pages,
-                                            write,
-                                            1, /* force */
-                                            buf->pages,
-                                            NULL);
-
-       if (num_pages_from_user != buf->sg_desc.num_pages)
-               goto userptr_fail_get_user_pages;
-
-       sg_set_page(&buf->sg_desc.sglist[0], buf->pages[0],
-                   PAGE_SIZE - buf->offset, buf->offset);
-       size -= PAGE_SIZE - buf->offset;
-       for (i = 1; i < buf->sg_desc.num_pages; ++i) {
-               sg_set_page(&buf->sg_desc.sglist[i], buf->pages[i],
-                           min_t(size_t, PAGE_SIZE, size), 0);
-               size -= min_t(size_t, PAGE_SIZE, size);
-       }
-       return buf;
-
-userptr_fail_get_user_pages:
-       printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n",
-              num_pages_from_user, buf->sg_desc.num_pages);
-       while (--num_pages_from_user >= 0)
-               put_page(buf->pages[num_pages_from_user]);
-       kfree(buf->pages);
-
-userptr_fail_pages_array_alloc:
-       vfree(buf->sg_desc.sglist);
-
-userptr_fail_sglist_alloc:
-       kfree(buf);
-       return NULL;
-}
-
-/*
- * @put_userptr: inform the allocator that a USERPTR buffer will no longer
- *              be used
- */
-static void vb2_dma_sg_put_userptr(void *buf_priv)
-{
-       struct vb2_dma_sg_buf *buf = buf_priv;
-       int i = buf->sg_desc.num_pages;
-
-       printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n",
-              __func__, buf->sg_desc.num_pages);
-       if (buf->vaddr)
-               vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
-       while (--i >= 0) {
-               if (buf->write)
-                       set_page_dirty_lock(buf->pages[i]);
-               put_page(buf->pages[i]);
-       }
-       vfree(buf->sg_desc.sglist);
-       kfree(buf->pages);
-       kfree(buf);
-}
-
-static void *vb2_dma_sg_vaddr(void *buf_priv)
-{
-       struct vb2_dma_sg_buf *buf = buf_priv;
-
-       BUG_ON(!buf);
-
-       if (!buf->vaddr)
-               buf->vaddr = vm_map_ram(buf->pages,
-                                       buf->sg_desc.num_pages,
-                                       -1,
-                                       PAGE_KERNEL);
-
-       /* add offset in case userptr is not page-aligned */
-       return buf->vaddr + buf->offset;
-}
-
-static unsigned int vb2_dma_sg_num_users(void *buf_priv)
-{
-       struct vb2_dma_sg_buf *buf = buf_priv;
-
-       return atomic_read(&buf->refcount);
-}
-
-static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
-{
-       struct vb2_dma_sg_buf *buf = buf_priv;
-       unsigned long uaddr = vma->vm_start;
-       unsigned long usize = vma->vm_end - vma->vm_start;
-       int i = 0;
-
-       if (!buf) {
-               printk(KERN_ERR "No memory to map\n");
-               return -EINVAL;
-       }
-
-       do {
-               int ret;
-
-               ret = vm_insert_page(vma, uaddr, buf->pages[i++]);
-               if (ret) {
-                       printk(KERN_ERR "Remapping memory, error: %d\n", ret);
-                       return ret;
-               }
-
-               uaddr += PAGE_SIZE;
-               usize -= PAGE_SIZE;
-       } while (usize > 0);
-
-
-       /*
-        * Use common vm_area operations to track buffer refcount.
-        */
-       vma->vm_private_data    = &buf->handler;
-       vma->vm_ops             = &vb2_common_vm_ops;
-
-       vma->vm_ops->open(vma);
-
-       return 0;
-}
-
-static void *vb2_dma_sg_cookie(void *buf_priv)
-{
-       struct vb2_dma_sg_buf *buf = buf_priv;
-
-       return &buf->sg_desc;
-}
-
-const struct vb2_mem_ops vb2_dma_sg_memops = {
-       .alloc          = vb2_dma_sg_alloc,
-       .put            = vb2_dma_sg_put,
-       .get_userptr    = vb2_dma_sg_get_userptr,
-       .put_userptr    = vb2_dma_sg_put_userptr,
-       .vaddr          = vb2_dma_sg_vaddr,
-       .mmap           = vb2_dma_sg_mmap,
-       .num_users      = vb2_dma_sg_num_users,
-       .cookie         = vb2_dma_sg_cookie,
-};
-EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
-
-MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
-MODULE_AUTHOR("Andrzej Pietrasiewicz");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
deleted file mode 100644 (file)
index 504cd4c..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * videobuf2-memops.c - generic memory handling routines for videobuf2
- *
- * Copyright (C) 2010 Samsung Electronics
- *
- * Author: Pawel Osciak <pawel@osciak.com>
- *        Marek Szyprowski <m.szyprowski@samsung.com>
- *
- * 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.
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/vmalloc.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/file.h>
-
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-memops.h>
-
-/**
- * vb2_get_vma() - acquire and lock the virtual memory area
- * @vma:       given virtual memory area
- *
- * This function attempts to acquire an area mapped in the userspace for
- * the duration of a hardware operation. The area is "locked" by performing
- * the same set of operation that are done when process calls fork() and
- * memory areas are duplicated.
- *
- * Returns a copy of a virtual memory region on success or NULL.
- */
-struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma)
-{
-       struct vm_area_struct *vma_copy;
-
-       vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
-       if (vma_copy == NULL)
-               return NULL;
-
-       if (vma->vm_ops && vma->vm_ops->open)
-               vma->vm_ops->open(vma);
-
-       if (vma->vm_file)
-               get_file(vma->vm_file);
-
-       memcpy(vma_copy, vma, sizeof(*vma));
-
-       vma_copy->vm_mm = NULL;
-       vma_copy->vm_next = NULL;
-       vma_copy->vm_prev = NULL;
-
-       return vma_copy;
-}
-EXPORT_SYMBOL_GPL(vb2_get_vma);
-
-/**
- * vb2_put_userptr() - release a userspace virtual memory area
- * @vma:       virtual memory region associated with the area to be released
- *
- * This function releases the previously acquired memory area after a hardware
- * operation.
- */
-void vb2_put_vma(struct vm_area_struct *vma)
-{
-       if (!vma)
-               return;
-
-       if (vma->vm_ops && vma->vm_ops->close)
-               vma->vm_ops->close(vma);
-
-       if (vma->vm_file)
-               fput(vma->vm_file);
-
-       kfree(vma);
-}
-EXPORT_SYMBOL_GPL(vb2_put_vma);
-
-/**
- * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory
- * @vaddr:     starting virtual address of the area to be verified
- * @size:      size of the area
- * @res_paddr: will return physical address for the given vaddr
- * @res_vma:   will return locked copy of struct vm_area for the given area
- *
- * This function will go through memory area of size @size mapped at @vaddr and
- * verify that the underlying physical pages are contiguous. If they are
- * contiguous the virtual memory area is locked and a @res_vma is filled with
- * the copy and @res_pa set to the physical address of the buffer.
- *
- * Returns 0 on success.
- */
-int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
-                          struct vm_area_struct **res_vma, dma_addr_t *res_pa)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-       unsigned long offset, start, end;
-       unsigned long this_pfn, prev_pfn;
-       dma_addr_t pa = 0;
-
-       start = vaddr;
-       offset = start & ~PAGE_MASK;
-       end = start + size;
-
-       vma = find_vma(mm, start);
-
-       if (vma == NULL || vma->vm_end < end)
-               return -EFAULT;
-
-       for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
-               int ret = follow_pfn(vma, start, &this_pfn);
-               if (ret)
-                       return ret;
-
-               if (prev_pfn == 0)
-                       pa = this_pfn << PAGE_SHIFT;
-               else if (this_pfn != prev_pfn + 1)
-                       return -EFAULT;
-
-               prev_pfn = this_pfn;
-       }
-
-       /*
-        * Memory is contigous, lock vma and return to the caller
-        */
-       *res_vma = vb2_get_vma(vma);
-       if (*res_vma == NULL)
-               return -ENOMEM;
-
-       *res_pa = pa + offset;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
-
-/**
- * vb2_mmap_pfn_range() - map physical pages to userspace
- * @vma:       virtual memory region for the mapping
- * @paddr:     starting physical address of the memory to be mapped
- * @size:      size of the memory to be mapped
- * @vm_ops:    vm operations to be assigned to the created area
- * @priv:      private data to be associated with the area
- *
- * Returns 0 on success.
- */
-int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
-                               unsigned long size,
-                               const struct vm_operations_struct *vm_ops,
-                               void *priv)
-{
-       int ret;
-
-       size = min_t(unsigned long, vma->vm_end - vma->vm_start, size);
-
-       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-       ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
-                               size, vma->vm_page_prot);
-       if (ret) {
-               printk(KERN_ERR "Remapping memory failed, error: %d\n", ret);
-               return ret;
-       }
-
-       vma->vm_flags           |= VM_DONTEXPAND | VM_RESERVED;
-       vma->vm_private_data    = priv;
-       vma->vm_ops             = vm_ops;
-
-       vma->vm_ops->open(vma);
-
-       pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
-                       __func__, paddr, vma->vm_start, size);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range);
-
-/**
- * vb2_common_vm_open() - increase refcount of the vma
- * @vma:       virtual memory region for the mapping
- *
- * This function adds another user to the provided vma. It expects
- * struct vb2_vmarea_handler pointer in vma->vm_private_data.
- */
-static void vb2_common_vm_open(struct vm_area_struct *vma)
-{
-       struct vb2_vmarea_handler *h = vma->vm_private_data;
-
-       pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
-              __func__, h, atomic_read(h->refcount), vma->vm_start,
-              vma->vm_end);
-
-       atomic_inc(h->refcount);
-}
-
-/**
- * vb2_common_vm_close() - decrease refcount of the vma
- * @vma:       virtual memory region for the mapping
- *
- * This function releases the user from the provided vma. It expects
- * struct vb2_vmarea_handler pointer in vma->vm_private_data.
- */
-static void vb2_common_vm_close(struct vm_area_struct *vma)
-{
-       struct vb2_vmarea_handler *h = vma->vm_private_data;
-
-       pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
-              __func__, h, atomic_read(h->refcount), vma->vm_start,
-              vma->vm_end);
-
-       h->put(h->arg);
-}
-
-/**
- * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped
- * video buffers
- */
-const struct vm_operations_struct vb2_common_vm_ops = {
-       .open = vb2_common_vm_open,
-       .close = vb2_common_vm_close,
-};
-EXPORT_SYMBOL_GPL(vb2_common_vm_ops);
-
-MODULE_DESCRIPTION("common memory handling routines for videobuf2");
-MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c
deleted file mode 100644 (file)
index 94efa04..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
- *
- * Copyright (C) 2010 Samsung Electronics
- *
- * Author: Pawel Osciak <pawel@osciak.com>
- *
- * 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.
- */
-
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/videobuf2-memops.h>
-
-struct vb2_vmalloc_buf {
-       void                            *vaddr;
-       struct page                     **pages;
-       struct vm_area_struct           *vma;
-       int                             write;
-       unsigned long                   size;
-       unsigned int                    n_pages;
-       atomic_t                        refcount;
-       struct vb2_vmarea_handler       handler;
-};
-
-static void vb2_vmalloc_put(void *buf_priv);
-
-static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
-{
-       struct vb2_vmalloc_buf *buf;
-
-       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
-       if (!buf)
-               return NULL;
-
-       buf->size = size;
-       buf->vaddr = vmalloc_user(buf->size);
-       buf->handler.refcount = &buf->refcount;
-       buf->handler.put = vb2_vmalloc_put;
-       buf->handler.arg = buf;
-
-       if (!buf->vaddr) {
-               pr_debug("vmalloc of size %ld failed\n", buf->size);
-               kfree(buf);
-               return NULL;
-       }
-
-       atomic_inc(&buf->refcount);
-       return buf;
-}
-
-static void vb2_vmalloc_put(void *buf_priv)
-{
-       struct vb2_vmalloc_buf *buf = buf_priv;
-
-       if (atomic_dec_and_test(&buf->refcount)) {
-               vfree(buf->vaddr);
-               kfree(buf);
-       }
-}
-
-static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
-                                    unsigned long size, int write)
-{
-       struct vb2_vmalloc_buf *buf;
-       unsigned long first, last;
-       int n_pages, offset;
-       struct vm_area_struct *vma;
-       dma_addr_t physp;
-
-       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
-       if (!buf)
-               return NULL;
-
-       buf->write = write;
-       offset = vaddr & ~PAGE_MASK;
-       buf->size = size;
-
-
-       vma = find_vma(current->mm, vaddr);
-       if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
-               if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
-                       goto fail_pages_array_alloc;
-               buf->vma = vma;
-               buf->vaddr = ioremap_nocache(physp, size);
-               if (!buf->vaddr)
-                       goto fail_pages_array_alloc;
-       } else {
-               first = vaddr >> PAGE_SHIFT;
-               last  = (vaddr + size - 1) >> PAGE_SHIFT;
-               buf->n_pages = last - first + 1;
-               buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
-                                    GFP_KERNEL);
-               if (!buf->pages)
-                       goto fail_pages_array_alloc;
-
-               /* current->mm->mmap_sem is taken by videobuf2 core */
-               n_pages = get_user_pages(current, current->mm,
-                                        vaddr & PAGE_MASK, buf->n_pages,
-                                        write, 1, /* force */
-                                        buf->pages, NULL);
-               if (n_pages != buf->n_pages)
-                       goto fail_get_user_pages;
-
-               buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
-                                       PAGE_KERNEL);
-               if (!buf->vaddr)
-                       goto fail_get_user_pages;
-       }
-
-       buf->vaddr += offset;
-       return buf;
-
-fail_get_user_pages:
-       pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
-                buf->n_pages);
-       while (--n_pages >= 0)
-               put_page(buf->pages[n_pages]);
-       kfree(buf->pages);
-
-fail_pages_array_alloc:
-       kfree(buf);
-
-       return NULL;
-}
-
-static void vb2_vmalloc_put_userptr(void *buf_priv)
-{
-       struct vb2_vmalloc_buf *buf = buf_priv;
-       unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
-       unsigned int i;
-
-       if (buf->pages) {
-               if (vaddr)
-                       vm_unmap_ram((void *)vaddr, buf->n_pages);
-               for (i = 0; i < buf->n_pages; ++i) {
-                       if (buf->write)
-                               set_page_dirty_lock(buf->pages[i]);
-                       put_page(buf->pages[i]);
-               }
-               kfree(buf->pages);
-       } else {
-               if (buf->vma)
-                       vb2_put_vma(buf->vma);
-               iounmap(buf->vaddr);
-       }
-       kfree(buf);
-}
-
-static void *vb2_vmalloc_vaddr(void *buf_priv)
-{
-       struct vb2_vmalloc_buf *buf = buf_priv;
-
-       if (!buf->vaddr) {
-               pr_err("Address of an unallocated plane requested "
-                      "or cannot map user pointer\n");
-               return NULL;
-       }
-
-       return buf->vaddr;
-}
-
-static unsigned int vb2_vmalloc_num_users(void *buf_priv)
-{
-       struct vb2_vmalloc_buf *buf = buf_priv;
-       return atomic_read(&buf->refcount);
-}
-
-static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
-{
-       struct vb2_vmalloc_buf *buf = buf_priv;
-       int ret;
-
-       if (!buf) {
-               pr_err("No memory to map\n");
-               return -EINVAL;
-       }
-
-       ret = remap_vmalloc_range(vma, buf->vaddr, 0);
-       if (ret) {
-               pr_err("Remapping vmalloc memory, error: %d\n", ret);
-               return ret;
-       }
-
-       /*
-        * Make sure that vm_areas for 2 buffers won't be merged together
-        */
-       vma->vm_flags           |= VM_DONTEXPAND;
-
-       /*
-        * Use common vm_area operations to track buffer refcount.
-        */
-       vma->vm_private_data    = &buf->handler;
-       vma->vm_ops             = &vb2_common_vm_ops;
-
-       vma->vm_ops->open(vma);
-
-       return 0;
-}
-
-const struct vb2_mem_ops vb2_vmalloc_memops = {
-       .alloc          = vb2_vmalloc_alloc,
-       .put            = vb2_vmalloc_put,
-       .get_userptr    = vb2_vmalloc_get_userptr,
-       .put_userptr    = vb2_vmalloc_put_userptr,
-       .vaddr          = vb2_vmalloc_vaddr,
-       .mmap           = vb2_vmalloc_mmap,
-       .num_users      = vb2_vmalloc_num_users,
-};
-EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
-
-MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
-MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
-MODULE_LICENSE("GPL");