V4L/DVB: bttv: remove bogus prio check in g_frequency
[safe/jmp/linux-2.6] / drivers / media / video / bt8xx / bttv-driver.c
index 6e1ddad..fcfc934 100644 (file)
@@ -9,6 +9,16 @@
     some v4l2 code lines are taken from Justin's bttv2 driver which is
     (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
 
+    V4L1 removal from:
+    (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+
+    Fixes to be fully V4L2 compliant by
+    (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+
+    Cropping and overscan support
+    Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+    Sponsored by OPQ Systems AB
+
     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
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp_lock.h>
 #include <linux/interrupt.h>
 #include <linux/kdev_t.h>
 #include "bttvp.h"
 #include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
 #include <media/tvaudio.h>
 #include <media/msp3400.h>
 
@@ -48,7 +60,7 @@
 
 
 unsigned int bttv_num;                 /* number of Bt848s in use */
-struct bttv bttvs[BTTV_MAX];
+struct bttv *bttvs[BTTV_MAX];
 
 unsigned int bttv_debug;
 unsigned int bttv_verbose = 1;
@@ -64,11 +76,13 @@ static unsigned int radio[BTTV_MAX];
 static unsigned int irq_debug;
 static unsigned int gbuffers = 8;
 static unsigned int gbufsize = 0x208000;
+static unsigned int reset_crop = 1;
 
-static int video_nr = -1;
-static int radio_nr = -1;
-static int vbi_nr = -1;
+static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
 static int debug_latency;
+static int disable_ir;
 
 static unsigned int fdsr;
 
@@ -85,7 +99,6 @@ static unsigned int irq_iswitch;
 static unsigned int uv_ratio    = 50;
 static unsigned int full_luma_range;
 static unsigned int coring;
-extern int no_overlay;
 
 /* API features (turn on/off stuff for testing) */
 static unsigned int v4l2        = 1;
@@ -96,13 +109,12 @@ module_param(bttv_gpio,         int, 0644);
 module_param(bttv_debug,        int, 0644);
 module_param(irq_debug,         int, 0644);
 module_param(debug_latency,     int, 0644);
+module_param(disable_ir,        int, 0444);
 
 module_param(fdsr,              int, 0444);
-module_param(video_nr,          int, 0444);
-module_param(radio_nr,          int, 0444);
-module_param(vbi_nr,            int, 0444);
 module_param(gbuffers,          int, 0444);
 module_param(gbufsize,          int, 0444);
+module_param(reset_crop,        int, 0444);
 
 module_param(v4l2,              int, 0644);
 module_param(bigendian,         int, 0644);
@@ -119,7 +131,10 @@ module_param(uv_ratio,          int, 0444);
 module_param(full_luma_range,   int, 0444);
 module_param(coring,            int, 0444);
 
-module_param_array(radio, int, NULL, 0444);
+module_param_array(radio,       int, NULL, 0444);
+module_param_array(video_nr,    int, NULL, 0444);
+module_param_array(radio_nr,    int, NULL, 0444);
+module_param_array(vbi_nr,      int, NULL, 0444);
 
 MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
 MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
@@ -127,8 +142,11 @@ MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
 MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
 MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
 MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
 MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
 MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
+                "is 1 (yes) for compatibility with older applications");
 MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
 MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
 MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
@@ -139,6 +157,9 @@ MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
 MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
 MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
 MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
 
 MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
 MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
@@ -147,13 +168,32 @@ MODULE_LICENSE("GPL");
 /* ----------------------------------------------------------------------- */
 /* sysfs                                                                   */
 
-static ssize_t show_card(struct class_device *cd, char *buf)
+static ssize_t show_card(struct device *cd,
+                        struct device_attribute *attr, char *buf)
 {
-       struct video_device *vfd = to_video_device(cd);
-       struct bttv *btv = dev_get_drvdata(vfd->dev);
+       struct video_device *vfd = container_of(cd, struct video_device, dev);
+       struct bttv *btv = video_get_drvdata(vfd);
        return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
 }
-static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+
+/* ----------------------------------------------------------------------- */
+/* dvb auto-load setup                                                     */
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+       request_module("dvb-bt8xx");
+}
+
+static void request_modules(struct bttv *dev)
+{
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
 
 /* ----------------------------------------------------------------------- */
 /* static data                                                             */
@@ -192,6 +232,33 @@ static u8 SRAM_Table[][60] =
        }
 };
 
+/* minhdelayx1 first video pixel we can capture on a line and
+   hdelayx1    start of active video, both relative to rising edge of
+               /HRESET pulse (0H) in 1 / fCLKx1.
+   swidth      width of active video and
+   totalwidth  total line width, both in 1 / fCLKx1.
+   sqwidth     total line width in square pixels.
+   vdelay      start of active video in 2 * field lines relative to
+               trailing edge of /VRESET pulse (VDELAY register).
+   sheight     height of active video in 2 * field lines.
+   videostart0 ITU-R frame line number of the line corresponding
+               to vdelay in the first field. */
+#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth,     \
+               vdelay, sheight, videostart0)                            \
+       .cropcap.bounds.left = minhdelayx1,                              \
+       /* * 2 because vertically we count field lines times two, */     \
+       /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */           \
+       .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
+       /* 4 is a safety margin at the end of the line. */               \
+       .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4,        \
+       .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY,      \
+       .cropcap.defrect.left = hdelayx1,                                \
+       .cropcap.defrect.top = (videostart0) * 2,                        \
+       .cropcap.defrect.width = swidth,                                 \
+       .cropcap.defrect.height = sheight,                               \
+       .cropcap.pixelaspect.numerator = totalwidth,                     \
+       .cropcap.pixelaspect.denominator = sqwidth,
+
 const struct bttv_tvnorm bttv_tvnorms[] = {
        /* PAL-BDGHI */
        /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
@@ -210,11 +277,26 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .hdelayx1       = 186,
                .hactivex1      = 924,
                .vdelay         = 0x20,
-               .vbipack        = 255,
+               .vbipack        = 255, /* min (2048 / 4, 0x1ff) & 0xff */
                .sram           = 0,
                /* ITU-R frame line number of the first VBI line
-                  we can capture, of the first and second field. */
-               .vbistart       = { 7,320 },
+                  we can capture, of the first and second field.
+                  The last line is determined by cropcap.bounds. */
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* Should be (768 * 1135 + 944 / 2) / 944.
+                          cropcap.defrect is used for image width
+                          checks, so we keep the old value 924. */
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+               /* bt878 (and bt848?) can capture another
+                  line below active video. */
+               .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
        },{
                .v4l2_id        = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
                .name           = "NTSC",
@@ -229,9 +311,18 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .hdelayx1       = 128,
                .hactivex1      = 910,
                .vdelay         = 0x1a,
-               .vbipack        = 144,
+               .vbipack        = 144, /* min (1600 / 4, 0x1ff) & 0xff */
                .sram           = 1,
                .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 128,
+                       /* Should be (640 * 910 + 780 / 2) / 780? */
+                       /* swidth */ 768,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
        },{
                .v4l2_id        = V4L2_STD_SECAM,
                .name           = "SECAM",
@@ -249,6 +340,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .vbipack        = 255,
                .sram           = 0, /* like PAL, correct? */
                .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
        },{
                .v4l2_id        = V4L2_STD_PAL_Nc,
                .name           = "PAL-Nc",
@@ -266,6 +365,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .vbipack        = 144,
                .sram           = -1,
                .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 130,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
        },{
                .v4l2_id        = V4L2_STD_PAL_M,
                .name           = "PAL-M",
@@ -283,6 +390,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .vbipack        = 144,
                .sram           = -1,
                .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 135,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
        },{
                .v4l2_id        = V4L2_STD_PAL_N,
                .name           = "PAL-N",
@@ -299,7 +414,15 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .vdelay         = 0x20,
                .vbipack        = 144,
                .sram           = -1,
-               .vbistart       = { 7, 320},
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ (768 * 1135 + 944 / 2) / 944,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
        },{
                .v4l2_id        = V4L2_STD_NTSC_M_JP,
                .name           = "NTSC-JP",
@@ -316,7 +439,15 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .vdelay         = 0x16,
                .vbipack        = 144,
                .sram           = -1,
-               .vbistart       = {10, 273},
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 135,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x16,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
        },{
                /* that one hopefully works with the strange timing
                 * which video recorders produce when playing a NTSC
@@ -338,6 +469,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
                .vtotal         = 524,
                .sram           = -1,
                .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
        }
 };
 static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
@@ -345,31 +484,27 @@ static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
 /* ----------------------------------------------------------------------- */
 /* bttv format list
    packed pixel formats must come first */
-static const struct bttv_format bttv_formats[] = {
+static const struct bttv_format formats[] = {
        {
                .name     = "8 bpp, gray",
-               .palette  = VIDEO_PALETTE_GREY,
                .fourcc   = V4L2_PIX_FMT_GREY,
                .btformat = BT848_COLOR_FMT_Y8,
                .depth    = 8,
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "8 bpp, dithered color",
-               .palette  = VIDEO_PALETTE_HI240,
                .fourcc   = V4L2_PIX_FMT_HI240,
                .btformat = BT848_COLOR_FMT_RGB8,
                .depth    = 8,
                .flags    = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
        },{
                .name     = "15 bpp RGB, le",
-               .palette  = VIDEO_PALETTE_RGB555,
                .fourcc   = V4L2_PIX_FMT_RGB555,
                .btformat = BT848_COLOR_FMT_RGB15,
                .depth    = 16,
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "15 bpp RGB, be",
-               .palette  = -1,
                .fourcc   = V4L2_PIX_FMT_RGB555X,
                .btformat = BT848_COLOR_FMT_RGB15,
                .btswap   = 0x03, /* byteswap */
@@ -377,14 +512,12 @@ static const struct bttv_format bttv_formats[] = {
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "16 bpp RGB, le",
-               .palette  = VIDEO_PALETTE_RGB565,
                .fourcc   = V4L2_PIX_FMT_RGB565,
                .btformat = BT848_COLOR_FMT_RGB16,
                .depth    = 16,
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "16 bpp RGB, be",
-               .palette  = -1,
                .fourcc   = V4L2_PIX_FMT_RGB565X,
                .btformat = BT848_COLOR_FMT_RGB16,
                .btswap   = 0x03, /* byteswap */
@@ -392,21 +525,18 @@ static const struct bttv_format bttv_formats[] = {
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "24 bpp RGB, le",
-               .palette  = VIDEO_PALETTE_RGB24,
                .fourcc   = V4L2_PIX_FMT_BGR24,
                .btformat = BT848_COLOR_FMT_RGB24,
                .depth    = 24,
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "32 bpp RGB, le",
-               .palette  = VIDEO_PALETTE_RGB32,
                .fourcc   = V4L2_PIX_FMT_BGR32,
                .btformat = BT848_COLOR_FMT_RGB32,
                .depth    = 32,
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "32 bpp RGB, be",
-               .palette  = -1,
                .fourcc   = V4L2_PIX_FMT_RGB32,
                .btformat = BT848_COLOR_FMT_RGB32,
                .btswap   = 0x0f, /* byte+word swap */
@@ -414,21 +544,18 @@ static const struct bttv_format bttv_formats[] = {
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "4:2:2, packed, YUYV",
-               .palette  = VIDEO_PALETTE_YUV422,
                .fourcc   = V4L2_PIX_FMT_YUYV,
                .btformat = BT848_COLOR_FMT_YUY2,
                .depth    = 16,
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "4:2:2, packed, YUYV",
-               .palette  = VIDEO_PALETTE_YUYV,
                .fourcc   = V4L2_PIX_FMT_YUYV,
                .btformat = BT848_COLOR_FMT_YUY2,
                .depth    = 16,
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "4:2:2, packed, UYVY",
-               .palette  = VIDEO_PALETTE_UYVY,
                .fourcc   = V4L2_PIX_FMT_UYVY,
                .btformat = BT848_COLOR_FMT_YUY2,
                .btswap   = 0x03, /* byteswap */
@@ -436,7 +563,6 @@ static const struct bttv_format bttv_formats[] = {
                .flags    = FORMAT_FLAGS_PACKED,
        },{
                .name     = "4:2:2, planar, Y-Cb-Cr",
-               .palette  = VIDEO_PALETTE_YUV422P,
                .fourcc   = V4L2_PIX_FMT_YUV422P,
                .btformat = BT848_COLOR_FMT_YCrCb422,
                .depth    = 16,
@@ -445,7 +571,6 @@ static const struct bttv_format bttv_formats[] = {
                .vshift   = 0,
        },{
                .name     = "4:2:0, planar, Y-Cb-Cr",
-               .palette  = VIDEO_PALETTE_YUV420P,
                .fourcc   = V4L2_PIX_FMT_YUV420,
                .btformat = BT848_COLOR_FMT_YCrCb422,
                .depth    = 12,
@@ -454,7 +579,6 @@ static const struct bttv_format bttv_formats[] = {
                .vshift   = 1,
        },{
                .name     = "4:2:0, planar, Y-Cr-Cb",
-               .palette  = -1,
                .fourcc   = V4L2_PIX_FMT_YVU420,
                .btformat = BT848_COLOR_FMT_YCrCb422,
                .depth    = 12,
@@ -463,7 +587,6 @@ static const struct bttv_format bttv_formats[] = {
                .vshift   = 1,
        },{
                .name     = "4:1:1, planar, Y-Cb-Cr",
-               .palette  = VIDEO_PALETTE_YUV411P,
                .fourcc   = V4L2_PIX_FMT_YUV411P,
                .btformat = BT848_COLOR_FMT_YCrCb411,
                .depth    = 12,
@@ -472,7 +595,6 @@ static const struct bttv_format bttv_formats[] = {
                .vshift   = 0,
        },{
                .name     = "4:1:0, planar, Y-Cb-Cr",
-               .palette  = VIDEO_PALETTE_YUV410P,
                .fourcc   = V4L2_PIX_FMT_YUV410,
                .btformat = BT848_COLOR_FMT_YCrCb411,
                .depth    = 9,
@@ -481,7 +603,6 @@ static const struct bttv_format bttv_formats[] = {
                .vshift   = 2,
        },{
                .name     = "4:1:0, planar, Y-Cr-Cb",
-               .palette  = -1,
                .fourcc   = V4L2_PIX_FMT_YVU410,
                .btformat = BT848_COLOR_FMT_YCrCb411,
                .depth    = 9,
@@ -490,14 +611,13 @@ static const struct bttv_format bttv_formats[] = {
                .vshift   = 2,
        },{
                .name     = "raw scanlines",
-               .palette  = VIDEO_PALETTE_RAW,
                .fourcc   = -1,
                .btformat = BT848_COLOR_FMT_RAW,
                .depth    = 8,
                .flags    = FORMAT_FLAGS_RAW,
        }
 };
-static const unsigned int BTTV_FORMATS = ARRAY_SIZE(bttv_formats);
+static const unsigned int FORMATS = ARRAY_SIZE(formats);
 
 /* ----------------------------------------------------------------------- */
 
@@ -673,30 +793,104 @@ static const struct v4l2_queryctrl bttv_ctls[] = {
 
 
 };
-static const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls);
+
+static const struct v4l2_queryctrl *ctrl_by_id(int id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+               if (bttv_ctls[i].id == id)
+                       return bttv_ctls+i;
+
+       return NULL;
+}
 
 /* ----------------------------------------------------------------------- */
 /* resource management                                                     */
 
+/*
+   RESOURCE_    allocated by                freed by
+
+   VIDEO_READ   bttv_read 1)                bttv_read 2)
+
+   VIDEO_STREAM VIDIOC_STREAMON             VIDIOC_STREAMOFF
+                VIDIOC_QBUF 1)              bttv_release
+                VIDIOCMCAPTURE 1)
+
+   OVERLAY      VIDIOCCAPTURE on            VIDIOCCAPTURE off
+                VIDIOC_OVERLAY on           VIDIOC_OVERLAY off
+                3)                          bttv_release
+
+   VBI          VIDIOC_STREAMON             VIDIOC_STREAMOFF
+                VIDIOC_QBUF 1)              bttv_release
+                bttv_read, bttv_poll 1) 4)
+
+   1) The resource must be allocated when we enter buffer prepare functions
+      and remain allocated while buffers are in the DMA queue.
+   2) This is a single frame read.
+   3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
+      RESOURCE_OVERLAY is allocated.
+   4) This is a continuous read, implies VIDIOC_STREAMON.
+
+   Note this driver permits video input and standard changes regardless if
+   resources are allocated.
+*/
+
+#define VBI_RESOURCES (RESOURCE_VBI)
+#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
+                        RESOURCE_VIDEO_STREAM | \
+                        RESOURCE_OVERLAY)
+
 static
 int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit)
 {
+       int xbits; /* mutual exclusive resources */
+
        if (fh->resources & bit)
                /* have it already allocated */
                return 1;
 
+       xbits = bit;
+       if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
+               xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
+
        /* is it free? */
-       mutex_lock(&btv->reslock);
-       if (btv->resources & bit) {
+       mutex_lock(&btv->lock);
+       if (btv->resources & xbits) {
                /* no, someone else uses it */
-               mutex_unlock(&btv->reslock);
-               return 0;
+               goto fail;
+       }
+
+       if ((bit & VIDEO_RESOURCES)
+           && 0 == (btv->resources & VIDEO_RESOURCES)) {
+               /* Do crop - use current, don't - use default parameters. */
+               __s32 top = btv->crop[!!fh->do_crop].rect.top;
+
+               if (btv->vbi_end > top)
+                       goto fail;
+
+               /* We cannot capture the same line as video and VBI data.
+                  Claim scan lines crop[].rect.top to bottom. */
+               btv->crop_start = top;
+       } else if (bit & VBI_RESOURCES) {
+               __s32 end = fh->vbi_fmt.end;
+
+               if (end > btv->crop_start)
+                       goto fail;
+
+               /* Claim scan lines above fh->vbi_fmt.end. */
+               btv->vbi_end = end;
        }
+
        /* it's free, grab it */
        fh->resources  |= bit;
        btv->resources |= bit;
-       mutex_unlock(&btv->reslock);
+       mutex_unlock(&btv->lock);
        return 1;
+
+ fail:
+       mutex_unlock(&btv->lock);
+       return 0;
 }
 
 static
@@ -711,6 +905,35 @@ int locked_btres(struct bttv *btv, int bit)
        return (btv->resources & bit);
 }
 
+/* Call with btv->lock down. */
+static void
+disclaim_vbi_lines(struct bttv *btv)
+{
+       btv->vbi_end = 0;
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_video_lines(struct bttv *btv)
+{
+       const struct bttv_tvnorm *tvnorm;
+       u8 crop;
+
+       tvnorm = &bttv_tvnorms[btv->tvnorm];
+       btv->crop_start = tvnorm->cropcap.bounds.top
+               + tvnorm->cropcap.bounds.height;
+
+       /* VBI capturing ends at VDELAY, start of video capturing, no
+          matter how many lines the VBI RISC program expects. When video
+          capturing is off, it shall no longer "preempt" VBI capturing,
+          so we set VDELAY to maximum. */
+       crop = btread(BT848_E_CROP) | 0xc0;
+       btwrite(crop, BT848_E_CROP);
+       btwrite(0xfe, BT848_E_VDELAY_LO);
+       btwrite(crop, BT848_O_CROP);
+       btwrite(0xfe, BT848_O_VDELAY_LO);
+}
+
 static
 void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits)
 {
@@ -718,10 +941,19 @@ void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits)
                /* trying to free ressources not allocated by us ... */
                printk("bttv: BUG! (btres)\n");
        }
-       mutex_lock(&btv->reslock);
+       mutex_lock(&btv->lock);
        fh->resources  &= ~bits;
        btv->resources &= ~bits;
-       mutex_unlock(&btv->reslock);
+
+       bits = btv->resources;
+
+       if (0 == (bits & VIDEO_RESOURCES))
+               disclaim_video_lines(btv);
+
+       if (0 == (bits & VBI_RESOURCES))
+               disclaim_vbi_lines(btv);
+
+       mutex_unlock(&btv->lock);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -813,7 +1045,7 @@ static void bt848A_set_timing(struct bttv *btv)
        int table_idx = bttv_tvnorms[btv->tvnorm].sram;
        int fsc       = bttv_tvnorms[btv->tvnorm].Fsc;
 
-       if (UNSET == bttv_tvcards[btv->c.type].muxsel[btv->input]) {
+       if (btv->input == btv->dig) {
                dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
                        btv->c.nr,table_idx);
 
@@ -915,7 +1147,7 @@ video_mux(struct bttv *btv, unsigned int input)
                btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
                btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
        }
-       mux = bttv_tvcards[btv->c.type].muxsel[input] & 3;
+       mux = bttv_muxsel(btv, input);
        btaor(mux<<5, ~(3<<5), BT848_IFORM);
        dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
                btv->c.nr,input,mux);
@@ -936,7 +1168,6 @@ audio_mux(struct bttv *btv, int input, int mute)
 {
        int gpio_val, signal;
        struct v4l2_control ctrl;
-       struct i2c_client *c;
 
        gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
                   bttv_tvcards[btv->c.type].gpiomask);
@@ -953,7 +1184,16 @@ audio_mux(struct bttv *btv, int input, int mute)
        else
                gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
 
-       gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+       switch (btv->c.type) {
+       case BTTV_BOARD_VOODOOTV_FM:
+       case BTTV_BOARD_VOODOOTV_200:
+               gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
+               break;
+
+       default:
+               gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+       }
+
        if (bttv_gpio)
                bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
        if (in_interrupt())
@@ -961,10 +1201,9 @@ audio_mux(struct bttv *btv, int input, int mute)
 
        ctrl.id = V4L2_CID_AUDIO_MUTE;
        ctrl.value = btv->mute;
-       bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, &ctrl);
-       c = btv->i2c_msp34xx_client;
-       if (c) {
-               struct v4l2_routing route;
+       bttv_call_all(btv, core, s_ctrl, &ctrl);
+       if (btv->sd_msp34xx) {
+               u32 in;
 
                /* Note: the inputs tuner/radio/extern/intern are translated
                   to msp routings. This assumes common behavior for all msp3400
@@ -973,11 +1212,11 @@ audio_mux(struct bttv *btv, int input, int mute)
                   For now this is sufficient. */
                switch (input) {
                case TVAUDIO_INPUT_RADIO:
-                       route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                       in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
                                    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
                        break;
                case TVAUDIO_INPUT_EXTERN:
-                       route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+                       in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
                                    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
                        break;
                case TVAUDIO_INPUT_INTERN:
@@ -986,24 +1225,27 @@ audio_mux(struct bttv *btv, int input, int mute)
                           input is the BTTV_BOARD_AVERMEDIA98. I wonder how
                           that was tested. My guess is that the whole INTERN
                           input does not work. */
-                       route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                       in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
                                    MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
                        break;
                case TVAUDIO_INPUT_TUNER:
                default:
-                       route.input = MSP_INPUT_DEFAULT;
+                       /* This is the only card that uses TUNER2, and afaik,
+                          is the only difference between the VOODOOTV_FM
+                          and VOODOOTV_200 */
+                       if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
+                               in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
+                                       MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
+                       else
+                               in = MSP_INPUT_DEFAULT;
                        break;
                }
-               route.output = MSP_OUTPUT_DEFAULT;
-               c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+               v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
+                              in, MSP_OUTPUT_DEFAULT, 0);
        }
-       c = btv->i2c_tvaudio_client;
-       if (c) {
-               struct v4l2_routing route;
-
-               route.input = input;
-               route.output = 0;
-               c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+       if (btv->sd_tvaudio) {
+               v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
+                               input, 0, 0);
        }
        return 0;
 }
@@ -1021,26 +1263,59 @@ audio_input(struct bttv *btv, int input)
 }
 
 static void
-i2c_vidiocschan(struct bttv *btv)
+bttv_crop_calc_limits(struct bttv_crop *c)
 {
-       v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id;
+       /* Scale factor min. 1:1, max. 16:1. Min. image size
+          48 x 32. Scaled width must be a multiple of 4. */
+
+       if (1) {
+               /* For bug compatibility with VIDIOCGCAP and image
+                  size checks in earlier driver versions. */
+               c->min_scaled_width = 48;
+               c->min_scaled_height = 32;
+       } else {
+               c->min_scaled_width =
+                       (max(48, c->rect.width >> 4) + 3) & ~3;
+               c->min_scaled_height =
+                       max(32, c->rect.height >> 4);
+       }
 
-       bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std);
-       if (btv->c.type == BTTV_BOARD_VOODOOTV_FM)
-               bttv_tda9880_setnorm(btv,btv->tvnorm);
+       c->max_scaled_width  = c->rect.width & ~3;
+       c->max_scaled_height = c->rect.height;
+}
+
+static void
+bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+{
+       c->rect = bttv_tvnorms[norm].cropcap.defrect;
+       bttv_crop_calc_limits(c);
 }
 
+/* Call with btv->lock down. */
 static int
 set_tvnorm(struct bttv *btv, unsigned int norm)
 {
        const struct bttv_tvnorm *tvnorm;
+       v4l2_std_id id;
 
-       if (norm < 0 || norm >= BTTV_TVNORMS)
-               return -EINVAL;
+       BUG_ON(norm >= BTTV_TVNORMS);
+       BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
 
-       btv->tvnorm = norm;
        tvnorm = &bttv_tvnorms[norm];
 
+       if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
+                   sizeof (tvnorm->cropcap))) {
+               bttv_crop_reset(&btv->crop[0], norm);
+               btv->crop[1] = btv->crop[0]; /* current = default */
+
+               if (0 == (btv->resources & VIDEO_RESOURCES)) {
+                       btv->crop_start = tvnorm->cropcap.bounds.top
+                               + tvnorm->cropcap.bounds.height;
+               }
+       }
+
+       btv->tvnorm = norm;
+
        btwrite(tvnorm->adelay, BT848_ADELAY);
        btwrite(tvnorm->bdelay, BT848_BDELAY);
        btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
@@ -1051,14 +1326,19 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
 
        switch (btv->c.type) {
        case BTTV_BOARD_VOODOOTV_FM:
-               bttv_tda9880_setnorm(btv,norm);
+       case BTTV_BOARD_VOODOOTV_200:
+               bttv_tda9880_setnorm(btv, gpio_read());
                break;
        }
+       id = tvnorm->v4l2_id;
+       bttv_call_all(btv, core, s_std, id);
+
        return 0;
 }
 
+/* Call with btv->lock down. */
 static void
-set_input(struct bttv *btv, unsigned int input)
+set_input(struct bttv *btv, unsigned int input, unsigned int norm)
 {
        unsigned long flags;
 
@@ -1075,10 +1355,9 @@ set_input(struct bttv *btv, unsigned int input)
        } else {
                video_mux(btv,input);
        }
-       audio_input(btv,(input == bttv_tvcards[btv->c.type].tuner ?
-                      TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN));
-       set_tvnorm(btv,btv->tvnorm);
-       i2c_vidiocschan(btv);
+       audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
+                        TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+       set_tvnorm(btv, norm);
 }
 
 static void init_irqreg(struct bttv *btv)
@@ -1096,7 +1375,7 @@ static void init_irqreg(struct bttv *btv)
                        (btv->gpioirq ? BT848_INT_GPINT : 0) |
                        BT848_INT_SCERR |
                        (fdsr ? BT848_INT_FDSR : 0) |
-                       BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
+                       BT848_INT_RISCI | BT848_INT_OCERR |
                        BT848_INT_FMTCHG|BT848_INT_HLOCK|
                        BT848_INT_I2CDONE,
                        BT848_INT_MASK);
@@ -1168,41 +1447,15 @@ static void bttv_reinit_bt848(struct bttv *btv)
 
        init_bt848(btv);
        btv->pll.pll_current = -1;
-       set_input(btv,btv->input);
+       set_input(btv, btv->input, btv->tvnorm);
 }
 
-static int get_control(struct bttv *btv, struct v4l2_control *c)
+static int bttv_g_ctrl(struct file *file, void *priv,
+                                       struct v4l2_control *c)
 {
-       struct video_audio va;
-       int i;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
 
-       for (i = 0; i < BTTV_CTLS; i++)
-               if (bttv_ctls[i].id == c->id)
-                       break;
-       if (i == BTTV_CTLS)
-               return -EINVAL;
-       if (btv->audio_hook && i >= 4 && i <= 8) {
-               memset(&va,0,sizeof(va));
-               btv->audio_hook(btv,&va,0);
-               switch (c->id) {
-               case V4L2_CID_AUDIO_MUTE:
-                       c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0;
-                       break;
-               case V4L2_CID_AUDIO_VOLUME:
-                       c->value = va.volume;
-                       break;
-               case V4L2_CID_AUDIO_BALANCE:
-                       c->value = va.balance;
-                       break;
-               case V4L2_CID_AUDIO_BASS:
-                       c->value = va.bass;
-                       break;
-               case V4L2_CID_AUDIO_TREBLE:
-                       c->value = va.treble;
-                       break;
-               }
-               return 0;
-       }
        switch (c->id) {
        case V4L2_CID_BRIGHTNESS:
                c->value = btv->bright;
@@ -1222,7 +1475,7 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
        case V4L2_CID_AUDIO_BALANCE:
        case V4L2_CID_AUDIO_BASS:
        case V4L2_CID_AUDIO_TREBLE:
-               bttv_call_i2c_clients(btv,VIDIOC_G_CTRL,c);
+               bttv_call_all(btv, core, g_ctrl, c);
                break;
 
        case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1264,67 +1517,44 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
        return 0;
 }
 
-static int set_control(struct bttv *btv, struct v4l2_control *c)
+static int bttv_s_ctrl(struct file *file, void *f,
+                                       struct v4l2_control *c)
 {
-       struct video_audio va;
-       int i,val;
+       int err;
+       int val;
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
 
-       for (i = 0; i < BTTV_CTLS; i++)
-               if (bttv_ctls[i].id == c->id)
-                       break;
-       if (i == BTTV_CTLS)
-               return -EINVAL;
-       if (btv->audio_hook && i >= 4 && i <= 8) {
-               memset(&va,0,sizeof(va));
-               btv->audio_hook(btv,&va,0);
-               switch (c->id) {
-               case V4L2_CID_AUDIO_MUTE:
-                       if (c->value) {
-                               va.flags |= VIDEO_AUDIO_MUTE;
-                               audio_mute(btv, 1);
-                       } else {
-                               va.flags &= ~VIDEO_AUDIO_MUTE;
-                               audio_mute(btv, 0);
-                       }
-                       break;
+       err = v4l2_prio_check(&btv->prio, &fh->prio);
+       if (0 != err)
+               return err;
 
-               case V4L2_CID_AUDIO_VOLUME:
-                       va.volume = c->value;
-                       break;
-               case V4L2_CID_AUDIO_BALANCE:
-                       va.balance = c->value;
-                       break;
-               case V4L2_CID_AUDIO_BASS:
-                       va.bass = c->value;
-                       break;
-               case V4L2_CID_AUDIO_TREBLE:
-                       va.treble = c->value;
-                       break;
-               }
-               btv->audio_hook(btv,&va,1);
-               return 0;
-       }
        switch (c->id) {
        case V4L2_CID_BRIGHTNESS:
-               bt848_bright(btv,c->value);
+               bt848_bright(btv, c->value);
                break;
        case V4L2_CID_HUE:
-               bt848_hue(btv,c->value);
+               bt848_hue(btv, c->value);
                break;
        case V4L2_CID_CONTRAST:
-               bt848_contrast(btv,c->value);
+               bt848_contrast(btv, c->value);
                break;
        case V4L2_CID_SATURATION:
-               bt848_sat(btv,c->value);
+               bt848_sat(btv, c->value);
                break;
        case V4L2_CID_AUDIO_MUTE:
                audio_mute(btv, c->value);
                /* fall through */
        case V4L2_CID_AUDIO_VOLUME:
+               if (btv->volume_gpio)
+                       btv->volume_gpio(btv, c->value);
+
+               bttv_call_all(btv, core, s_ctrl, c);
+               break;
        case V4L2_CID_AUDIO_BALANCE:
        case V4L2_CID_AUDIO_BASS:
        case V4L2_CID_AUDIO_TREBLE:
-               bttv_call_i2c_clients(btv,VIDIOC_S_CTRL,c);
+               bttv_call_all(btv, core, s_ctrl, c);
                break;
 
        case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1351,8 +1581,9 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
                break;
        case V4L2_CID_PRIVATE_AGC_CRUSH:
                btv->opt_adc_crush = c->value;
-               btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
-                       BT848_ADC);
+               btwrite(BT848_ADC_RESERVED |
+                               (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+                               BT848_ADC);
                break;
        case V4L2_CID_PRIVATE_VCR_HACK:
                btv->opt_vcr_hack = c->value;
@@ -1412,29 +1643,15 @@ static void bttv_field_count(struct bttv *btv)
 }
 
 static const struct bttv_format*
-format_by_palette(int palette)
-{
-       unsigned int i;
-
-       for (i = 0; i < BTTV_FORMATS; i++) {
-               if (-1 == bttv_formats[i].palette)
-                       continue;
-               if (bttv_formats[i].palette == palette)
-                       return bttv_formats+i;
-       }
-       return NULL;
-}
-
-static const struct bttv_format*
 format_by_fourcc(int fourcc)
 {
        unsigned int i;
 
-       for (i = 0; i < BTTV_FORMATS; i++) {
-               if (-1 == bttv_formats[i].fourcc)
+       for (i = 0; i < FORMATS; i++) {
+               if (-1 == formats[i].fourcc)
                        continue;
-               if (bttv_formats[i].fourcc == fourcc)
-                       return bttv_formats+i;
+               if (formats[i].fourcc == fourcc)
+                       return formats+i;
        }
        return NULL;
 }
@@ -1452,20 +1669,20 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
 
        dprintk("switch_overlay: enter [new=%p]\n",new);
        if (new)
-               new->vb.state = STATE_DONE;
+               new->vb.state = VIDEOBUF_DONE;
        spin_lock_irqsave(&btv->s_lock,flags);
        old = btv->screen;
        btv->screen = new;
        btv->loop_irq |= 1;
        bttv_set_dma(btv, 0x03);
        spin_unlock_irqrestore(&btv->s_lock,flags);
-       if (NULL == new)
-               free_btres(btv,fh,RESOURCE_OVERLAY);
        if (NULL != old) {
                dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state);
                bttv_dma_free(&fh->cap,btv, old);
                kfree(old);
        }
+       if (NULL == new)
+               free_btres(btv,fh,RESOURCE_OVERLAY);
        dprintk("switch_overlay: done\n");
        return retval;
 }
@@ -1479,7 +1696,10 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
                               unsigned int width, unsigned int height,
                               enum v4l2_field field)
 {
+       struct bttv_fh *fh = q->priv_data;
        int redo_dma_risc = 0;
+       struct bttv_crop c;
+       int norm;
        int rc;
 
        /* check settings */
@@ -1491,12 +1711,52 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
                if (width*height > buf->vb.bsize)
                        return -EINVAL;
                buf->vb.size = buf->vb.bsize;
+
+               /* Make sure tvnorm and vbi_end remain consistent
+                  until we're done. */
+               mutex_lock(&btv->lock);
+
+               norm = btv->tvnorm;
+
+               /* In this mode capturing always starts at defrect.top
+                  (default VDELAY), ignoring cropping parameters. */
+               if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
+                       mutex_unlock(&btv->lock);
+                       return -EINVAL;
+               }
+
+               mutex_unlock(&btv->lock);
+
+               c.rect = bttv_tvnorms[norm].cropcap.defrect;
        } else {
-               if (width  < 48 ||
-                   height < 32 ||
-                   width  > bttv_tvnorms[btv->tvnorm].swidth ||
-                   height > bttv_tvnorms[btv->tvnorm].sheight)
+               mutex_lock(&btv->lock);
+
+               norm = btv->tvnorm;
+               c = btv->crop[!!fh->do_crop];
+
+               mutex_unlock(&btv->lock);
+
+               if (width < c.min_scaled_width ||
+                   width > c.max_scaled_width ||
+                   height < c.min_scaled_height)
                        return -EINVAL;
+
+               switch (field) {
+               case V4L2_FIELD_TOP:
+               case V4L2_FIELD_BOTTOM:
+               case V4L2_FIELD_ALTERNATE:
+                       /* btv->crop counts frame lines. Max. scale
+                          factor is 16:1 for frames, 8:1 for fields. */
+                       if (height * 2 > c.max_scaled_height)
+                               return -EINVAL;
+                       break;
+
+               default:
+                       if (height > c.max_scaled_height)
+                               return -EINVAL;
+                       break;
+               }
+
                buf->vb.size = (width * height * fmt->depth) >> 3;
                if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
                        return -EINVAL;
@@ -1505,17 +1765,22 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
        /* alloc + fill struct bttv_buffer (if changed) */
        if (buf->vb.width != width || buf->vb.height != height ||
            buf->vb.field != field ||
-           buf->tvnorm != btv->tvnorm || buf->fmt != fmt) {
+           buf->tvnorm != norm || buf->fmt != fmt ||
+           buf->crop.top != c.rect.top ||
+           buf->crop.left != c.rect.left ||
+           buf->crop.width != c.rect.width ||
+           buf->crop.height != c.rect.height) {
                buf->vb.width  = width;
                buf->vb.height = height;
                buf->vb.field  = field;
-               buf->tvnorm    = btv->tvnorm;
+               buf->tvnorm    = norm;
                buf->fmt       = fmt;
+               buf->crop      = c.rect;
                redo_dma_risc = 1;
        }
 
        /* alloc risc memory */
-       if (STATE_NEEDS_INIT == buf->vb.state) {
+       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
                redo_dma_risc = 1;
                if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
                        goto fail;
@@ -1525,7 +1790,7 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
                if (0 != (rc = bttv_buffer_risc(btv,buf)))
                        goto fail;
 
-       buf->vb.state = STATE_PREPARED;
+       buf->vb.state = VIDEOBUF_PREPARED;
        return 0;
 
  fail:
@@ -1541,8 +1806,8 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
        *size = fh->fmt->depth*fh->width*fh->height >> 3;
        if (0 == *count)
                *count = gbuffers;
-       while (*size * *count > gbuffers * gbufsize)
-               (*count)--;
+       if (*size * *count > gbuffers * gbufsize)
+               *count = (gbuffers * gbufsize) / *size;
        return 0;
 }
 
@@ -1564,7 +1829,7 @@ buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
        struct bttv_fh *fh = q->priv_data;
        struct bttv    *btv = fh->btv;
 
-       buf->vb.state = STATE_QUEUED;
+       buf->vb.state = VIDEOBUF_QUEUED;
        list_add_tail(&buf->vb.queue,&btv->capture);
        if (!btv->curr.frame_irq) {
                btv->loop_irq |= 1;
@@ -1577,7 +1842,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
        struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
        struct bttv_fh *fh = q->priv_data;
 
-       bttv_dma_free(&fh->cap,fh->btv,buf);
+       bttv_dma_free(q,fh->btv,buf);
 }
 
 static struct videobuf_queue_ops bttv_video_qops = {
@@ -1587,363 +1852,400 @@ static struct videobuf_queue_ops bttv_video_qops = {
        .buf_release  = buffer_release,
 };
 
-static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
+static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
 {
-       switch (cmd) {
-       case BTTV_VERSION:
-               return BTTV_VERSION_CODE;
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       unsigned int i;
+       int err;
 
-       /* ***  v4l1  *** ************************************************ */
-       case VIDIOCGFREQ:
-       {
-               unsigned long *freq = arg;
-               *freq = btv->freq;
-               return 0;
-       }
-       case VIDIOCSFREQ:
-       {
-               struct v4l2_frequency freq;
+       err = v4l2_prio_check(&btv->prio, &fh->prio);
+       if (0 != err)
+               return err;
 
-               memset(&freq, 0, sizeof(freq));
-               freq.frequency = *(unsigned long *)arg;
-               mutex_lock(&btv->lock);
-               freq.type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-               btv->freq = *(unsigned long *)arg;
-               bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,&freq);
-               if (btv->has_matchbox && btv->radio_user)
-                       tea5757_set_freq(btv,*(unsigned long *)arg);
-               mutex_unlock(&btv->lock);
-               return 0;
-       }
+       for (i = 0; i < BTTV_TVNORMS; i++)
+               if (*id & bttv_tvnorms[i].v4l2_id)
+                       break;
+       if (i == BTTV_TVNORMS)
+               return -EINVAL;
 
-       case VIDIOCGTUNER:
-       {
-               struct video_tuner *v = arg;
+       mutex_lock(&btv->lock);
+       set_tvnorm(btv, i);
+       mutex_unlock(&btv->lock);
 
-               if (UNSET == bttv_tvcards[btv->c.type].tuner)
-                       return -EINVAL;
-               if (v->tuner) /* Only tuner 0 */
-                       return -EINVAL;
-               strcpy(v->name, "Television");
-               v->rangelow  = 0;
-               v->rangehigh = 0x7FFFFFFF;
-               v->flags     = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
-               v->mode      = btv->tvnorm;
-               v->signal    = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
-               bttv_call_i2c_clients(btv,cmd,v);
-               return 0;
-       }
-       case VIDIOCSTUNER:
-       {
-               struct video_tuner *v = arg;
+       return 0;
+}
 
-               if (v->tuner) /* Only tuner 0 */
-                       return -EINVAL;
-               if (v->mode >= BTTV_TVNORMS)
-                       return -EINVAL;
+static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
 
-               mutex_lock(&btv->lock);
-               set_tvnorm(btv,v->mode);
-               bttv_call_i2c_clients(btv,cmd,v);
-               mutex_unlock(&btv->lock);
-               return 0;
-       }
+       if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+               *id = V4L2_STD_625_50;
+       else
+               *id = V4L2_STD_525_60;
+       return 0;
+}
 
-       case VIDIOCGCHAN:
-       {
-               struct video_channel *v = arg;
-               unsigned int channel = v->channel;
+static int bttv_enum_input(struct file *file, void *priv,
+                                       struct v4l2_input *i)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int n;
 
-               if (channel >= bttv_tvcards[btv->c.type].video_inputs)
-                       return -EINVAL;
-               v->tuners=0;
-               v->flags = VIDEO_VC_AUDIO;
-               v->type = VIDEO_TYPE_CAMERA;
-               v->norm = btv->tvnorm;
-               if (channel == bttv_tvcards[btv->c.type].tuner)  {
-                       strcpy(v->name,"Television");
-                       v->flags|=VIDEO_VC_TUNER;
-                       v->type=VIDEO_TYPE_TV;
-                       v->tuners=1;
-               } else if (channel == btv->svhs) {
-                       strcpy(v->name,"S-Video");
-               } else {
-                       sprintf(v->name,"Composite%d",channel);
-               }
-               return 0;
-       }
-       case VIDIOCSCHAN:
-       {
-               struct video_channel *v = arg;
-               unsigned int channel = v->channel;
+       if (i->index >= bttv_tvcards[btv->c.type].video_inputs)
+               return -EINVAL;
 
-               if (channel >= bttv_tvcards[btv->c.type].video_inputs)
-                       return -EINVAL;
-               if (v->norm >= BTTV_TVNORMS)
-                       return -EINVAL;
+       i->type     = V4L2_INPUT_TYPE_CAMERA;
+       i->audioset = 1;
 
-               mutex_lock(&btv->lock);
-               if (channel == btv->input &&
-                   v->norm == btv->tvnorm) {
-                       /* nothing to do */
-                       mutex_unlock(&btv->lock);
-                       return 0;
-               }
+       if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+               sprintf(i->name, "Television");
+               i->type  = V4L2_INPUT_TYPE_TUNER;
+               i->tuner = 0;
+       } else if (i->index == btv->svhs) {
+               sprintf(i->name, "S-Video");
+       } else {
+               sprintf(i->name, "Composite%d", i->index);
+       }
 
-               btv->tvnorm = v->norm;
-               set_input(btv,v->channel);
-               mutex_unlock(&btv->lock);
-               return 0;
+       if (i->index == btv->input) {
+               __u32 dstatus = btread(BT848_DSTATUS);
+               if (0 == (dstatus & BT848_DSTATUS_PRES))
+                       i->status |= V4L2_IN_ST_NO_SIGNAL;
+               if (0 == (dstatus & BT848_DSTATUS_HLOC))
+                       i->status |= V4L2_IN_ST_NO_H_LOCK;
        }
 
-       case VIDIOCGAUDIO:
-       {
-               struct video_audio *v = arg;
+       for (n = 0; n < BTTV_TVNORMS; n++)
+               i->std |= bttv_tvnorms[n].v4l2_id;
 
-               memset(v,0,sizeof(*v));
-               strcpy(v->name,"Television");
-               v->flags |= VIDEO_AUDIO_MUTABLE;
-               v->mode  = VIDEO_SOUND_MONO;
+       return 0;
+}
 
-               mutex_lock(&btv->lock);
-               bttv_call_i2c_clients(btv,cmd,v);
+static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
 
-               /* card specific hooks */
-               if (btv->audio_hook)
-                       btv->audio_hook(btv,v,0);
+       *i = btv->input;
+       return 0;
+}
 
-               mutex_unlock(&btv->lock);
-               return 0;
-       }
-       case VIDIOCSAUDIO:
-       {
-               struct video_audio *v = arg;
-               unsigned int audio = v->audio;
+static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+{
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
 
-               if (audio >= bttv_tvcards[btv->c.type].audio_inputs)
-                       return -EINVAL;
+       int err;
 
-               mutex_lock(&btv->lock);
-               audio_mute(btv, (v->flags&VIDEO_AUDIO_MUTE) ? 1 : 0);
-               bttv_call_i2c_clients(btv,cmd,v);
+       err = v4l2_prio_check(&btv->prio, &fh->prio);
+       if (0 != err)
+               return err;
 
-               /* card specific hooks */
-               if (btv->audio_hook)
-                       btv->audio_hook(btv,v,1);
+       if (i > bttv_tvcards[btv->c.type].video_inputs)
+               return -EINVAL;
 
-               mutex_unlock(&btv->lock);
-               return 0;
-       }
+       mutex_lock(&btv->lock);
+       set_input(btv, i, btv->tvnorm);
+       mutex_unlock(&btv->lock);
+       return 0;
+}
 
-       /* ***  v4l2  *** ************************************************ */
-       case VIDIOC_ENUMSTD:
-       {
-               struct v4l2_standard *e = arg;
-               unsigned int index = e->index;
+static int bttv_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+{
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       int err;
 
-               if (index >= BTTV_TVNORMS)
-                       return -EINVAL;
-               v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id,
-                                        bttv_tvnorms[e->index].name);
-               e->index = index;
-               return 0;
-       }
-       case VIDIOC_G_STD:
-       {
-               v4l2_std_id *id = arg;
-               *id = bttv_tvnorms[btv->tvnorm].v4l2_id;
-               return 0;
-       }
-       case VIDIOC_S_STD:
-       {
-               v4l2_std_id *id = arg;
-               unsigned int i;
+       err = v4l2_prio_check(&btv->prio, &fh->prio);
+       if (0 != err)
+               return err;
 
-               for (i = 0; i < BTTV_TVNORMS; i++)
-                       if (*id & bttv_tvnorms[i].v4l2_id)
-                               break;
-               if (i == BTTV_TVNORMS)
-                       return -EINVAL;
+       if (btv->tuner_type == TUNER_ABSENT)
+               return -EINVAL;
 
-               mutex_lock(&btv->lock);
-               set_tvnorm(btv,i);
-               i2c_vidiocschan(btv);
-               mutex_unlock(&btv->lock);
-               return 0;
-       }
-       case VIDIOC_QUERYSTD:
-       {
-               v4l2_std_id *id = arg;
+       if (0 != t->index)
+               return -EINVAL;
 
-               if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
-                       *id = V4L2_STD_625_50;
-               else
-                       *id = V4L2_STD_525_60;
-               return 0;
-       }
+       mutex_lock(&btv->lock);
+       bttv_call_all(btv, tuner, s_tuner, t);
 
-       case VIDIOC_ENUMINPUT:
-       {
-               struct v4l2_input *i = arg;
-               unsigned int n;
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 1);
 
-               n = i->index;
-               if (n >= bttv_tvcards[btv->c.type].video_inputs)
-                       return -EINVAL;
-               memset(i,0,sizeof(*i));
-               i->index    = n;
-               i->type     = V4L2_INPUT_TYPE_CAMERA;
-               i->audioset = 0;
-               if (i->index == bttv_tvcards[btv->c.type].tuner) {
-                       sprintf(i->name, "Television");
-                       i->type  = V4L2_INPUT_TYPE_TUNER;
-                       i->tuner = 0;
-               } else if (i->index == btv->svhs) {
-                       sprintf(i->name, "S-Video");
-               } else {
-                       sprintf(i->name,"Composite%d",i->index);
-               }
-               if (i->index == btv->input) {
-                       __u32 dstatus = btread(BT848_DSTATUS);
-                       if (0 == (dstatus & BT848_DSTATUS_PRES))
-                               i->status |= V4L2_IN_ST_NO_SIGNAL;
-                       if (0 == (dstatus & BT848_DSTATUS_HLOC))
-                               i->status |= V4L2_IN_ST_NO_H_LOCK;
-               }
-               for (n = 0; n < BTTV_TVNORMS; n++)
-                       i->std |= bttv_tvnorms[n].v4l2_id;
-               return 0;
-       }
-       case VIDIOC_G_INPUT:
-       {
-               int *i = arg;
-               *i = btv->input;
-               return 0;
-       }
-       case VIDIOC_S_INPUT:
-       {
-               unsigned int *i = arg;
+       mutex_unlock(&btv->lock);
 
-               if (*i > bttv_tvcards[btv->c.type].video_inputs)
-                       return -EINVAL;
-               mutex_lock(&btv->lock);
-               set_input(btv,*i);
-               mutex_unlock(&btv->lock);
-               return 0;
+       return 0;
+}
+
+static int bttv_g_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+{
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+
+       f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       f->frequency = btv->freq;
+
+       return 0;
+}
+
+static int bttv_s_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+{
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       int err;
+
+       err = v4l2_prio_check(&btv->prio, &fh->prio);
+       if (0 != err)
+               return err;
+
+       if (unlikely(f->tuner != 0))
+               return -EINVAL;
+       if (unlikely(f->type != (btv->radio_user
+               ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV)))
+               return -EINVAL;
+       mutex_lock(&btv->lock);
+       btv->freq = f->frequency;
+       bttv_call_all(btv, tuner, s_frequency, f);
+       if (btv->has_matchbox && btv->radio_user)
+               tea5757_set_freq(btv, btv->freq);
+       mutex_unlock(&btv->lock);
+       return 0;
+}
+
+static int bttv_log_status(struct file *file, void *f)
+{
+       struct bttv_fh *fh  = f;
+       struct bttv *btv = fh->btv;
+
+       printk(KERN_INFO "bttv%d: ========  START STATUS CARD #%d  ========\n",
+                       btv->c.nr, btv->c.nr);
+       bttv_call_all(btv, core, log_status);
+       printk(KERN_INFO "bttv%d: ========  END STATUS CARD   #%d  ========\n",
+                       btv->c.nr, btv->c.nr);
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bttv_g_register(struct file *file, void *f,
+                                       struct v4l2_dbg_register *reg)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (!v4l2_chip_match_host(&reg->match))
+               return -EINVAL;
+
+       /* bt848 has a 12-bit register space */
+       reg->reg &= 0xfff;
+       reg->val = btread(reg->reg);
+       reg->size = 1;
+
+       return 0;
+}
+
+static int bttv_s_register(struct file *file, void *f,
+                                       struct v4l2_dbg_register *reg)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (!v4l2_chip_match_host(&reg->match))
+               return -EINVAL;
+
+       /* bt848 has a 12-bit register space */
+       reg->reg &= 0xfff;
+       btwrite(reg->val, reg->reg);
+
+       return 0;
+}
+#endif
+
+/* Given cropping boundaries b and the scaled width and height of a
+   single field or frame, which must not exceed hardware limits, this
+   function adjusts the cropping parameters c. */
+static void
+bttv_crop_adjust       (struct bttv_crop *             c,
+                        const struct v4l2_rect *       b,
+                        __s32                          width,
+                        __s32                          height,
+                        enum v4l2_field                field)
+{
+       __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+       __s32 max_left;
+       __s32 max_top;
+
+       if (width < c->min_scaled_width) {
+               /* Max. hor. scale factor 16:1. */
+               c->rect.width = width * 16;
+       } else if (width > c->max_scaled_width) {
+               /* Min. hor. scale factor 1:1. */
+               c->rect.width = width;
+
+               max_left = b->left + b->width - width;
+               max_left = min(max_left, (__s32) MAX_HDELAY);
+               if (c->rect.left > max_left)
+                       c->rect.left = max_left;
        }
 
-       case VIDIOC_G_TUNER:
-       {
-               struct v4l2_tuner *t = arg;
+       if (height < c->min_scaled_height) {
+               /* Max. vert. scale factor 16:1, single fields 8:1. */
+               c->rect.height = height * 16;
+       } else if (frame_height > c->max_scaled_height) {
+               /* Min. vert. scale factor 1:1.
+                  Top and height count field lines times two. */
+               c->rect.height = (frame_height + 1) & ~1;
 
-               if (UNSET == bttv_tvcards[btv->c.type].tuner)
-                       return -EINVAL;
-               if (0 != t->index)
-                       return -EINVAL;
-               mutex_lock(&btv->lock);
-               memset(t,0,sizeof(*t));
-               t->rxsubchans = V4L2_TUNER_SUB_MONO;
-               bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
-               strcpy(t->name, "Television");
-               t->capability = V4L2_TUNER_CAP_NORM;
-               t->type       = V4L2_TUNER_ANALOG_TV;
-               if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
-                       t->signal = 0xffff;
-
-               if (btv->audio_hook) {
-                       /* Hmmm ... */
-                       struct video_audio va;
-                       memset(&va, 0, sizeof(struct video_audio));
-                       btv->audio_hook(btv,&va,0);
-                       t->audmode    = V4L2_TUNER_MODE_MONO;
-                       t->rxsubchans = V4L2_TUNER_SUB_MONO;
-                       if(va.mode & VIDEO_SOUND_STEREO) {
-                               t->audmode    = V4L2_TUNER_MODE_STEREO;
-                               t->rxsubchans = V4L2_TUNER_SUB_STEREO;
-                       }
-                       if(va.mode & VIDEO_SOUND_LANG2) {
-                               t->audmode    = V4L2_TUNER_MODE_LANG1;
-                               t->rxsubchans = V4L2_TUNER_SUB_LANG1
-                                       | V4L2_TUNER_SUB_LANG2;
-                       }
-               }
-               /* FIXME: fill capability+audmode */
-               mutex_unlock(&btv->lock);
-               return 0;
+               max_top = b->top + b->height - c->rect.height;
+               if (c->rect.top > max_top)
+                       c->rect.top = max_top;
        }
-       case VIDIOC_S_TUNER:
-       {
-               struct v4l2_tuner *t = arg;
 
-               if (UNSET == bttv_tvcards[btv->c.type].tuner)
-                       return -EINVAL;
-               if (0 != t->index)
-                       return -EINVAL;
-               mutex_lock(&btv->lock);
-               bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t);
-               if (btv->audio_hook) {
-                       struct video_audio va;
-                       memset(&va, 0, sizeof(struct video_audio));
-                       if (t->audmode == V4L2_TUNER_MODE_MONO)
-                               va.mode = VIDEO_SOUND_MONO;
-                       else if (t->audmode == V4L2_TUNER_MODE_STEREO ||
-                                t->audmode == V4L2_TUNER_MODE_LANG1_LANG2)
-                               va.mode = VIDEO_SOUND_STEREO;
-                       else if (t->audmode == V4L2_TUNER_MODE_LANG1)
-                               va.mode = VIDEO_SOUND_LANG1;
-                       else if (t->audmode == V4L2_TUNER_MODE_LANG2)
-                               va.mode = VIDEO_SOUND_LANG2;
-                       btv->audio_hook(btv,&va,1);
+       bttv_crop_calc_limits(c);
+}
+
+/* Returns an error if scaling to a frame or single field with the given
+   width and height is not possible with the current cropping parameters
+   and width aligned according to width_mask. If adjust_size is TRUE the
+   function may adjust the width and/or height instead, rounding width
+   to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
+   also adjust the current cropping parameters to get closer to the
+   desired image size. */
+static int
+limit_scaled_size       (struct bttv_fh *               fh,
+                        __s32 *                        width,
+                        __s32 *                        height,
+                        enum v4l2_field                field,
+                        unsigned int                   width_mask,
+                        unsigned int                   width_bias,
+                        int                            adjust_size,
+                        int                            adjust_crop)
+{
+       struct bttv *btv = fh->btv;
+       const struct v4l2_rect *b;
+       struct bttv_crop *c;
+       __s32 min_width;
+       __s32 min_height;
+       __s32 max_width;
+       __s32 max_height;
+       int rc;
+
+       BUG_ON((int) width_mask >= 0 ||
+              width_bias >= (unsigned int) -width_mask);
+
+       /* Make sure tvnorm, vbi_end and the current cropping parameters
+          remain consistent until we're done. */
+       mutex_lock(&btv->lock);
+
+       b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+       /* Do crop - use current, don't - use default parameters. */
+       c = &btv->crop[!!fh->do_crop];
+
+       if (fh->do_crop
+           && adjust_size
+           && adjust_crop
+           && !locked_btres(btv, VIDEO_RESOURCES)) {
+               min_width = 48;
+               min_height = 32;
+
+               /* We cannot scale up. When the scaled image is larger
+                  than crop.rect we adjust the crop.rect as required
+                  by the V4L2 spec, hence cropcap.bounds are our limit. */
+               max_width = min(b->width, (__s32) MAX_HACTIVE);
+               max_height = b->height;
+
+               /* We cannot capture the same line as video and VBI data.
+                  Note btv->vbi_end is really a minimum, see
+                  bttv_vbi_try_fmt(). */
+               if (btv->vbi_end > b->top) {
+                       max_height -= btv->vbi_end - b->top;
+                       rc = -EBUSY;
+                       if (min_height > max_height)
+                               goto fail;
                }
-               mutex_unlock(&btv->lock);
-               return 0;
-       }
+       } else {
+               rc = -EBUSY;
+               if (btv->vbi_end > c->rect.top)
+                       goto fail;
 
-       case VIDIOC_G_FREQUENCY:
-       {
-               struct v4l2_frequency *f = arg;
+               min_width  = c->min_scaled_width;
+               min_height = c->min_scaled_height;
+               max_width  = c->max_scaled_width;
+               max_height = c->max_scaled_height;
 
-               memset(f,0,sizeof(*f));
-               f->type = V4L2_TUNER_ANALOG_TV;
-               f->frequency = btv->freq;
-               return 0;
+               adjust_crop = 0;
        }
-       case VIDIOC_S_FREQUENCY:
-       {
-               struct v4l2_frequency *f = arg;
 
-               if (unlikely(f->tuner != 0))
-                       return -EINVAL;
-               if (unlikely (f->type != V4L2_TUNER_ANALOG_TV))
-                       return -EINVAL;
-               mutex_lock(&btv->lock);
-               btv->freq = f->frequency;
-               bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,f);
-               if (btv->has_matchbox && btv->radio_user)
-                       tea5757_set_freq(btv,btv->freq);
-               mutex_unlock(&btv->lock);
-               return 0;
-       }
-       case VIDIOC_LOG_STATUS:
-       {
-               printk(KERN_INFO "bttv%d: =================  START STATUS CARD #%d  =================\n", btv->c.nr, btv->c.nr);
-               bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL);
-               printk(KERN_INFO "bttv%d: ==================  END STATUS CARD #%d  ==================\n", btv->c.nr, btv->c.nr);
-               return 0;
-       }
+       min_width = (min_width - width_mask - 1) & width_mask;
+       max_width = max_width & width_mask;
 
-       default:
-               return -ENOIOCTLCMD;
+       /* Max. scale factor is 16:1 for frames, 8:1 for fields. */
+       min_height = min_height;
+       /* Min. scale factor is 1:1. */
+       max_height >>= !V4L2_FIELD_HAS_BOTH(field);
+
+       if (adjust_size) {
+               *width = clamp(*width, min_width, max_width);
+               *height = clamp(*height, min_height, max_height);
 
+               /* Round after clamping to avoid overflow. */
+               *width = (*width + width_bias) & width_mask;
+
+               if (adjust_crop) {
+                       bttv_crop_adjust(c, b, *width, *height, field);
+
+                       if (btv->vbi_end > c->rect.top) {
+                               /* Move the crop window out of the way. */
+                               c->rect.top = btv->vbi_end;
+                       }
+               }
+       } else {
+               rc = -EINVAL;
+               if (*width  < min_width ||
+                   *height < min_height ||
+                   *width  > max_width ||
+                   *height > max_height ||
+                   0 != (*width & ~width_mask))
+                       goto fail;
        }
-       return 0;
+
+       rc = 0; /* success */
+
+ fail:
+       mutex_unlock(&btv->lock);
+
+       return rc;
 }
 
-static int verify_window(const struct bttv_tvnorm *tvn,
-                        struct v4l2_window *win, int fixup)
+/* Returns an error if the given overlay window dimensions are not
+   possible with the current cropping parameters. If adjust_size is
+   TRUE the function may adjust the window width and/or height
+   instead, however it always rounds the horizontal position and
+   width as btcx_align() does. If adjust_crop is TRUE the function
+   may also adjust the current cropping parameters to get closer
+   to the desired window size. */
+static int
+verify_window          (struct bttv_fh *               fh,
+                        struct v4l2_window *           win,
+                        int                            adjust_size,
+                        int                            adjust_crop)
 {
        enum v4l2_field field;
-       int maxw, maxh;
+       unsigned int width_mask;
+       int rc;
 
        if (win->w.width  < 48 || win->w.height < 32)
                return -EINVAL;
@@ -1951,32 +2253,52 @@ static int verify_window(const struct bttv_tvnorm *tvn,
                return -EINVAL;
 
        field = win->field;
-       maxw  = tvn->swidth;
-       maxh  = tvn->sheight;
 
        if (V4L2_FIELD_ANY == field) {
-               field = (win->w.height > maxh/2)
+               __s32 height2;
+
+               height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+               field = (win->w.height > height2)
                        ? V4L2_FIELD_INTERLACED
                        : V4L2_FIELD_TOP;
        }
        switch (field) {
        case V4L2_FIELD_TOP:
        case V4L2_FIELD_BOTTOM:
-               maxh = maxh / 2;
-               break;
        case V4L2_FIELD_INTERLACED:
                break;
        default:
                return -EINVAL;
        }
 
-       if (!fixup && (win->w.width > maxw || win->w.height > maxh))
+       /* 4-byte alignment. */
+       if (NULL == fh->ovfmt)
                return -EINVAL;
+       width_mask = ~0;
+       switch (fh->ovfmt->depth) {
+       case 8:
+       case 24:
+               width_mask = ~3;
+               break;
+       case 16:
+               width_mask = ~1;
+               break;
+       case 32:
+               break;
+       default:
+               BUG();
+       }
+
+       win->w.width -= win->w.left & ~width_mask;
+       win->w.left = (win->w.left - width_mask - 1) & width_mask;
+
+       rc = limit_scaled_size(fh, &win->w.width, &win->w.height,
+                              field, width_mask,
+                              /* width_bias: round down */ 0,
+                              adjust_size, adjust_crop);
+       if (0 != rc)
+               return rc;
 
-       if (win->w.width > maxw)
-               win->w.width = maxw;
-       if (win->w.height > maxh)
-               win->w.height = maxh;
        win->field = field;
        return 0;
 }
@@ -1991,7 +2313,9 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
                return -EINVAL;
        if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
                return -EINVAL;
-       retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup);
+       retval = verify_window(fh, win,
+                              /* adjust_size */ fixup,
+                              /* adjust_crop */ fixup);
        if (0 != retval)
                return retval;
 
@@ -2030,8 +2354,8 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
                BUG();
        }
 
-       mutex_lock(&fh->cap.lock);
-               kfree(fh->ov.clips);
+       mutex_lock(&fh->cap.vb_lock);
+       kfree(fh->ov.clips);
        fh->ov.clips    = clips;
        fh->ov.nclips   = n;
 
@@ -2047,11 +2371,12 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
        if (check_btres(fh, RESOURCE_OVERLAY)) {
                struct bttv_buffer *new;
 
-               new = videobuf_alloc(sizeof(*new));
+               new = videobuf_sg_alloc(sizeof(*new));
+               new->crop = btv->crop[!!fh->do_crop].rect;
                bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
                retval = bttv_switch_overlay(btv,fh,new);
        }
-       mutex_unlock(&fh->cap.lock);
+       mutex_unlock(&fh->cap.vb_lock);
        return retval;
 }
 
@@ -2080,7 +2405,7 @@ static int bttv_resource(struct bttv_fh *fh)
 
        switch (fh->type) {
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               res = RESOURCE_VIDEO;
+               res = RESOURCE_VIDEO_STREAM;
                break;
        case V4L2_BUF_TYPE_VBI_CAPTURE:
                res = RESOURCE_VBI;
@@ -2122,861 +2447,672 @@ pix_format_set_size     (struct v4l2_pix_format *       f,
        }
 }
 
-static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
 {
-       switch (f->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format));
-               pix_format_set_size (&f->fmt.pix, fh->fmt,
-                                    fh->width, fh->height);
-               f->fmt.pix.field        = fh->cap.field;
-               f->fmt.pix.pixelformat  = fh->fmt->fourcc;
-               return 0;
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               memset(&f->fmt.win,0,sizeof(struct v4l2_window));
-               f->fmt.win.w     = fh->ov.w;
-               f->fmt.win.field = fh->ov.field;
-               return 0;
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               bttv_vbi_get_fmt(fh,f);
-               return 0;
-       default:
-               return -EINVAL;
-       }
+       struct bttv_fh *fh  = priv;
+
+       pix_format_set_size(&f->fmt.pix, fh->fmt,
+                               fh->width, fh->height);
+       f->fmt.pix.field        = fh->cap.field;
+       f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+
+       return 0;
 }
 
-static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv,
-                       struct v4l2_format *f)
+static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
 {
-       switch (f->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-       {
-               const struct bttv_format *fmt;
-               enum v4l2_field field;
-               unsigned int maxw,maxh;
+       struct bttv_fh *fh  = priv;
 
-               fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-               if (NULL == fmt)
-                       return -EINVAL;
+       f->fmt.win.w     = fh->ov.w;
+       f->fmt.win.field = fh->ov.field;
 
-               /* fixup format */
-               maxw  = bttv_tvnorms[btv->tvnorm].swidth;
-               maxh  = bttv_tvnorms[btv->tvnorm].sheight;
-               field = f->fmt.pix.field;
-               if (V4L2_FIELD_ANY == field)
-                       field = (f->fmt.pix.height > maxh/2)
-                               ? V4L2_FIELD_INTERLACED
-                               : V4L2_FIELD_BOTTOM;
-               if (V4L2_FIELD_SEQ_BT == field)
-                       field = V4L2_FIELD_SEQ_TB;
-               switch (field) {
-               case V4L2_FIELD_TOP:
-               case V4L2_FIELD_BOTTOM:
-               case V4L2_FIELD_ALTERNATE:
-                       maxh = maxh/2;
-                       break;
-               case V4L2_FIELD_INTERLACED:
-                       break;
-               case V4L2_FIELD_SEQ_TB:
-                       if (fmt->flags & FORMAT_FLAGS_PLANAR)
-                               return -EINVAL;
-                       break;
-               default:
-                       return -EINVAL;
-               }
+       return 0;
+}
 
-               /* update data for the application */
-               f->fmt.pix.field = field;
-               if (f->fmt.pix.width  < 48)
-                       f->fmt.pix.width  = 48;
-               if (f->fmt.pix.height < 32)
-                       f->fmt.pix.height = 32;
-               if (f->fmt.pix.width  > maxw)
-                       f->fmt.pix.width = maxw;
-               if (f->fmt.pix.height > maxh)
-                       f->fmt.pix.height = maxh;
-               pix_format_set_size (&f->fmt.pix, fmt,
-                                    f->fmt.pix.width & ~3,
-                                    f->fmt.pix.height);
+static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+{
+       const struct bttv_format *fmt;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       enum v4l2_field field;
+       __s32 width, height;
+       int rc;
 
-               return 0;
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       if (NULL == fmt)
+               return -EINVAL;
+
+       field = f->fmt.pix.field;
+
+       if (V4L2_FIELD_ANY == field) {
+               __s32 height2;
+
+               height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+               field = (f->fmt.pix.height > height2)
+                       ? V4L2_FIELD_INTERLACED
+                       : V4L2_FIELD_BOTTOM;
        }
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               return verify_window(&bttv_tvnorms[btv->tvnorm],
-                                    &f->fmt.win, 1);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               bttv_vbi_try_fmt(fh,f);
-               return 0;
+
+       if (V4L2_FIELD_SEQ_BT == field)
+               field = V4L2_FIELD_SEQ_TB;
+
+       switch (field) {
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+       case V4L2_FIELD_ALTERNATE:
+       case V4L2_FIELD_INTERLACED:
+               break;
+       case V4L2_FIELD_SEQ_TB:
+               if (fmt->flags & FORMAT_FLAGS_PLANAR)
+                       return -EINVAL;
+               break;
        default:
                return -EINVAL;
        }
+
+       width = f->fmt.pix.width;
+       height = f->fmt.pix.height;
+
+       rc = limit_scaled_size(fh, &width, &height, field,
+                              /* width_mask: 4 pixels */ ~3,
+                              /* width_bias: nearest */ 2,
+                              /* adjust_size */ 1,
+                              /* adjust_crop */ 0);
+       if (0 != rc)
+               return rc;
+
+       /* update data for the application */
+       f->fmt.pix.field = field;
+       pix_format_set_size(&f->fmt.pix, fmt, width, height);
+
+       return 0;
 }
 
-static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv,
-                     struct v4l2_format *f)
+static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+{
+       struct bttv_fh *fh = priv;
+
+       return verify_window(fh, &f->fmt.win,
+                       /* adjust_size */ 1,
+                       /* adjust_crop */ 0);
+}
+
+static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
 {
        int retval;
+       const struct bttv_format *fmt;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       __s32 width, height;
+       enum v4l2_field field;
 
-       switch (f->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-       {
-               const struct bttv_format *fmt;
+       retval = bttv_switch_type(fh, f->type);
+       if (0 != retval)
+               return retval;
 
-               retval = bttv_switch_type(fh,f->type);
-               if (0 != retval)
-                       return retval;
-               retval = bttv_try_fmt(fh,btv,f);
-               if (0 != retval)
-                       return retval;
-               fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-
-               /* update our state informations */
-               mutex_lock(&fh->cap.lock);
-               fh->fmt              = fmt;
-               fh->cap.field        = f->fmt.pix.field;
-               fh->cap.last         = V4L2_FIELD_NONE;
-               fh->width            = f->fmt.pix.width;
-               fh->height           = f->fmt.pix.height;
-               btv->init.fmt        = fmt;
-               btv->init.width      = f->fmt.pix.width;
-               btv->init.height     = f->fmt.pix.height;
-               mutex_unlock(&fh->cap.lock);
+       retval = bttv_try_fmt_vid_cap(file, priv, f);
+       if (0 != retval)
+               return retval;
 
-               return 0;
-       }
-       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               if (no_overlay > 0) {
-                       printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
-                       return -EINVAL;
-               }
-               return setup_window(fh, btv, &f->fmt.win, 1);
-       case V4L2_BUF_TYPE_VBI_CAPTURE:
-               retval = bttv_switch_type(fh,f->type);
-               if (0 != retval)
-                       return retval;
-               if (locked_btres(fh->btv, RESOURCE_VBI))
-                       return -EBUSY;
-               bttv_vbi_try_fmt(fh,f);
-               bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]);
-               bttv_vbi_get_fmt(fh,f);
-               return 0;
-       default:
+       width = f->fmt.pix.width;
+       height = f->fmt.pix.height;
+       field = f->fmt.pix.field;
+
+       retval = limit_scaled_size(fh, &width, &height, f->fmt.pix.field,
+                              /* width_mask: 4 pixels */ ~3,
+                              /* width_bias: nearest */ 2,
+                              /* adjust_size */ 1,
+                              /* adjust_crop */ 1);
+       if (0 != retval)
+               return retval;
+
+       f->fmt.pix.field = field;
+
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+       /* update our state informations */
+       mutex_lock(&fh->cap.vb_lock);
+       fh->fmt              = fmt;
+       fh->cap.field        = f->fmt.pix.field;
+       fh->cap.last         = V4L2_FIELD_NONE;
+       fh->width            = f->fmt.pix.width;
+       fh->height           = f->fmt.pix.height;
+       btv->init.fmt        = fmt;
+       btv->init.width      = f->fmt.pix.width;
+       btv->init.height     = f->fmt.pix.height;
+       mutex_unlock(&fh->cap.vb_lock);
+
+       return 0;
+}
+
+static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+
+       if (no_overlay > 0) {
+               printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
                return -EINVAL;
        }
+
+       return setup_window(fh, btv, &f->fmt.win, 1);
 }
 
-static int bttv_do_ioctl(struct inode *inode, struct file *file,
-                        unsigned int cmd, void *arg)
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
 {
-       struct bttv_fh *fh  = file->private_data;
-       struct bttv    *btv = fh->btv;
-       unsigned long flags;
-       int retval = 0;
+       int retval;
+       unsigned int i;
+       struct bttv_fh *fh = priv;
 
-       if (bttv_debug > 1)
-               v4l_print_ioctl(btv->c.name, cmd);
-
-       if (btv->errors)
-               bttv_reinit_bt848(btv);
-
-       switch (cmd) {
-       case VIDIOCSFREQ:
-       case VIDIOCSTUNER:
-       case VIDIOCSCHAN:
-       case VIDIOC_S_CTRL:
-       case VIDIOC_S_STD:
-       case VIDIOC_S_INPUT:
-       case VIDIOC_S_TUNER:
-       case VIDIOC_S_FREQUENCY:
-               retval = v4l2_prio_check(&btv->prio,&fh->prio);
-               if (0 != retval)
-                       return retval;
-       };
+       mutex_lock(&fh->cap.vb_lock);
+       retval = __videobuf_mmap_setup(&fh->cap, gbuffers, gbufsize,
+                                    V4L2_MEMORY_MMAP);
+       if (retval < 0) {
+               mutex_unlock(&fh->cap.vb_lock);
+               return retval;
+       }
 
-       switch (cmd) {
+       gbuffers = retval;
+       memset(mbuf, 0, sizeof(*mbuf));
+       mbuf->frames = gbuffers;
+       mbuf->size   = gbuffers * gbufsize;
 
-       /* ***  v4l1  *** ************************************************ */
-       case VIDIOCGCAP:
-       {
-               struct video_capability *cap = arg;
+       for (i = 0; i < gbuffers; i++)
+               mbuf->offsets[i] = i * gbufsize;
 
-               memset(cap,0,sizeof(*cap));
-               strcpy(cap->name,btv->video_dev->name);
-               if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
-                       /* vbi */
-                       cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT;
-               } else {
-                       /* others */
-                       cap->type = VID_TYPE_CAPTURE|
-                               VID_TYPE_TUNER|
-                               VID_TYPE_CLIPPING|
-                               VID_TYPE_SCALES;
-                       if (no_overlay <= 0)
-                               cap->type |= VID_TYPE_OVERLAY;
-
-                       cap->maxwidth  = bttv_tvnorms[btv->tvnorm].swidth;
-                       cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
-                       cap->minwidth  = 48;
-                       cap->minheight = 32;
-               }
-               cap->channels  = bttv_tvcards[btv->c.type].video_inputs;
-               cap->audios    = bttv_tvcards[btv->c.type].audio_inputs;
-               return 0;
+       mutex_unlock(&fh->cap.vb_lock);
+       return 0;
+}
+#endif
+
+static int bttv_querycap(struct file *file, void  *priv,
+                               struct v4l2_capability *cap)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+
+       if (0 == v4l2)
+               return -EINVAL;
+
+       strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+       strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+       snprintf(cap->bus_info, sizeof(cap->bus_info),
+                "PCI:%s", pci_name(btv->c.pci));
+       cap->version = BTTV_VERSION_CODE;
+       cap->capabilities =
+               V4L2_CAP_VIDEO_CAPTURE |
+               V4L2_CAP_VBI_CAPTURE |
+               V4L2_CAP_READWRITE |
+               V4L2_CAP_STREAMING;
+       if (btv->has_saa6588)
+               cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+       if (no_overlay <= 0)
+               cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+       if (btv->tuner_type != TUNER_ABSENT)
+               cap->capabilities |= V4L2_CAP_TUNER;
+       return 0;
+}
+
+static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+{
+       int index = -1, i;
+
+       for (i = 0; i < FORMATS; i++) {
+               if (formats[i].fourcc != -1)
+                       index++;
+               if ((unsigned int)index == f->index)
+                       break;
        }
+       if (FORMATS == i)
+               return -EINVAL;
 
-       case VIDIOCGPICT:
-       {
-               struct video_picture *pic = arg;
-
-               memset(pic,0,sizeof(*pic));
-               pic->brightness = btv->bright;
-               pic->contrast   = btv->contrast;
-               pic->hue        = btv->hue;
-               pic->colour     = btv->saturation;
-               if (fh->fmt) {
-                       pic->depth   = fh->fmt->depth;
-                       pic->palette = fh->fmt->palette;
-               }
-               return 0;
+       f->pixelformat = formats[i].fourcc;
+       strlcpy(f->description, formats[i].name, sizeof(f->description));
+
+       return i;
+}
+
+static int bttv_enum_fmt_vid_cap(struct file *file, void  *priv,
+                               struct v4l2_fmtdesc *f)
+{
+       int rc = bttv_enum_fmt_cap_ovr(f);
+
+       if (rc < 0)
+               return rc;
+
+       return 0;
+}
+
+static int bttv_enum_fmt_vid_overlay(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+{
+       int rc;
+
+       if (no_overlay > 0) {
+               printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+               return -EINVAL;
        }
-       case VIDIOCSPICT:
-       {
-               struct video_picture *pic = arg;
-               const struct bttv_format *fmt;
 
-               fmt = format_by_palette(pic->palette);
-               if (NULL == fmt)
+       rc = bttv_enum_fmt_cap_ovr(f);
+
+       if (rc < 0)
+               return rc;
+
+       if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int bttv_g_fbuf(struct file *file, void *f,
+                               struct v4l2_framebuffer *fb)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+
+       *fb = btv->fbuf;
+       fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+       if (fh->ovfmt)
+               fb->fmt.pixelformat  = fh->ovfmt->fourcc;
+       return 0;
+}
+
+static int bttv_overlay(struct file *file, void *f, unsigned int on)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       struct bttv_buffer *new;
+       int retval;
+
+       if (on) {
+               /* verify args */
+               if (NULL == btv->fbuf.base)
+                       return -EINVAL;
+               if (!fh->ov.setup_ok) {
+                       dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr);
                        return -EINVAL;
-               mutex_lock(&fh->cap.lock);
-               if (fmt->depth != pic->depth) {
-                       retval = -EINVAL;
-                       goto fh_unlock_and_return;
-               }
-               if (fmt->flags & FORMAT_FLAGS_RAW) {
-                       /* VIDIOCMCAPTURE uses gbufsize, not RAW_BPL *
-                          RAW_LINES * 2. F1 is stored at offset 0, F2
-                          at buffer size / 2. */
-                       fh->width = RAW_BPL;
-                       fh->height = gbufsize / RAW_BPL;
-                       btv->init.width  = RAW_BPL;
-                       btv->init.height = gbufsize / RAW_BPL;
-               }
-               fh->ovfmt   = fmt;
-               fh->fmt     = fmt;
-               btv->init.ovfmt   = fmt;
-               btv->init.fmt     = fmt;
-               if (bigendian) {
-                       /* dirty hack time:  swap bytes for overlay if the
-                          display adaptor is big endian (insmod option) */
-                       if (fmt->palette == VIDEO_PALETTE_RGB555 ||
-                           fmt->palette == VIDEO_PALETTE_RGB565 ||
-                           fmt->palette == VIDEO_PALETTE_RGB32) {
-                               fh->ovfmt = fmt+1;
-                       }
                }
-               bt848_bright(btv,pic->brightness);
-               bt848_contrast(btv,pic->contrast);
-               bt848_hue(btv,pic->hue);
-               bt848_sat(btv,pic->colour);
-               mutex_unlock(&fh->cap.lock);
-               return 0;
        }
 
-       case VIDIOCGWIN:
-       {
-               struct video_window *win = arg;
+       if (!check_alloc_btres(btv, fh, RESOURCE_OVERLAY))
+               return -EBUSY;
 
-               memset(win,0,sizeof(*win));
-               win->x      = fh->ov.w.left;
-               win->y      = fh->ov.w.top;
-               win->width  = fh->ov.w.width;
-               win->height = fh->ov.w.height;
-               return 0;
+       mutex_lock(&fh->cap.vb_lock);
+       if (on) {
+               fh->ov.tvnorm = btv->tvnorm;
+               new = videobuf_sg_alloc(sizeof(*new));
+               new->crop = btv->crop[!!fh->do_crop].rect;
+               bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+       } else {
+               new = NULL;
        }
-       case VIDIOCSWIN:
-       {
-               struct video_window *win = arg;
-               struct v4l2_window w2;
 
-               if (no_overlay > 0) {
-                       printk ("VIDIOCSWIN: no_overlay\n");
-                       return -EINVAL;
-               }
+       /* switch over */
+       retval = bttv_switch_overlay(btv, fh, new);
+       mutex_unlock(&fh->cap.vb_lock);
+       return retval;
+}
 
-               w2.field = V4L2_FIELD_ANY;
-               w2.w.left    = win->x;
-               w2.w.top     = win->y;
-               w2.w.width   = win->width;
-               w2.w.height  = win->height;
-               w2.clipcount = win->clipcount;
-               w2.clips     = (struct v4l2_clip __user *)win->clips;
-               retval = setup_window(fh, btv, &w2, 0);
-               if (0 == retval) {
-                       /* on v4l1 this ioctl affects the read() size too */
-                       fh->width  = fh->ov.w.width;
-                       fh->height = fh->ov.w.height;
-                       btv->init.width  = fh->ov.w.width;
-                       btv->init.height = fh->ov.w.height;
-               }
-               return retval;
+static int bttv_s_fbuf(struct file *file, void *f,
+                               struct v4l2_framebuffer *fb)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       const struct bttv_format *fmt;
+       int retval;
+
+       if (!capable(CAP_SYS_ADMIN) &&
+               !capable(CAP_SYS_RAWIO))
+               return -EPERM;
+
+       /* check args */
+       fmt = format_by_fourcc(fb->fmt.pixelformat);
+       if (NULL == fmt)
+               return -EINVAL;
+       if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+
+       retval = -EINVAL;
+       if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+               __s32 width = fb->fmt.width;
+               __s32 height = fb->fmt.height;
+
+               retval = limit_scaled_size(fh, &width, &height,
+                                          V4L2_FIELD_INTERLACED,
+                                          /* width_mask */ ~3,
+                                          /* width_bias */ 2,
+                                          /* adjust_size */ 0,
+                                          /* adjust_crop */ 0);
+               if (0 != retval)
+                       return retval;
        }
 
-       case VIDIOCGFBUF:
-       {
-               struct video_buffer *fbuf = arg;
-
-               fbuf->base          = btv->fbuf.base;
-               fbuf->width         = btv->fbuf.fmt.width;
-               fbuf->height        = btv->fbuf.fmt.height;
-               fbuf->bytesperline  = btv->fbuf.fmt.bytesperline;
-               if (fh->ovfmt)
-                       fbuf->depth = fh->ovfmt->depth;
-               else {
-                       if (fbuf->width)
-                               fbuf->depth   = ((fbuf->bytesperline<<3)
-                                                 + (fbuf->width-1) )
-                                                 /fbuf->width;
-                       else
-                               fbuf->depth = 0;
+       /* ok, accept it */
+       mutex_lock(&fh->cap.vb_lock);
+       btv->fbuf.base       = fb->base;
+       btv->fbuf.fmt.width  = fb->fmt.width;
+       btv->fbuf.fmt.height = fb->fmt.height;
+       if (0 != fb->fmt.bytesperline)
+               btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+       else
+               btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+
+       retval = 0;
+       fh->ovfmt = fmt;
+       btv->init.ovfmt = fmt;
+       if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+               fh->ov.w.left   = 0;
+               fh->ov.w.top    = 0;
+               fh->ov.w.width  = fb->fmt.width;
+               fh->ov.w.height = fb->fmt.height;
+               btv->init.ov.w.width  = fb->fmt.width;
+               btv->init.ov.w.height = fb->fmt.height;
+                       kfree(fh->ov.clips);
+               fh->ov.clips = NULL;
+               fh->ov.nclips = 0;
+
+               if (check_btres(fh, RESOURCE_OVERLAY)) {
+                       struct bttv_buffer *new;
+
+                       new = videobuf_sg_alloc(sizeof(*new));
+                       new->crop = btv->crop[!!fh->do_crop].rect;
+                       bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+                       retval = bttv_switch_overlay(btv, fh, new);
                }
-               return 0;
-       }
-       case VIDIOCSFBUF:
-       {
-               struct video_buffer *fbuf = arg;
-               const struct bttv_format *fmt;
-               unsigned long end;
-
-               if(!capable(CAP_SYS_ADMIN) &&
-                  !capable(CAP_SYS_RAWIO))
-                       return -EPERM;
-               end = (unsigned long)fbuf->base +
-                       fbuf->height * fbuf->bytesperline;
-               mutex_lock(&fh->cap.lock);
-               retval = -EINVAL;
-
-               switch (fbuf->depth) {
-               case 8:
-                       fmt = format_by_palette(VIDEO_PALETTE_HI240);
-                       break;
-               case 16:
-                       fmt = format_by_palette(VIDEO_PALETTE_RGB565);
-                       break;
-               case 24:
-                       fmt = format_by_palette(VIDEO_PALETTE_RGB24);
-                       break;
-               case 32:
-                       fmt = format_by_palette(VIDEO_PALETTE_RGB32);
-                       break;
-               case 15:
-                       fbuf->depth = 16;
-                       fmt = format_by_palette(VIDEO_PALETTE_RGB555);
-                       break;
-               default:
-                       fmt = NULL;
-                       break;
-               }
-               if (NULL == fmt)
-                       goto fh_unlock_and_return;
-
-               fh->ovfmt = fmt;
-               fh->fmt   = fmt;
-               btv->init.ovfmt = fmt;
-               btv->init.fmt   = fmt;
-               btv->fbuf.base             = fbuf->base;
-               btv->fbuf.fmt.width        = fbuf->width;
-               btv->fbuf.fmt.height       = fbuf->height;
-               if (fbuf->bytesperline)
-                       btv->fbuf.fmt.bytesperline = fbuf->bytesperline;
-               else
-                       btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fbuf->depth/8;
-               mutex_unlock(&fh->cap.lock);
-               return 0;
        }
+       mutex_unlock(&fh->cap.vb_lock);
+       return retval;
+}
 
-       case VIDIOCCAPTURE:
-       case VIDIOC_OVERLAY:
-       {
-               struct bttv_buffer *new;
-               int *on = arg;
+static int bttv_reqbufs(struct file *file, void *priv,
+                               struct v4l2_requestbuffers *p)
+{
+       struct bttv_fh *fh = priv;
+       return videobuf_reqbufs(bttv_queue(fh), p);
+}
 
-               if (*on) {
-                       /* verify args */
-                       if (NULL == btv->fbuf.base)
-                               return -EINVAL;
-                       if (!fh->ov.setup_ok) {
-                               dprintk("bttv%d: overlay: !setup_ok\n",btv->c.nr);
-                               return -EINVAL;
-                       }
-               }
+static int bttv_querybuf(struct file *file, void *priv,
+                               struct v4l2_buffer *b)
+{
+       struct bttv_fh *fh = priv;
+       return videobuf_querybuf(bttv_queue(fh), b);
+}
 
-               if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
-                       return -EBUSY;
+static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int res = bttv_resource(fh);
 
-               mutex_lock(&fh->cap.lock);
-               if (*on) {
-                       fh->ov.tvnorm = btv->tvnorm;
-                       new = videobuf_alloc(sizeof(*new));
-                       bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
-               } else {
-                       new = NULL;
-               }
+       if (!check_alloc_btres(btv, fh, res))
+               return -EBUSY;
 
-               /* switch over */
-               retval = bttv_switch_overlay(btv,fh,new);
-               mutex_unlock(&fh->cap.lock);
+       return videobuf_qbuf(bttv_queue(fh), b);
+}
+
+static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+       struct bttv_fh *fh = priv;
+       return videobuf_dqbuf(bttv_queue(fh), b,
+                       file->f_flags & O_NONBLOCK);
+}
+
+static int bttv_streamon(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int res = bttv_resource(fh);
+
+       if (!check_alloc_btres(btv, fh, res))
+               return -EBUSY;
+       return videobuf_streamon(bttv_queue(fh));
+}
+
+
+static int bttv_streamoff(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int retval;
+       int res = bttv_resource(fh);
+
+
+       retval = videobuf_streamoff(bttv_queue(fh));
+       if (retval < 0)
                return retval;
-       }
+       free_btres(btv, fh, res);
+       return 0;
+}
 
-       case VIDIOCGMBUF:
-       {
-               struct video_mbuf *mbuf = arg;
-               unsigned int i;
-
-               mutex_lock(&fh->cap.lock);
-               retval = videobuf_mmap_setup(&fh->cap,gbuffers,gbufsize,
-                                            V4L2_MEMORY_MMAP);
-               if (retval < 0)
-                       goto fh_unlock_and_return;
-               memset(mbuf,0,sizeof(*mbuf));
-               mbuf->frames = gbuffers;
-               mbuf->size   = gbuffers * gbufsize;
-               for (i = 0; i < gbuffers; i++)
-                       mbuf->offsets[i] = i * gbufsize;
-               mutex_unlock(&fh->cap.lock);
-               return 0;
-       }
-       case VIDIOCMCAPTURE:
-       {
-               struct video_mmap *vm = arg;
-               struct bttv_buffer *buf;
-               enum v4l2_field field;
+static int bttv_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       const struct v4l2_queryctrl *ctrl;
 
-               if (vm->frame >= VIDEO_MAX_FRAME)
-                       return -EINVAL;
+       if ((c->id <  V4L2_CID_BASE ||
+            c->id >= V4L2_CID_LASTP1) &&
+           (c->id <  V4L2_CID_PRIVATE_BASE ||
+            c->id >= V4L2_CID_PRIVATE_LASTP1))
+               return -EINVAL;
 
-               mutex_lock(&fh->cap.lock);
-               retval = -EINVAL;
-               buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame];
-               if (NULL == buf)
-                       goto fh_unlock_and_return;
-               if (0 == buf->vb.baddr)
-                       goto fh_unlock_and_return;
-               if (buf->vb.state == STATE_QUEUED ||
-                   buf->vb.state == STATE_ACTIVE)
-                       goto fh_unlock_and_return;
-
-               field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2)
-                       ? V4L2_FIELD_INTERLACED
-                       : V4L2_FIELD_BOTTOM;
-               retval = bttv_prepare_buffer(&fh->cap,btv,buf,
-                                            format_by_palette(vm->format),
-                                            vm->width,vm->height,field);
-               if (0 != retval)
-                       goto fh_unlock_and_return;
-               spin_lock_irqsave(&btv->s_lock,flags);
-               buffer_queue(&fh->cap,&buf->vb);
-               spin_unlock_irqrestore(&btv->s_lock,flags);
-               mutex_unlock(&fh->cap.lock);
-               return 0;
+       if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+               *c = no_ctl;
+       else {
+               ctrl = ctrl_by_id(c->id);
+
+               *c = (NULL != ctrl) ? *ctrl : no_ctl;
        }
-       case VIDIOCSYNC:
-       {
-               int *frame = arg;
-               struct bttv_buffer *buf;
 
-               if (*frame >= VIDEO_MAX_FRAME)
-                       return -EINVAL;
+       return 0;
+}
+
+static int bttv_g_parm(struct file *file, void *f,
+                               struct v4l2_streamparm *parm)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+
+       v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
+                                   &parm->parm.capture.timeperframe);
+       return 0;
+}
+
+static int bttv_g_tuner(struct file *file, void *priv,
+                               struct v4l2_tuner *t)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+
+       if (btv->tuner_type == TUNER_ABSENT)
+               return -EINVAL;
+       if (0 != t->index)
+               return -EINVAL;
+
+       mutex_lock(&btv->lock);
+       t->rxsubchans = V4L2_TUNER_SUB_MONO;
+       bttv_call_all(btv, tuner, g_tuner, t);
+       strcpy(t->name, "Television");
+       t->capability = V4L2_TUNER_CAP_NORM;
+       t->type       = V4L2_TUNER_ANALOG_TV;
+       if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+               t->signal = 0xffff;
+
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 0);
+
+       mutex_unlock(&btv->lock);
+       return 0;
+}
+
+static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
 
-               mutex_lock(&fh->cap.lock);
-               retval = -EINVAL;
-               buf = (struct bttv_buffer *)fh->cap.bufs[*frame];
-               if (NULL == buf)
-                       goto fh_unlock_and_return;
-               retval = videobuf_waiton(&buf->vb,0,1);
-               if (0 != retval)
-                       goto fh_unlock_and_return;
-               switch (buf->vb.state) {
-               case STATE_ERROR:
-                       retval = -EIO;
-                       /* fall through */
-               case STATE_DONE:
-                       videobuf_dma_sync(&fh->cap,&buf->vb.dma);
-                       bttv_dma_free(&fh->cap,btv,buf);
-                       break;
-               default:
-                       retval = -EINVAL;
-                       break;
-               }
-               mutex_unlock(&fh->cap.lock);
-               return retval;
-       }
+       *p = v4l2_prio_max(&btv->prio);
 
-       case VIDIOCGVBIFMT:
-       {
-               struct vbi_format *fmt = (void *) arg;
-               struct v4l2_format fmt2;
+       return 0;
+}
 
-               if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) {
-                       retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
-                       if (0 != retval)
-                               return retval;
-               }
-               bttv_vbi_get_fmt(fh, &fmt2);
-
-               memset(fmt,0,sizeof(*fmt));
-               fmt->sampling_rate    = fmt2.fmt.vbi.sampling_rate;
-               fmt->samples_per_line = fmt2.fmt.vbi.samples_per_line;
-               fmt->sample_format    = VIDEO_PALETTE_RAW;
-               fmt->start[0]         = fmt2.fmt.vbi.start[0];
-               fmt->count[0]         = fmt2.fmt.vbi.count[0];
-               fmt->start[1]         = fmt2.fmt.vbi.start[1];
-               fmt->count[1]         = fmt2.fmt.vbi.count[1];
-               if (fmt2.fmt.vbi.flags & V4L2_VBI_UNSYNC)
-                       fmt->flags   |= VBI_UNSYNC;
-               if (fmt2.fmt.vbi.flags & V4L2_VBI_INTERLACED)
-                       fmt->flags   |= VBI_INTERLACED;
-               return 0;
-       }
-       case VIDIOCSVBIFMT:
-       {
-               struct vbi_format *fmt = (void *) arg;
-               struct v4l2_format fmt2;
+static int bttv_s_priority(struct file *file, void *f,
+                                       enum v4l2_priority prio)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
 
-               retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
-               if (0 != retval)
-                       return retval;
-               bttv_vbi_get_fmt(fh, &fmt2);
-
-               if (fmt->sampling_rate    != fmt2.fmt.vbi.sampling_rate     ||
-                   fmt->samples_per_line != fmt2.fmt.vbi.samples_per_line  ||
-                   fmt->sample_format    != VIDEO_PALETTE_RAW              ||
-                   fmt->start[0]         != fmt2.fmt.vbi.start[0]          ||
-                   fmt->start[1]         != fmt2.fmt.vbi.start[1]          ||
-                   fmt->count[0]         != fmt->count[1]                  ||
-                   fmt->count[0]         <  1                              ||
-                   fmt->count[0]         >  32 /* VBI_MAXLINES */)
-                       return -EINVAL;
+       return v4l2_prio_change(&btv->prio, &fh->prio, prio);
+}
 
-               bttv_vbi_setlines(fh,btv,fmt->count[0]);
-               return 0;
-       }
+static int bttv_cropcap(struct file *file, void *priv,
+                               struct v4l2_cropcap *cap)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
 
-       case BTTV_VERSION:
-       case VIDIOCGFREQ:
-       case VIDIOCSFREQ:
-       case VIDIOCGTUNER:
-       case VIDIOCSTUNER:
-       case VIDIOCGCHAN:
-       case VIDIOCSCHAN:
-       case VIDIOCGAUDIO:
-       case VIDIOCSAUDIO:
-               return bttv_common_ioctls(btv,cmd,arg);
-
-       /* ***  v4l2  *** ************************************************ */
-       case VIDIOC_QUERYCAP:
-       {
-               struct v4l2_capability *cap = arg;
+       if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
 
-               if (0 == v4l2)
-                       return -EINVAL;
-               memset(cap, 0, sizeof (*cap));
-               strlcpy(cap->driver, "bttv", sizeof (cap->driver));
-               strlcpy(cap->card, btv->video_dev->name, sizeof (cap->card));
-               snprintf(cap->bus_info, sizeof (cap->bus_info),
-                        "PCI:%s", pci_name(btv->c.pci));
-               cap->version = BTTV_VERSION_CODE;
-               cap->capabilities =
-                       V4L2_CAP_VIDEO_CAPTURE |
-                       V4L2_CAP_VBI_CAPTURE |
-                       V4L2_CAP_READWRITE |
-                       V4L2_CAP_STREAMING;
-               if (no_overlay <= 0)
-                       cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
-
-               if (bttv_tvcards[btv->c.type].tuner != UNSET &&
-                   bttv_tvcards[btv->c.type].tuner != TUNER_ABSENT)
-                       cap->capabilities |= V4L2_CAP_TUNER;
-               return 0;
-       }
+       *cap = bttv_tvnorms[btv->tvnorm].cropcap;
 
-       case VIDIOC_ENUM_FMT:
-       {
-               struct v4l2_fmtdesc *f = arg;
-               enum v4l2_buf_type type;
-               unsigned int i;
-               int index;
-
-               type  = f->type;
-               if (V4L2_BUF_TYPE_VBI_CAPTURE == type) {
-                       /* vbi */
-                       index = f->index;
-                       if (0 != index)
-                               return -EINVAL;
-                       memset(f,0,sizeof(*f));
-                       f->index       = index;
-                       f->type        = type;
-                       f->pixelformat = V4L2_PIX_FMT_GREY;
-                       strcpy(f->description,"vbi data");
-                       return 0;
-               }
+       return 0;
+}
 
-               /* video capture + overlay */
-               index = -1;
-               for (i = 0; i < BTTV_FORMATS; i++) {
-                       if (bttv_formats[i].fourcc != -1)
-                               index++;
-                       if ((unsigned int)index == f->index)
-                               break;
-               }
-               if (BTTV_FORMATS == i)
-                       return -EINVAL;
+static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
 
-               switch (f->type) {
-               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-                       break;
-               case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-                       if (!(bttv_formats[i].flags & FORMAT_FLAGS_PACKED))
-                               return -EINVAL;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               memset(f,0,sizeof(*f));
-               f->index       = index;
-               f->type        = type;
-               f->pixelformat = bttv_formats[i].fourcc;
-               strlcpy(f->description,bttv_formats[i].name,sizeof(f->description));
-               return 0;
-       }
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
 
-       case VIDIOC_TRY_FMT:
-       {
-               struct v4l2_format *f = arg;
-               return bttv_try_fmt(fh,btv,f);
-       }
-       case VIDIOC_G_FMT:
-       {
-               struct v4l2_format *f = arg;
-               return bttv_g_fmt(fh,f);
-       }
-       case VIDIOC_S_FMT:
-       {
-               struct v4l2_format *f = arg;
-               return bttv_s_fmt(fh,btv,f);
-       }
+       /* No fh->do_crop = 1; because btv->crop[1] may be
+          inconsistent with fh->width or fh->height and apps
+          do not expect a change here. */
 
-       case VIDIOC_G_FBUF:
-       {
-               struct v4l2_framebuffer *fb = arg;
+       crop->c = btv->crop[!!fh->do_crop].rect;
 
-               *fb = btv->fbuf;
-               fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
-               if (fh->ovfmt)
-                       fb->fmt.pixelformat  = fh->ovfmt->fourcc;
-               return 0;
-       }
-       case VIDIOC_S_FBUF:
-       {
-               struct v4l2_framebuffer *fb = arg;
-               const struct bttv_format *fmt;
+       return 0;
+}
+
+static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       const struct v4l2_rect *b;
+       int retval;
+       struct bttv_crop c;
+       __s32 b_left;
+       __s32 b_top;
+       __s32 b_right;
+       __s32 b_bottom;
+
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
 
-               if(!capable(CAP_SYS_ADMIN) &&
-                  !capable(CAP_SYS_RAWIO))
-                       return -EPERM;
+       retval = v4l2_prio_check(&btv->prio, &fh->prio);
+       if (0 != retval)
+               return retval;
 
-               /* check args */
-               fmt = format_by_fourcc(fb->fmt.pixelformat);
-               if (NULL == fmt)
-                       return -EINVAL;
-               if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
-                       return -EINVAL;
+       /* Make sure tvnorm, vbi_end and the current cropping
+          parameters remain consistent until we're done. Note
+          read() may change vbi_end in check_alloc_btres(). */
+       mutex_lock(&btv->lock);
 
-               mutex_lock(&fh->cap.lock);
-               retval = -EINVAL;
-               if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
-                       if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth)
-                               goto fh_unlock_and_return;
-                       if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight)
-                               goto fh_unlock_and_return;
-               }
+       retval = -EBUSY;
 
-               /* ok, accept it */
-               btv->fbuf.base       = fb->base;
-               btv->fbuf.fmt.width  = fb->fmt.width;
-               btv->fbuf.fmt.height = fb->fmt.height;
-               if (0 != fb->fmt.bytesperline)
-                       btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
-               else
-                       btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
-
-               retval = 0;
-               fh->ovfmt = fmt;
-               btv->init.ovfmt = fmt;
-               if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
-                       fh->ov.w.left   = 0;
-                       fh->ov.w.top    = 0;
-                       fh->ov.w.width  = fb->fmt.width;
-                       fh->ov.w.height = fb->fmt.height;
-                       btv->init.ov.w.width  = fb->fmt.width;
-                       btv->init.ov.w.height = fb->fmt.height;
-                               kfree(fh->ov.clips);
-                       fh->ov.clips = NULL;
-                       fh->ov.nclips = 0;
-
-                       if (check_btres(fh, RESOURCE_OVERLAY)) {
-                               struct bttv_buffer *new;
-
-                               new = videobuf_alloc(sizeof(*new));
-                               bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
-                               retval = bttv_switch_overlay(btv,fh,new);
-                       }
-               }
-               mutex_unlock(&fh->cap.lock);
+       if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+               mutex_unlock(&btv->lock);
                return retval;
        }
 
-       case VIDIOC_REQBUFS:
-               return videobuf_reqbufs(bttv_queue(fh),arg);
+       b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
 
-       case VIDIOC_QUERYBUF:
-               return videobuf_querybuf(bttv_queue(fh),arg);
+       b_left = b->left;
+       b_right = b_left + b->width;
+       b_bottom = b->top + b->height;
 
-       case VIDIOC_QBUF:
-               return videobuf_qbuf(bttv_queue(fh),arg);
+       b_top = max(b->top, btv->vbi_end);
+       if (b_top + 32 >= b_bottom) {
+               mutex_unlock(&btv->lock);
+               return retval;
+       }
 
-       case VIDIOC_DQBUF:
-               return videobuf_dqbuf(bttv_queue(fh),arg,
-                                     file->f_flags & O_NONBLOCK);
+       /* Min. scaled size 48 x 32. */
+       c.rect.left = clamp(crop->c.left, b_left, b_right - 48);
+       c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
 
-       case VIDIOC_STREAMON:
-       {
-               int res = bttv_resource(fh);
+       c.rect.width = clamp(crop->c.width,
+                            48, b_right - c.rect.left);
 
-               if (!check_alloc_btres(btv,fh,res))
-                       return -EBUSY;
-               return videobuf_streamon(bttv_queue(fh));
-       }
-       case VIDIOC_STREAMOFF:
-       {
-               int res = bttv_resource(fh);
+       c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32);
+       /* Top and height must be a multiple of two. */
+       c.rect.top = (c.rect.top + 1) & ~1;
 
-               retval = videobuf_streamoff(bttv_queue(fh));
-               if (retval < 0)
-                       return retval;
-               free_btres(btv,fh,res);
-               return 0;
-       }
+       c.rect.height = clamp(crop->c.height,
+                             32, b_bottom - c.rect.top);
+       c.rect.height = (c.rect.height + 1) & ~1;
 
-       case VIDIOC_QUERYCTRL:
-       {
-               struct v4l2_queryctrl *c = arg;
-               int i;
+       bttv_crop_calc_limits(&c);
 
-               if ((c->id <  V4L2_CID_BASE ||
-                    c->id >= V4L2_CID_LASTP1) &&
-                   (c->id <  V4L2_CID_PRIVATE_BASE ||
-                    c->id >= V4L2_CID_PRIVATE_LASTP1))
-                       return -EINVAL;
-               for (i = 0; i < BTTV_CTLS; i++)
-                       if (bttv_ctls[i].id == c->id)
-                               break;
-               if (i == BTTV_CTLS) {
-                       *c = no_ctl;
-                       return 0;
-               }
-               *c = bttv_ctls[i];
-               if (btv->audio_hook && i >= 4 && i <= 8) {
-                       struct video_audio va;
-                       memset(&va,0,sizeof(va));
-                       btv->audio_hook(btv,&va,0);
-                       switch (bttv_ctls[i].id) {
-                       case V4L2_CID_AUDIO_VOLUME:
-                               if (!(va.flags & VIDEO_AUDIO_VOLUME))
-                                       *c = no_ctl;
-                               break;
-                       case V4L2_CID_AUDIO_BALANCE:
-                               if (!(va.flags & VIDEO_AUDIO_BALANCE))
-                                       *c = no_ctl;
-                               break;
-                       case V4L2_CID_AUDIO_BASS:
-                               if (!(va.flags & VIDEO_AUDIO_BASS))
-                                       *c = no_ctl;
-                               break;
-                       case V4L2_CID_AUDIO_TREBLE:
-                               if (!(va.flags & VIDEO_AUDIO_TREBLE))
-                                       *c = no_ctl;
-                               break;
-                       }
-               }
-               return 0;
-       }
-       case VIDIOC_G_CTRL:
-               return get_control(btv,arg);
-       case VIDIOC_S_CTRL:
-               return set_control(btv,arg);
-       case VIDIOC_G_PARM:
-       {
-               struct v4l2_streamparm *parm = arg;
-               struct v4l2_standard s;
-               if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
-               memset(parm,0,sizeof(*parm));
-               v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id,
-                                        bttv_tvnorms[btv->tvnorm].name);
-               parm->parm.capture.timeperframe = s.frameperiod;
-               return 0;
-       }
+       btv->crop[1] = c;
 
-       case VIDIOC_G_PRIORITY:
-       {
-               enum v4l2_priority *p = arg;
+       mutex_unlock(&btv->lock);
 
-               *p = v4l2_prio_max(&btv->prio);
-               return 0;
+       fh->do_crop = 1;
+
+       mutex_lock(&fh->cap.vb_lock);
+
+       if (fh->width < c.min_scaled_width) {
+               fh->width = c.min_scaled_width;
+               btv->init.width = c.min_scaled_width;
+       } else if (fh->width > c.max_scaled_width) {
+               fh->width = c.max_scaled_width;
+               btv->init.width = c.max_scaled_width;
        }
-       case VIDIOC_S_PRIORITY:
-       {
-               enum v4l2_priority *prio = arg;
 
-               return v4l2_prio_change(&btv->prio, &fh->prio, *prio);
+       if (fh->height < c.min_scaled_height) {
+               fh->height = c.min_scaled_height;
+               btv->init.height = c.min_scaled_height;
+       } else if (fh->height > c.max_scaled_height) {
+               fh->height = c.max_scaled_height;
+               btv->init.height = c.max_scaled_height;
        }
 
-       case VIDIOC_ENUMSTD:
-       case VIDIOC_G_STD:
-       case VIDIOC_S_STD:
-       case VIDIOC_ENUMINPUT:
-       case VIDIOC_G_INPUT:
-       case VIDIOC_S_INPUT:
-       case VIDIOC_G_TUNER:
-       case VIDIOC_S_TUNER:
-       case VIDIOC_G_FREQUENCY:
-       case VIDIOC_S_FREQUENCY:
-       case VIDIOC_LOG_STATUS:
-               return bttv_common_ioctls(btv,cmd,arg);
+       mutex_unlock(&fh->cap.vb_lock);
 
-       default:
-               return -ENOIOCTLCMD;
-       }
        return 0;
+}
 
- fh_unlock_and_return:
-       mutex_unlock(&fh->cap.lock);
-       return retval;
+static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+       if (unlikely(a->index))
+               return -EINVAL;
+
+       strcpy(a->name, "audio");
+       return 0;
 }
 
-static int bttv_ioctl(struct inode *inode, struct file *file,
-                     unsigned int cmd, unsigned long arg)
+static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
 {
-       struct bttv_fh *fh  = file->private_data;
+       if (unlikely(a->index))
+               return -EINVAL;
 
-       switch (cmd) {
-       case BTTV_VBISIZE:
-               bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
-               return fh->lines * 2 * 2048;
-       default:
-               return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);
-       }
+       return 0;
 }
 
 static ssize_t bttv_read(struct file *file, char __user *data,
@@ -2992,10 +3128,14 @@ static ssize_t bttv_read(struct file *file, char __user *data,
 
        switch (fh->type) {
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-               if (locked_btres(fh->btv,RESOURCE_VIDEO))
+               if (!check_alloc_btres(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+                       /* VIDEO_READ in use by another fh,
+                          or VIDEO_STREAM by any fh. */
                        return -EBUSY;
+               }
                retval = videobuf_read_one(&fh->cap, data, count, ppos,
                                           file->f_flags & O_NONBLOCK);
+               free_btres(fh->btv, fh, RESOURCE_VIDEO_READ);
                break;
        case V4L2_BUF_TYPE_VBI_CAPTURE:
                if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI))
@@ -3014,6 +3154,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait)
        struct bttv_fh *fh = file->private_data;
        struct bttv_buffer *buf;
        enum v4l2_field field;
+       unsigned int rc = POLLERR;
 
        if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
                if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI))
@@ -3021,109 +3162,121 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait)
                return videobuf_poll_stream(file, &fh->vbi, wait);
        }
 
-       if (check_btres(fh,RESOURCE_VIDEO)) {
+       if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
+               mutex_lock(&fh->cap.vb_lock);
                /* streaming capture */
                if (list_empty(&fh->cap.stream))
-                       return POLLERR;
+                       goto err;
                buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
        } else {
                /* read() capture */
-               mutex_lock(&fh->cap.lock);
+               mutex_lock(&fh->cap.vb_lock);
                if (NULL == fh->cap.read_buf) {
                        /* need to capture a new frame */
-                       if (locked_btres(fh->btv,RESOURCE_VIDEO)) {
-                               mutex_unlock(&fh->cap.lock);
-                               return POLLERR;
-                       }
-                       fh->cap.read_buf = videobuf_alloc(fh->cap.msize);
-                       if (NULL == fh->cap.read_buf) {
-                               mutex_unlock(&fh->cap.lock);
-                               return POLLERR;
-                       }
+                       if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
+                               goto err;
+                       fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
+                       if (NULL == fh->cap.read_buf)
+                               goto err;
                        fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
                        field = videobuf_next_field(&fh->cap);
                        if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
                                kfree (fh->cap.read_buf);
                                fh->cap.read_buf = NULL;
-                               mutex_unlock(&fh->cap.lock);
-                               return POLLERR;
+                               goto err;
                        }
                        fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
                        fh->cap.read_off = 0;
                }
-               mutex_unlock(&fh->cap.lock);
+               mutex_unlock(&fh->cap.vb_lock);
                buf = (struct bttv_buffer*)fh->cap.read_buf;
        }
 
        poll_wait(file, &buf->vb.done, wait);
-       if (buf->vb.state == STATE_DONE ||
-           buf->vb.state == STATE_ERROR)
-               return POLLIN|POLLRDNORM;
-       return 0;
+       if (buf->vb.state == VIDEOBUF_DONE ||
+           buf->vb.state == VIDEOBUF_ERROR)
+               rc =  POLLIN|POLLRDNORM;
+       else
+               rc = 0;
+err:
+       mutex_unlock(&fh->cap.vb_lock);
+       return rc;
 }
 
-static int bttv_open(struct inode *inode, struct file *file)
+static int bttv_open(struct file *file)
 {
-       int minor = iminor(inode);
-       struct bttv *btv = NULL;
+       struct video_device *vdev = video_devdata(file);
+       struct bttv *btv = video_drvdata(file);
        struct bttv_fh *fh;
        enum v4l2_buf_type type = 0;
-       unsigned int i;
 
-       dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
+       dprintk(KERN_DEBUG "bttv: open dev=%s\n", video_device_node_name(vdev));
 
-       for (i = 0; i < bttv_num; i++) {
-               if (bttvs[i].video_dev &&
-                   bttvs[i].video_dev->minor == minor) {
-                       btv = &bttvs[i];
-                       type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-                       break;
-               }
-               if (bttvs[i].vbi_dev &&
-                   bttvs[i].vbi_dev->minor == minor) {
-                       btv = &bttvs[i];
-                       type = V4L2_BUF_TYPE_VBI_CAPTURE;
-                       break;
-               }
-       }
-       if (NULL == btv)
+       if (vdev->vfl_type == VFL_TYPE_GRABBER) {
+               type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       } else if (vdev->vfl_type == VFL_TYPE_VBI) {
+               type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       } else {
+               WARN_ON(1);
                return -ENODEV;
+       }
+
+       lock_kernel();
 
        dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
                btv->c.nr,v4l2_type_names[type]);
 
        /* allocate per filehandle data */
        fh = kmalloc(sizeof(*fh),GFP_KERNEL);
-       if (NULL == fh)
+       if (NULL == fh) {
+               unlock_kernel();
                return -ENOMEM;
+       }
        file->private_data = fh;
        *fh = btv->init;
        fh->type = type;
        fh->ov.setup_ok = 0;
        v4l2_prio_open(&btv->prio,&fh->prio);
 
-       videobuf_queue_init(&fh->cap, &bttv_video_qops,
-                           btv->c.pci, &btv->s_lock,
+       videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
+                           &btv->c.pci->dev, &btv->s_lock,
                            V4L2_BUF_TYPE_VIDEO_CAPTURE,
                            V4L2_FIELD_INTERLACED,
                            sizeof(struct bttv_buffer),
                            fh);
-       videobuf_queue_init(&fh->vbi, &bttv_vbi_qops,
-                           btv->c.pci, &btv->s_lock,
+       videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
+                           &btv->c.pci->dev, &btv->s_lock,
                            V4L2_BUF_TYPE_VBI_CAPTURE,
                            V4L2_FIELD_SEQ_TB,
                            sizeof(struct bttv_buffer),
                            fh);
-       i2c_vidiocschan(btv);
+       set_tvnorm(btv,btv->tvnorm);
+       set_input(btv, btv->input, btv->tvnorm);
 
        btv->users++;
-       if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
-               bttv_vbi_setlines(fh,btv,16);
+
+       /* The V4L2 spec requires one global set of cropping parameters
+          which only change on request. These are stored in btv->crop[1].
+          However for compatibility with V4L apps and cropping unaware
+          V4L2 apps we now reset the cropping parameters as seen through
+          this fh, which is to say VIDIOC_G_CROP and scaling limit checks
+          will use btv->crop[0], the default cropping parameters for the
+          current video standard, and VIDIOC_S_FMT will not implicitely
+          change the cropping parameters until VIDIOC_S_CROP has been
+          called. */
+       fh->do_crop = !reset_crop; /* module parameter */
+
+       /* Likewise there should be one global set of VBI capture
+          parameters, but for compatibility with V4L apps and earlier
+          driver versions each fh has its own parameters. */
+       bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
+
        bttv_field_count(btv);
+       unlock_kernel();
        return 0;
 }
 
-static int bttv_release(struct inode *inode, struct file *file)
+static int bttv_release(struct file *file)
 {
        struct bttv_fh *fh = file->private_data;
        struct bttv *btv = fh->btv;
@@ -3133,21 +3286,21 @@ static int bttv_release(struct inode *inode, struct file *file)
                bttv_switch_overlay(btv,fh,NULL);
 
        /* stop video capture */
-       if (check_btres(fh, RESOURCE_VIDEO)) {
+       if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
                videobuf_streamoff(&fh->cap);
-               free_btres(btv,fh,RESOURCE_VIDEO);
+               free_btres(btv,fh,RESOURCE_VIDEO_STREAM);
        }
        if (fh->cap.read_buf) {
                buffer_release(&fh->cap,fh->cap.read_buf);
                kfree(fh->cap.read_buf);
        }
+       if (check_btres(fh, RESOURCE_VIDEO_READ)) {
+               free_btres(btv, fh, RESOURCE_VIDEO_READ);
+       }
 
        /* stop vbi capture */
        if (check_btres(fh, RESOURCE_VBI)) {
-               if (fh->vbi.streaming)
-                       videobuf_streamoff(&fh->vbi);
-               if (fh->vbi.reading)
-                       videobuf_read_stop(&fh->vbi);
+               videobuf_stop(&fh->vbi);
                free_btres(btv,fh,RESOURCE_VBI);
        }
 
@@ -3160,6 +3313,10 @@ static int bttv_release(struct inode *inode, struct file *file)
 
        btv->users--;
        bttv_field_count(btv);
+
+       if (!btv->users)
+               audio_mute(btv, 1);
+
        return 0;
 }
 
@@ -3174,182 +3331,309 @@ bttv_mmap(struct file *file, struct vm_area_struct *vma)
        return videobuf_mmap_mapper(bttv_queue(fh),vma);
 }
 
-static struct file_operations bttv_fops =
+static const struct v4l2_file_operations bttv_fops =
 {
        .owner    = THIS_MODULE,
        .open     = bttv_open,
        .release  = bttv_release,
-       .ioctl    = bttv_ioctl,
-       .compat_ioctl   = v4l_compat_ioctl32,
-       .llseek   = no_llseek,
+       .ioctl    = video_ioctl2,
        .read     = bttv_read,
        .mmap     = bttv_mmap,
        .poll     = bttv_poll,
 };
 
-static struct video_device bttv_video_template =
-{
-       .name     = "UNSET",
-       .type     = VID_TYPE_CAPTURE|VID_TYPE_TUNER|
-                   VID_TYPE_CLIPPING|VID_TYPE_SCALES,
-       .hardware = VID_HARDWARE_BT848,
-       .fops     = &bttv_fops,
-       .minor    = -1,
+static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
+       .vidioc_querycap                = bttv_querycap,
+       .vidioc_enum_fmt_vid_cap        = bttv_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = bttv_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap         = bttv_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = bttv_s_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_overlay    = bttv_enum_fmt_vid_overlay,
+       .vidioc_g_fmt_vid_overlay       = bttv_g_fmt_vid_overlay,
+       .vidioc_try_fmt_vid_overlay     = bttv_try_fmt_vid_overlay,
+       .vidioc_s_fmt_vid_overlay       = bttv_s_fmt_vid_overlay,
+       .vidioc_g_fmt_vbi_cap           = bttv_g_fmt_vbi_cap,
+       .vidioc_try_fmt_vbi_cap         = bttv_try_fmt_vbi_cap,
+       .vidioc_s_fmt_vbi_cap           = bttv_s_fmt_vbi_cap,
+       .vidioc_g_audio                 = bttv_g_audio,
+       .vidioc_s_audio                 = bttv_s_audio,
+       .vidioc_cropcap                 = bttv_cropcap,
+       .vidioc_reqbufs                 = bttv_reqbufs,
+       .vidioc_querybuf                = bttv_querybuf,
+       .vidioc_qbuf                    = bttv_qbuf,
+       .vidioc_dqbuf                   = bttv_dqbuf,
+       .vidioc_s_std                   = bttv_s_std,
+       .vidioc_enum_input              = bttv_enum_input,
+       .vidioc_g_input                 = bttv_g_input,
+       .vidioc_s_input                 = bttv_s_input,
+       .vidioc_queryctrl               = bttv_queryctrl,
+       .vidioc_g_ctrl                  = bttv_g_ctrl,
+       .vidioc_s_ctrl                  = bttv_s_ctrl,
+       .vidioc_streamon                = bttv_streamon,
+       .vidioc_streamoff               = bttv_streamoff,
+       .vidioc_g_tuner                 = bttv_g_tuner,
+       .vidioc_s_tuner                 = bttv_s_tuner,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+       .vidiocgmbuf                    = vidiocgmbuf,
+#endif
+       .vidioc_g_crop                  = bttv_g_crop,
+       .vidioc_s_crop                  = bttv_s_crop,
+       .vidioc_g_fbuf                  = bttv_g_fbuf,
+       .vidioc_s_fbuf                  = bttv_s_fbuf,
+       .vidioc_overlay                 = bttv_overlay,
+       .vidioc_g_priority              = bttv_g_priority,
+       .vidioc_s_priority              = bttv_s_priority,
+       .vidioc_g_parm                  = bttv_g_parm,
+       .vidioc_g_frequency             = bttv_g_frequency,
+       .vidioc_s_frequency             = bttv_s_frequency,
+       .vidioc_log_status              = bttv_log_status,
+       .vidioc_querystd                = bttv_querystd,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register              = bttv_g_register,
+       .vidioc_s_register              = bttv_s_register,
+#endif
 };
 
-static struct video_device bttv_vbi_template =
-{
-       .name     = "bt848/878 vbi",
-       .type     = VID_TYPE_TUNER|VID_TYPE_TELETEXT,
-       .hardware = VID_HARDWARE_BT848,
-       .fops     = &bttv_fops,
-       .minor    = -1,
+static struct video_device bttv_video_template = {
+       .fops         = &bttv_fops,
+       .ioctl_ops    = &bttv_ioctl_ops,
+       .tvnorms      = BTTV_NORMS,
+       .current_norm = V4L2_STD_PAL,
 };
 
 /* ----------------------------------------------------------------------- */
 /* radio interface                                                         */
 
-static int radio_open(struct inode *inode, struct file *file)
+static int radio_open(struct file *file)
 {
-       int minor = iminor(inode);
-       struct bttv *btv = NULL;
-       unsigned int i;
+       struct video_device *vdev = video_devdata(file);
+       struct bttv *btv = video_drvdata(file);
+       struct bttv_fh *fh;
 
-       dprintk("bttv: open minor=%d\n",minor);
+       dprintk("bttv: open dev=%s\n", video_device_node_name(vdev));
 
-       for (i = 0; i < bttv_num; i++) {
-               if (bttvs[i].radio_dev->minor == minor) {
-                       btv = &bttvs[i];
-                       break;
-               }
-       }
-       if (NULL == btv)
-               return -ENODEV;
+       lock_kernel();
 
        dprintk("bttv%d: open called (radio)\n",btv->c.nr);
+
+       /* allocate per filehandle data */
+       fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+       if (NULL == fh) {
+               unlock_kernel();
+               return -ENOMEM;
+       }
+       file->private_data = fh;
+       *fh = btv->init;
+       v4l2_prio_open(&btv->prio, &fh->prio);
+
        mutex_lock(&btv->lock);
 
        btv->radio_user++;
 
-       file->private_data = btv;
-
-       bttv_call_i2c_clients(btv,AUDC_SET_RADIO,NULL);
+       bttv_call_all(btv, tuner, s_radio);
        audio_input(btv,TVAUDIO_INPUT_RADIO);
 
        mutex_unlock(&btv->lock);
+       unlock_kernel();
        return 0;
 }
 
-static int radio_release(struct inode *inode, struct file *file)
+static int radio_release(struct file *file)
 {
-       struct bttv        *btv = file->private_data;
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
        struct rds_command cmd;
 
+       v4l2_prio_close(&btv->prio,&fh->prio);
+       file->private_data = NULL;
+       kfree(fh);
+
        btv->radio_user--;
 
-       bttv_call_i2c_clients(btv, RDS_CMD_CLOSE, &cmd);
+       bttv_call_all(btv, core, ioctl, RDS_CMD_CLOSE, &cmd);
 
        return 0;
 }
 
-static int radio_do_ioctl(struct inode *inode, struct file *file,
-                         unsigned int cmd, void *arg)
+static int radio_querycap(struct file *file, void *priv,
+                                       struct v4l2_capability *cap)
 {
-       struct bttv    *btv = file->private_data;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
 
-       switch (cmd) {
-       case VIDIOCGCAP:
-       {
-               struct video_capability *cap = arg;
+       strcpy(cap->driver, "bttv");
+       strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+       sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+       cap->version = BTTV_VERSION_CODE;
+       cap->capabilities = V4L2_CAP_TUNER;
 
-               memset(cap,0,sizeof(*cap));
-               strcpy(cap->name,btv->radio_dev->name);
-               cap->type = VID_TYPE_TUNER;
-               cap->channels = 1;
-               cap->audios = 1;
-               return 0;
-       }
+       return 0;
+}
 
-       case VIDIOCGTUNER:
-       {
-               struct video_tuner *v = arg;
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
 
-               if(v->tuner)
-                       return -EINVAL;
-               memset(v,0,sizeof(*v));
-               strcpy(v->name, "Radio");
-               bttv_call_i2c_clients(btv,cmd,v);
-               return 0;
-       }
-       case VIDIOCSTUNER:
-               /* nothing to do */
-               return 0;
+       if (btv->tuner_type == TUNER_ABSENT)
+               return -EINVAL;
+       if (0 != t->index)
+               return -EINVAL;
+       mutex_lock(&btv->lock);
+       strcpy(t->name, "Radio");
+       t->type = V4L2_TUNER_RADIO;
 
-       case BTTV_VERSION:
-       case VIDIOCGFREQ:
-       case VIDIOCSFREQ:
-       case VIDIOCGAUDIO:
-       case VIDIOCSAUDIO:
-       case VIDIOC_LOG_STATUS:
-               return bttv_common_ioctls(btv,cmd,arg);
+       bttv_call_all(btv, tuner, g_tuner, t);
 
-       default:
-               return -ENOIOCTLCMD;
-       }
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 0);
+
+       mutex_unlock(&btv->lock);
+
+       return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *i)
+{
+       if (i->index != 0)
+               return -EINVAL;
+
+       strcpy(i->name, "Radio");
+       i->type = V4L2_INPUT_TYPE_TUNER;
+
+       return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+                                       struct v4l2_audio *a)
+{
+       if (unlikely(a->index))
+               return -EINVAL;
+
+       strcpy(a->name, "Radio");
+
+       return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+{
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+
+       if (0 != t->index)
+               return -EINVAL;
+
+       bttv_call_all(btv, tuner, g_tuner, t);
+       return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+                                       struct v4l2_audio *a)
+{
+       if (unlikely(a->index))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+       if (unlikely(i))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
        return 0;
 }
 
-static int radio_ioctl(struct inode *inode, struct file *file,
-                      unsigned int cmd, unsigned long arg)
+static int radio_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
 {
-       return video_usercopy(inode, file, cmd, arg, radio_do_ioctl);
+       const struct v4l2_queryctrl *ctrl;
+
+       if (c->id <  V4L2_CID_BASE ||
+                       c->id >= V4L2_CID_LASTP1)
+               return -EINVAL;
+
+       if (c->id == V4L2_CID_AUDIO_MUTE) {
+               ctrl = ctrl_by_id(c->id);
+               *c = *ctrl;
+       } else
+               *c = no_ctl;
+
+       return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+       *i = 0;
+       return 0;
 }
 
 static ssize_t radio_read(struct file *file, char __user *data,
                         size_t count, loff_t *ppos)
 {
-       struct bttv    *btv = file->private_data;
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
        struct rds_command cmd;
        cmd.block_count = count/3;
        cmd.buffer = data;
        cmd.instance = file;
        cmd.result = -ENODEV;
 
-       bttv_call_i2c_clients(btv, RDS_CMD_READ, &cmd);
+       bttv_call_all(btv, core, ioctl, RDS_CMD_READ, &cmd);
 
        return cmd.result;
 }
 
 static unsigned int radio_poll(struct file *file, poll_table *wait)
 {
-       struct bttv    *btv = file->private_data;
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
        struct rds_command cmd;
        cmd.instance = file;
        cmd.event_list = wait;
        cmd.result = -ENODEV;
-       bttv_call_i2c_clients(btv, RDS_CMD_POLL, &cmd);
+       bttv_call_all(btv, core, ioctl, RDS_CMD_POLL, &cmd);
 
        return cmd.result;
 }
 
-static struct file_operations radio_fops =
+static const struct v4l2_file_operations radio_fops =
 {
        .owner    = THIS_MODULE,
        .open     = radio_open,
        .read     = radio_read,
        .release  = radio_release,
-       .ioctl    = radio_ioctl,
-       .llseek   = no_llseek,
+       .ioctl    = video_ioctl2,
        .poll     = radio_poll,
 };
 
-static struct video_device radio_template =
-{
-       .name     = "bt848/878 radio",
-       .type     = VID_TYPE_TUNER,
-       .hardware = VID_HARDWARE_BT848,
-       .fops     = &radio_fops,
-       .minor    = -1,
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+       .vidioc_querycap        = radio_querycap,
+       .vidioc_g_tuner         = radio_g_tuner,
+       .vidioc_enum_input      = radio_enum_input,
+       .vidioc_g_audio         = radio_g_audio,
+       .vidioc_s_tuner         = radio_s_tuner,
+       .vidioc_s_audio         = radio_s_audio,
+       .vidioc_s_input         = radio_s_input,
+       .vidioc_s_std           = radio_s_std,
+       .vidioc_queryctrl       = radio_queryctrl,
+       .vidioc_g_input         = radio_g_input,
+       .vidioc_g_ctrl          = bttv_g_ctrl,
+       .vidioc_s_ctrl          = bttv_s_ctrl,
+       .vidioc_g_frequency     = bttv_g_frequency,
+       .vidioc_s_frequency     = bttv_s_frequency,
+};
+
+static struct video_device radio_template = {
+       .fops      = &radio_fops,
+       .ioctl_ops = &radio_ioctl_ops,
 };
 
 /* ----------------------------------------------------------------------- */
@@ -3398,14 +3682,14 @@ static void bttv_risc_disasm(struct bttv *btv,
        unsigned int i,j,n;
 
        printk("%s: risc disasm: %p [dma=0x%08lx]\n",
-              btv->c.name, risc->cpu, (unsigned long)risc->dma);
+              btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
        for (i = 0; i < (risc->size >> 2); i += n) {
-               printk("%s:   0x%lx: ", btv->c.name,
+               printk("%s:   0x%lx: ", btv->c.v4l2_dev.name,
                       (unsigned long)(risc->dma + (i<<2)));
-               n = bttv_risc_decode(risc->cpu[i]);
+               n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
                for (j = 1; j < n; j++)
                        printk("%s:   0x%lx: 0x%08x [ arg #%d ]\n",
-                              btv->c.name, (unsigned long)(risc->dma + ((i+j)<<2)),
+                              btv->c.v4l2_dev.name, (unsigned long)(risc->dma + ((i+j)<<2)),
                               risc->cpu[i+j], j);
                if (0 == risc->cpu[i])
                        break;
@@ -3471,8 +3755,8 @@ static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
        printk("bttv%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
               btv->c.nr,
               (unsigned long)btv->main.dma,
-              (unsigned long)btv->main.cpu[RISC_SLOT_O_VBI+1],
-              (unsigned long)btv->main.cpu[RISC_SLOT_O_FIELD+1],
+              (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+              (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
               (unsigned long)rc);
 
        if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
@@ -3508,11 +3792,34 @@ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
                if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
                    (item->vb.queue.next != &btv->capture)) {
                        item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+                       /* Mike Isely <isely@pobox.com> - Only check
+                        * and set up the bottom field in the logic
+                        * below.  Don't ever do the top field.  This
+                        * of course means that if we set up the
+                        * bottom field in the above code that we'll
+                        * actually skip a field.  But that's OK.
+                        * Having processed only a single buffer this
+                        * time, then the next time around the first
+                        * available buffer should be for a top field.
+                        * That will then cause us here to set up a
+                        * top then a bottom field in the normal way.
+                        * The alternative to this understanding is
+                        * that we set up the second available buffer
+                        * as a top field, but that's out of order
+                        * since this driver always processes the top
+                        * field first - the effect will be the two
+                        * buffers being returned in the wrong order,
+                        * with the second buffer also being delayed
+                        * by one field time (owing to the fifo nature
+                        * of videobuf).  Worse still, we'll be stuck
+                        * doing fields out of order now every time
+                        * until something else causes a field to be
+                        * dropped.  By effectively forcing a field to
+                        * drop this way then we always get back into
+                        * sync within a single frame time.  (Out of
+                        * order fields can screw up deinterlacing
+                        * algorithms.) */
                        if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
-                               if (NULL == set->top &&
-                                   V4L2_FIELD_TOP == item->vb.field) {
-                                       set->top = item;
-                               }
                                if (NULL == set->bottom &&
                                    V4L2_FIELD_BOTTOM == item->vb.field) {
                                        set->bottom = item;
@@ -3631,20 +3938,20 @@ static void bttv_irq_timeout(unsigned long data)
        bttv_set_dma(btv, 0);
 
        /* wake up */
-       bttv_irq_wakeup_video(btv, &old, &new, STATE_ERROR);
-       bttv_irq_wakeup_vbi(btv, ovbi, STATE_ERROR);
+       bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+       bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
 
        /* cancel all outstanding capture / vbi requests */
        while (!list_empty(&btv->capture)) {
                item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
                list_del(&item->vb.queue);
-               item->vb.state = STATE_ERROR;
+               item->vb.state = VIDEOBUF_ERROR;
                wake_up(&item->vb.done);
        }
        while (!list_empty(&btv->vcapture)) {
                item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
                list_del(&item->vb.queue);
-               item->vb.state = STATE_ERROR;
+               item->vb.state = VIDEOBUF_ERROR;
                wake_up(&item->vb.done);
        }
 
@@ -3667,7 +3974,7 @@ bttv_irq_wakeup_top(struct bttv *btv)
 
        do_gettimeofday(&wakeup->vb.ts);
        wakeup->vb.field_count = btv->field_count;
-       wakeup->vb.state = STATE_DONE;
+       wakeup->vb.state = VIDEOBUF_DONE;
        wake_up(&wakeup->vb.done);
        spin_unlock(&btv->s_lock);
 }
@@ -3716,7 +4023,7 @@ bttv_irq_switch_video(struct bttv *btv)
        }
 
        /* wake up finished buffers */
-       bttv_irq_wakeup_video(btv, &old, &new, STATE_DONE);
+       bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
        spin_unlock(&btv->s_lock);
 }
 
@@ -3749,7 +4056,7 @@ bttv_irq_switch_vbi(struct bttv *btv)
        bttv_buffer_activate_vbi(btv, new);
        bttv_set_dma(btv, 0);
 
-       bttv_irq_wakeup_vbi(btv, old, STATE_DONE);
+       bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
        spin_unlock(&btv->s_lock);
 }
 
@@ -3871,8 +4178,8 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
 /* initialitation                                                          */
 
 static struct video_device *vdev_init(struct bttv *btv,
-                                     struct video_device *template,
-                                     char *type)
+                                     const struct video_device *template,
+                                     const char *type_name)
 {
        struct video_device *vfd;
 
@@ -3880,33 +4187,34 @@ static struct video_device *vdev_init(struct bttv *btv,
        if (NULL == vfd)
                return NULL;
        *vfd = *template;
-       vfd->minor   = -1;
-       vfd->dev     = &btv->c.pci->dev;
+       vfd->v4l2_dev = &btv->c.v4l2_dev;
        vfd->release = video_device_release;
+       vfd->debug   = bttv_debug;
+       video_set_drvdata(vfd, btv);
        snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
                 btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
-                type, bttv_tvcards[btv->c.type].name);
+                type_name, bttv_tvcards[btv->c.type].name);
        return vfd;
 }
 
 static void bttv_unregister_video(struct bttv *btv)
 {
        if (btv->video_dev) {
-               if (-1 != btv->video_dev->minor)
+               if (video_is_registered(btv->video_dev))
                        video_unregister_device(btv->video_dev);
                else
                        video_device_release(btv->video_dev);
                btv->video_dev = NULL;
        }
        if (btv->vbi_dev) {
-               if (-1 != btv->vbi_dev->minor)
+               if (video_is_registered(btv->vbi_dev))
                        video_unregister_device(btv->vbi_dev);
                else
                        video_device_release(btv->vbi_dev);
                btv->vbi_dev = NULL;
        }
        if (btv->radio_dev) {
-               if (-1 != btv->radio_dev->minor)
+               if (video_is_registered(btv->radio_dev))
                        video_unregister_device(btv->radio_dev);
                else
                        video_device_release(btv->radio_dev);
@@ -3917,35 +4225,36 @@ static void bttv_unregister_video(struct bttv *btv)
 /* register video4linux devices */
 static int __devinit bttv_register_video(struct bttv *btv)
 {
-       if (no_overlay <= 0) {
-               bttv_video_template.type |= VID_TYPE_OVERLAY;
-       } else {
+       if (no_overlay > 0)
                printk("bttv: Overlay support disabled.\n");
-       }
 
        /* video */
        btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+
        if (NULL == btv->video_dev)
                goto err;
-       if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
+       if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+                                 video_nr[btv->c.nr]) < 0)
                goto err;
-       printk(KERN_INFO "bttv%d: registered device video%d\n",
-              btv->c.nr,btv->video_dev->minor & 0x1f);
-       if (class_device_create_file(&btv->video_dev->class_dev,
-                                    &class_device_attr_card)<0) {
-               printk(KERN_ERR "bttv%d: class_device_create_file 'card' "
+       printk(KERN_INFO "bttv%d: registered device %s\n",
+              btv->c.nr, video_device_node_name(btv->video_dev));
+       if (device_create_file(&btv->video_dev->dev,
+                                    &dev_attr_card)<0) {
+               printk(KERN_ERR "bttv%d: device_create_file 'card' "
                       "failed\n", btv->c.nr);
                goto err;
        }
 
        /* vbi */
-       btv->vbi_dev = vdev_init(btv, &bttv_vbi_template, "vbi");
+       btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+
        if (NULL == btv->vbi_dev)
                goto err;
-       if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0)
+       if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+                                 vbi_nr[btv->c.nr]) < 0)
                goto err;
-       printk(KERN_INFO "bttv%d: registered device vbi%d\n",
-              btv->c.nr,btv->vbi_dev->minor & 0x1f);
+       printk(KERN_INFO "bttv%d: registered device %s\n",
+              btv->c.nr, video_device_node_name(btv->vbi_dev));
 
        if (!btv->has_radio)
                return 0;
@@ -3953,10 +4262,11 @@ static int __devinit bttv_register_video(struct bttv *btv)
        btv->radio_dev = vdev_init(btv, &radio_template, "radio");
        if (NULL == btv->radio_dev)
                goto err;
-       if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0)
+       if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+                                 radio_nr[btv->c.nr]) < 0)
                goto err;
-       printk(KERN_INFO "bttv%d: registered device radio%d\n",
-              btv->c.nr,btv->radio_dev->minor & 0x1f);
+       printk(KERN_INFO "bttv%d: registered device %s\n",
+              btv->c.nr, video_device_node_name(btv->radio_dev));
 
        /* all done */
        return 0;
@@ -3990,14 +4300,17 @@ static int __devinit bttv_probe(struct pci_dev *dev,
        if (bttv_num == BTTV_MAX)
                return -ENOMEM;
        printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
-       btv=&bttvs[bttv_num];
-       memset(btv,0,sizeof(*btv));
+       bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
+       if (btv == NULL) {
+               printk(KERN_ERR "bttv: out of memory.\n");
+               return -ENOMEM;
+       }
        btv->c.nr  = bttv_num;
-       sprintf(btv->c.name,"bttv%d",btv->c.nr);
+       snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
+                       "bttv%d", btv->c.nr);
 
        /* initialize structs / fill in defaults */
        mutex_init(&btv->lock);
-       mutex_init(&btv->reslock);
        spin_lock_init(&btv->s_lock);
        spin_lock_init(&btv->gpio_lock);
        init_waitqueue_head(&btv->gpioq);
@@ -4024,14 +4337,14 @@ static int __devinit bttv_probe(struct pci_dev *dev,
                       btv->c.nr);
                return -EIO;
        }
-       if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) {
+       if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
                printk(KERN_WARNING "bttv%d: No suitable DMA available.\n",
                       btv->c.nr);
                return -EIO;
        }
        if (!request_mem_region(pci_resource_start(dev,0),
                                pci_resource_len(dev,0),
-                               btv->c.name)) {
+                               btv->c.v4l2_dev.name)) {
                printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n",
                       btv->c.nr,
                       (unsigned long long)pci_resource_start(dev,0));
@@ -4039,7 +4352,12 @@ static int __devinit bttv_probe(struct pci_dev *dev,
        }
        pci_set_master(dev);
        pci_set_command(dev);
-       pci_set_drvdata(dev,btv);
+
+       result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
+       if (result < 0) {
+               printk(KERN_WARNING "bttv%d: v4l2_device_register() failed\n", btv->c.nr);
+               goto fail0;
+       }
 
        pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
        pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
@@ -4050,8 +4368,8 @@ static int __devinit bttv_probe(struct pci_dev *dev,
               (unsigned long long)pci_resource_start(dev,0));
        schedule();
 
-       btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000);
-       if (NULL == ioremap(pci_resource_start(dev,0), 0x1000)) {
+       btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+       if (NULL == btv->bt848_mmio) {
                printk("bttv%d: ioremap() failed\n", btv->c.nr);
                result = -EIO;
                goto fail1;
@@ -4063,7 +4381,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
        /* disable irqs, register irq handler */
        btwrite(0, BT848_INT_MASK);
        result = request_irq(btv->c.pci->irq, bttv_irq,
-                            IRQF_SHARED | IRQF_DISABLED,btv->c.name,(void *)btv);
+           IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
        if (result < 0) {
                printk(KERN_ERR "bttv%d: can't get IRQ %d\n",
                       bttv_num,btv->c.pci->irq);
@@ -4092,10 +4410,9 @@ static int __devinit bttv_probe(struct pci_dev *dev,
        btv->init.btv         = btv;
        btv->init.ov.w.width  = 320;
        btv->init.ov.w.height = 240;
-       btv->init.fmt         = format_by_palette(VIDEO_PALETTE_RGB24);
+       btv->init.fmt         = format_by_fourcc(V4L2_PIX_FMT_BGR24);
        btv->init.width       = 320;
        btv->init.height      = 240;
-       btv->init.lines       = 16;
        btv->input = 0;
 
        /* initialize hardware */
@@ -4119,6 +4436,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
 
        /* some card-specific stuff (needs working i2c) */
        bttv_init_card2(btv);
+       bttv_init_tuner(btv);
        init_irqreg(btv);
 
        /* register video4linux + input */
@@ -4129,34 +4447,46 @@ static int __devinit bttv_probe(struct pci_dev *dev,
                bt848_hue(btv,32768);
                bt848_sat(btv,32768);
                audio_mute(btv, 1);
-               set_input(btv,0);
+               set_input(btv, 0, btv->tvnorm);
+               bttv_crop_reset(&btv->crop[0], btv->tvnorm);
+               btv->crop[1] = btv->crop[0]; /* current = default */
+               disclaim_vbi_lines(btv);
+               disclaim_video_lines(btv);
        }
 
-       /* add subdevices */
-       if (bttv_tvcards[btv->c.type].has_dvb)
+       /* add subdevices and autoload dvb-bt8xx if needed */
+       if (bttv_tvcards[btv->c.type].has_dvb) {
                bttv_sub_add_device(&btv->c, "dvb");
+               request_modules(btv);
+       }
 
-       bttv_input_init(btv);
+       if (!disable_ir) {
+               init_bttv_i2c_ir(btv);
+               bttv_input_init(btv);
+       }
 
        /* everything is fine */
        bttv_num++;
        return 0;
 
- fail2:
+fail2:
        free_irq(btv->c.pci->irq,btv);
 
- fail1:
+fail1:
+       v4l2_device_unregister(&btv->c.v4l2_dev);
+
+fail0:
        if (btv->bt848_mmio)
                iounmap(btv->bt848_mmio);
        release_mem_region(pci_resource_start(btv->c.pci,0),
                           pci_resource_len(btv->c.pci,0));
-       pci_set_drvdata(dev,NULL);
        return result;
 }
 
 static void __devexit bttv_remove(struct pci_dev *pci_dev)
 {
-       struct bttv *btv = pci_get_drvdata(pci_dev);
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
 
        if (bttv_verbose)
                printk("bttv%d: unloading\n",btv->c.nr);
@@ -4190,14 +4520,18 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev)
        release_mem_region(pci_resource_start(btv->c.pci,0),
                           pci_resource_len(btv->c.pci,0));
 
-       pci_set_drvdata(pci_dev, NULL);
+       v4l2_device_unregister(&btv->c.v4l2_dev);
+       bttvs[btv->c.nr] = NULL;
+       kfree(btv);
+
        return;
 }
 
 #ifdef CONFIG_PM
 static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
 {
-       struct bttv *btv = pci_get_drvdata(pci_dev);
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
        struct bttv_buffer_set idle;
        unsigned long flags;
 
@@ -4232,7 +4566,8 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
 
 static int bttv_resume(struct pci_dev *pci_dev)
 {
-       struct bttv *btv = pci_get_drvdata(pci_dev);
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
        unsigned long flags;
        int err;
 
@@ -4278,14 +4613,10 @@ static int bttv_resume(struct pci_dev *pci_dev)
 #endif
 
 static struct pci_device_id bttv_pci_tbl[] = {
-       {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
-        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-       {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849,
-        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-       {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878,
-        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-       {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879,
-        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
        {0,}
 };
 
@@ -4302,7 +4633,7 @@ static struct pci_driver bttv_pci_driver = {
 #endif
 };
 
-static int bttv_init_module(void)
+static int __init bttv_init_module(void)
 {
        int ret;
 
@@ -4318,7 +4649,7 @@ static int bttv_init_module(void)
 #endif
        if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
                gbuffers = 2;
-       if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF)
+       if (gbufsize > BTTV_MAX_FBUF)
                gbufsize = BTTV_MAX_FBUF;
        gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
        if (bttv_verbose)
@@ -4332,14 +4663,17 @@ static int bttv_init_module(void)
                printk(KERN_WARNING "bttv: bus_register error: %d\n", ret);
                return ret;
        }
-       return pci_register_driver(&bttv_pci_driver);
+       ret = pci_register_driver(&bttv_pci_driver);
+       if (ret < 0)
+               bus_unregister(&bttv_sub_bus_type);
+
+       return ret;
 }
 
-static void bttv_cleanup_module(void)
+static void __exit bttv_cleanup_module(void)
 {
        pci_unregister_driver(&bttv_pci_driver);
        bus_unregister(&bttv_sub_bus_type);
-       return;
 }
 
 module_init(bttv_init_module);