V4L/DVB (5077): Bttv cropping support
authorMichael Schimek <mschimek@gmx.at>
Thu, 18 Jan 2007 19:17:39 +0000 (16:17 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Wed, 21 Feb 2007 15:34:36 +0000 (13:34 -0200)
Adds the missing VIDIOC_CROPCAP, G_CROP and S_CROP ioctls, permitting
applications to capture or overlay a subsection of the picture or to
extend the capture window beyond active video, into the VBI area and the
horizontal blanking. VBI capturing can start and end on any line,
including the picture area, and apps can capture different lines of each
field and single fields.
For compatibility with existing applications, the open() function
resets the cropping and VBI capturing parameters and a VIDIOC_S_CROP
call is necessary to actually enable cropping.
Regrettably in PAL-M, PAL-N, PAL-Nc and NTSC-JP mode the maximum image
width will increase from 640 and 768 to 747 and 923 pixels respectively.
Like the VBI changes however, this should only affect applications which
depend on former driver limitations, such as never getting more than 640
pixels regardless of the requested width.
Also, new freedoms require additional checks for conflicts and some
applications may not expect an EBUSY error from the VIDIOC_QBUF and
VIDIOCMCAPTURE ioctls. These errors should be rare though.
So far, the patch has been tested on a UP machine with a bt878 in PAL-
BGHI and NTSC-M mode using xawtv, tvtime, mplayer/mencoder, zapping/
libzvbi and these tools: http://zapping.sf.net/bttv-crop-test.tar.bz2
I'd be grateful about comments or bug reports.

Signed-off-by: Michael H. Schimek <mschimek@gmx.at>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/bt8xx/bttv-driver.c
drivers/media/video/bt8xx/bttv-risc.c
drivers/media/video/bt8xx/bttv-vbi.c
drivers/media/video/bt8xx/bttvp.h

index 41fd09d..50dfad4 100644 (file)
@@ -9,6 +9,10 @@
     some v4l2 code lines are taken from Justin's bttv2 driver which is
     (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
 
+    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
@@ -64,6 +68,7 @@ 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;
@@ -103,6 +108,7 @@ 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);
@@ -129,6 +135,8 @@ 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(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)");
@@ -192,6 +200,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 +245,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 +279,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 +308,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 +333,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 +358,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 +382,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 +407,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 +437,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);
@@ -678,25 +785,89 @@ static const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls);
 /* ----------------------------------------------------------------------- */
 /* 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 +882,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 +918,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);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -1030,6 +1239,36 @@ i2c_vidiocschan(struct bttv *btv)
                bttv_tda9880_setnorm(btv,btv->tvnorm);
 }
 
+static void
+bttv_crop_calc_limits(struct bttv_crop *c)
+{
+       /* 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);
+       }
+
+       c->max_scaled_width  = c->rect.width & ~3;
+       c->max_scaled_height = c->rect.height;
+}
+
+static void
+bttv_crop_reset(struct bttv_crop *c, 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)
 {
@@ -1038,9 +1277,24 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
        if (norm < 0 || norm >= BTTV_TVNORMS)
                return -EINVAL;
 
-       btv->tvnorm = norm;
        tvnorm = &bttv_tvnorms[norm];
 
+       if (btv->tvnorm < 0 ||
+           btv->tvnorm >= BTTV_TVNORMS ||
+           0 != 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),
@@ -1057,6 +1311,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
        return 0;
 }
 
+/* Call with btv->lock down. */
 static void
 set_input(struct bttv *btv, unsigned int input)
 {
@@ -1459,13 +1714,13 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
        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 +1734,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 +1749,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,12 +1803,17 @@ 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;
        }
 
@@ -1939,11 +2242,179 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
        return 0;
 }
 
-static int verify_window(const struct bttv_tvnorm *tvn,
-                        struct v4l2_window *win, int fixup)
+/* 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;
+       }
+
+       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;
+
+               max_top = b->top + b->height - c->rect.height;
+               if (c->rect.top > max_top)
+                       c->rect.top = max_top;
+       }
+
+       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;
+               }
+       } else {
+               rc = -EBUSY;
+               if (btv->vbi_end > c->rect.top)
+                       goto fail;
+
+               min_width  = c->min_scaled_width;
+               min_height = c->min_scaled_height;
+               max_width  = c->max_scaled_width;
+               max_height = c->max_scaled_height;
+
+               adjust_crop = 0;
+       }
+
+       min_width = (min_width - width_mask - 1) & width_mask;
+       max_width = max_width & width_mask;
+
+       /* 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;
+       }
+
+       rc = 0; /* success */
+
+ fail:
+       mutex_unlock(&btv->lock);
+
+       return rc;
+}
+
+/* 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 +2422,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 +2482,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;
 
@@ -2048,6 +2541,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
                struct bttv_buffer *new;
 
                new = videobuf_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);
        }
@@ -2080,7 +2574,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;
@@ -2138,7 +2632,7 @@ static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f)
                f->fmt.win.field = fh->ov.field;
                return 0;
        case V4L2_BUF_TYPE_VBI_CAPTURE:
-               bttv_vbi_get_fmt(fh,f);
+               bttv_vbi_get_fmt(fh, &f->fmt.vbi);
                return 0;
        default:
                return -EINVAL;
@@ -2146,35 +2640,35 @@ static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f)
 }
 
 static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv,
-                       struct v4l2_format *f)
+                       struct v4l2_format *f, int adjust_crop)
 {
        switch (f->type) {
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
        {
                const struct bttv_format *fmt;
                enum v4l2_field field;
-               unsigned int maxw,maxh;
+               __s32 width, height;
+               int rc;
 
                fmt = format_by_fourcc(f->fmt.pix.pixelformat);
                if (NULL == fmt)
                        return -EINVAL;
 
-               /* 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)
+               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;
+               }
                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:
@@ -2185,28 +2679,29 @@ static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv,
                        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);
+               if (0 != rc)
+                       return rc;
+
                /* 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);
+               pix_format_set_size(&f->fmt.pix, fmt, width, height);
 
                return 0;
        }
        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-               return verify_window(&bttv_tvnorms[btv->tvnorm],
-                                    &f->fmt.win, 1);
+               return verify_window(fh, &f->fmt.win,
+                                    /* adjust_size */ 1,
+                                    /* adjust_crop */ 0);
        case V4L2_BUF_TYPE_VBI_CAPTURE:
-               bttv_vbi_try_fmt(fh,f);
-               return 0;
+               return bttv_vbi_try_fmt(fh, &f->fmt.vbi);
        default:
                return -EINVAL;
        }
@@ -2225,7 +2720,7 @@ static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv,
                retval = bttv_switch_type(fh,f->type);
                if (0 != retval)
                        return retval;
-               retval = bttv_try_fmt(fh,btv,f);
+               retval = bttv_try_fmt(fh,btv,f, /* adjust_crop */ 1);
                if (0 != retval)
                        return retval;
                fmt = format_by_fourcc(f->fmt.pix.pixelformat);
@@ -2254,12 +2749,7 @@ static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv,
                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;
+               return bttv_vbi_set_fmt(fh, &f->fmt.vbi);
        default:
                return -EINVAL;
        }
@@ -2517,6 +3007,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                if (*on) {
                        fh->ov.tvnorm = btv->tvnorm;
                        new = videobuf_alloc(sizeof(*new));
+                       new->crop = btv->crop[!!fh->do_crop].rect;
                        bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
                } else {
                        new = NULL;
@@ -2551,10 +3042,16 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                struct video_mmap *vm = arg;
                struct bttv_buffer *buf;
                enum v4l2_field field;
+               __s32 height2;
+               int res;
 
                if (vm->frame >= VIDEO_MAX_FRAME)
                        return -EINVAL;
 
+               res = bttv_resource(fh);
+               if (!check_alloc_btres(btv, fh, res))
+                       return -EBUSY;
+
                mutex_lock(&fh->cap.lock);
                retval = -EINVAL;
                buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame];
@@ -2566,7 +3063,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                    buf->vb.state == STATE_ACTIVE)
                        goto fh_unlock_and_return;
 
-               field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2)
+               height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+               field = (vm->height > height2)
                        ? V4L2_FIELD_INTERLACED
                        : V4L2_FIELD_BOTTOM;
                retval = bttv_prepare_buffer(&fh->cap,btv,buf,
@@ -2613,54 +3111,17 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
        }
 
        case VIDIOCGVBIFMT:
-       {
-               struct vbi_format *fmt = (void *) arg;
-               struct v4l2_format fmt2;
-
                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;
 
-               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;
+               /* fall through */
 
-               bttv_vbi_setlines(fh,btv,fmt->count[0]);
-               return 0;
-       }
+       case VIDIOCSVBIFMT:
+               return v4l_compat_translate_ioctl(inode, file, cmd,
+                                                 arg, bttv_do_ioctl);
 
        case BTTV_VERSION:
        case VIDIOCGFREQ:
@@ -2753,7 +3214,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
        case VIDIOC_TRY_FMT:
        {
                struct v4l2_format *f = arg;
-               return bttv_try_fmt(fh,btv,f);
+               return bttv_try_fmt(fh,btv,f, /* adjust_crop */ 0);
        }
        case VIDIOC_G_FMT:
        {
@@ -2792,16 +3253,23 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
                        return -EINVAL;
 
-               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;
+                       __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;
                }
 
                /* ok, accept it */
+               mutex_lock(&fh->cap.lock);
                btv->fbuf.base       = fb->base;
                btv->fbuf.fmt.width  = fb->fmt.width;
                btv->fbuf.fmt.height = fb->fmt.height;
@@ -2828,6 +3296,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                                struct bttv_buffer *new;
 
                                new = videobuf_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);
                        }
@@ -2843,7 +3312,13 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                return videobuf_querybuf(bttv_queue(fh),arg);
 
        case VIDIOC_QBUF:
+       {
+               int res = bttv_resource(fh);
+
+               if (!check_alloc_btres(btv, fh, res))
+                       return -EBUSY;
                return videobuf_qbuf(bttv_queue(fh),arg);
+       }
 
        case VIDIOC_DQBUF:
                return videobuf_dqbuf(bttv_queue(fh),arg,
@@ -2942,6 +3417,122 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
                return v4l2_prio_change(&btv->prio, &fh->prio, *prio);
        }
 
+       case VIDIOC_CROPCAP:
+       {
+               struct v4l2_cropcap *cap = arg;
+               enum v4l2_buf_type type;
+
+               type = cap->type;
+
+               if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+                   type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+                       return -EINVAL;
+
+               *cap = bttv_tvnorms[btv->tvnorm].cropcap;
+               cap->type = type;
+
+               return 0;
+       }
+       case VIDIOC_G_CROP:
+       {
+               struct v4l2_crop * crop = arg;
+
+               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+                   crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+                       return -EINVAL;
+
+               /* 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. */
+
+               crop->c = btv->crop[!!fh->do_crop].rect;
+
+               return 0;
+       }
+       case VIDIOC_S_CROP:
+       {
+               struct v4l2_crop *crop = arg;
+               const struct v4l2_rect *b;
+               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;
+
+               retval = v4l2_prio_check(&btv->prio,&fh->prio);
+               if (0 != retval)
+                       return retval;
+
+               /* 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);
+
+               retval = -EBUSY;
+
+               if (locked_btres(fh->btv, VIDEO_RESOURCES))
+                       goto btv_unlock_and_return;
+
+               b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+               b_left = b->left;
+               b_right = b_left + b->width;
+               b_bottom = b->top + b->height;
+
+               b_top = max(b->top, btv->vbi_end);
+               if (b_top + 32 >= b_bottom)
+                       goto btv_unlock_and_return;
+
+               /* 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);
+
+               c.rect.width = clamp(crop->c.width,
+                                    48, b_right - c.rect.left);
+
+               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;
+
+               c.rect.height = clamp(crop->c.height,
+                                     32, b_bottom - c.rect.top);
+               c.rect.height = (c.rect.height + 1) & ~1;
+
+               bttv_crop_calc_limits(&c);
+
+               btv->crop[1] = c;
+
+               mutex_unlock(&btv->lock);
+
+               fh->do_crop = 1;
+
+               mutex_lock(&fh->cap.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;
+               }
+
+               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;
+               }
+
+               mutex_unlock(&fh->cap.lock);
+
+               return 0;
+       }
+
        case VIDIOC_ENUMSTD:
        case VIDIOC_G_STD:
        case VIDIOC_S_STD:
@@ -2963,6 +3554,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
  fh_unlock_and_return:
        mutex_unlock(&fh->cap.lock);
        return retval;
+
+ btv_unlock_and_return:
+       mutex_unlock(&btv->lock);
+       return retval;
 }
 
 static int bttv_ioctl(struct inode *inode, struct file *file,
@@ -2972,8 +3567,26 @@ static int bttv_ioctl(struct inode *inode, struct file *file,
 
        switch (cmd) {
        case BTTV_VBISIZE:
+       {
+               const struct bttv_tvnorm *tvnorm;
+
+               tvnorm = fh->vbi_fmt.tvnorm;
+
+               if (fh->vbi_fmt.fmt.start[0] != tvnorm->vbistart[0] ||
+                   fh->vbi_fmt.fmt.start[1] != tvnorm->vbistart[1] ||
+                   fh->vbi_fmt.fmt.count[0] != fh->vbi_fmt.fmt.count[1]) {
+                       /* BTTV_VBISIZE cannot express these parameters,
+                          however open() resets the paramters to defaults
+                          and apps shouldn't call BTTV_VBISIZE after
+                          VIDIOC_S_FMT. */
+                       return -EINVAL;
+               }
+
                bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
-               return fh->lines * 2 * 2048;
+               return (fh->vbi_fmt.fmt.count[0] * 2
+                       * fh->vbi_fmt.fmt.samples_per_line);
+       }
+
        default:
                return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);
        }
@@ -2992,10 +3605,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))
@@ -3021,7 +3638,7 @@ 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)) {
                /* streaming capture */
                if (list_empty(&fh->cap.stream))
                        return POLLERR;
@@ -3031,7 +3648,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait)
                mutex_lock(&fh->cap.lock);
                if (NULL == fh->cap.read_buf) {
                        /* need to capture a new frame */
-                       if (locked_btres(fh->btv,RESOURCE_VIDEO)) {
+                       if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM)) {
                                mutex_unlock(&fh->cap.lock);
                                return POLLERR;
                        }
@@ -3117,8 +3734,23 @@ static int bttv_open(struct inode *inode, struct file *file)
        i2c_vidiocschan(btv);
 
        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);
        return 0;
 }
@@ -3133,14 +3765,17 @@ 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)) {
@@ -3997,7 +4632,6 @@ static int __devinit bttv_probe(struct pci_dev *dev,
 
        /* 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);
@@ -4095,7 +4729,6 @@ static int __devinit bttv_probe(struct pci_dev *dev,
        btv->init.fmt         = format_by_palette(VIDEO_PALETTE_RGB24);
        btv->init.width       = 320;
        btv->init.height      = 240;
-       btv->init.lines       = 16;
        btv->input = 0;
 
        /* initialize hardware */
@@ -4130,6 +4763,10 @@ static int __devinit bttv_probe(struct pci_dev *dev,
                bt848_sat(btv,32768);
                audio_mute(btv, 1);
                set_input(btv,0);
+               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 */
index afcfe71..e7104d9 100644 (file)
@@ -43,7 +43,8 @@ int
 bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
                 struct scatterlist *sglist,
                 unsigned int offset, unsigned int bpl,
-                unsigned int padding, unsigned int lines)
+                unsigned int padding, unsigned int skip_lines,
+                unsigned int store_lines)
 {
        u32 instructions,line,todo;
        struct scatterlist *sg;
@@ -54,9 +55,11 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
           one write per scan line + sync + jump (all 2 dwords).  padding
           can cause next bpl to start close to a page border.  First DMA
           region may be smaller than PAGE_SIZE */
-       instructions  = 1 + ((bpl + padding) * lines) / PAGE_SIZE + lines;
-       instructions += 2;
-       if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0)
+       instructions  = skip_lines * 4;
+       instructions += (1 + ((bpl + padding) * store_lines)
+                        / PAGE_SIZE + store_lines) * 8;
+       instructions += 2 * 8;
+       if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions)) < 0)
                return rc;
 
        /* sync instruction */
@@ -64,11 +67,16 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
        *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
        *(rp++) = cpu_to_le32(0);
 
+       while (skip_lines-- > 0) {
+               *(rp++) = cpu_to_le32(BT848_RISC_SKIP | BT848_RISC_SOL |
+                                     BT848_RISC_EOL | bpl);
+       }
+
        /* scan lines */
        sg = sglist;
-       for (line = 0; line < lines; line++) {
+       for (line = 0; line < store_lines; line++) {
                if ((btv->opt_vcr_hack) &&
-                   (line >= (lines - VCR_HACK_LINES)))
+                   (line >= (store_lines - VCR_HACK_LINES)))
                        continue;
                while (offset && offset >= sg_dma_len(sg)) {
                        offset -= sg_dma_len(sg);
@@ -130,7 +138,8 @@ bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc,
        /* estimate risc mem: worst case is one write per page border +
           one write per scan line (5 dwords)
           plus sync + jump (2 dwords) */
-       instructions  = (ybpl * ylines * 2) / PAGE_SIZE + ylines;
+       instructions  = ((3 + (ybpl + ypadding) * ylines * 2)
+                        / PAGE_SIZE) + ylines;
        instructions += 2;
        if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0)
                return rc;
@@ -317,10 +326,10 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc,
 /* ---------------------------------------------------------- */
 
 static void
-bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo,
-             int width, int height, int interleaved, int norm)
+bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo,
+                 int width, int height, int interleaved,
+                 const struct bttv_tvnorm *tvnorm)
 {
-       const struct bttv_tvnorm *tvnorm = &bttv_tvnorms[norm];
        u32 xsf, sr;
        int vdelay;
 
@@ -361,6 +370,62 @@ bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo,
 }
 
 static void
+bttv_calc_geo          (struct bttv *                  btv,
+                        struct bttv_geometry *         geo,
+                        unsigned int                   width,
+                        unsigned int                   height,
+                        int                            both_fields,
+                        const struct bttv_tvnorm *     tvnorm,
+                        const struct v4l2_rect *       crop)
+{
+       unsigned int c_width;
+       unsigned int c_height;
+       u32 sr;
+
+       if ((crop->left == tvnorm->cropcap.defrect.left
+            && crop->top == tvnorm->cropcap.defrect.top
+            && crop->width == tvnorm->cropcap.defrect.width
+            && crop->height == tvnorm->cropcap.defrect.height
+            && width <= tvnorm->swidth /* see PAL-Nc et al */)
+           || bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) {
+               bttv_calc_geo_old(btv, geo, width, height,
+                                 both_fields, tvnorm);
+               return;
+       }
+
+       /* For bug compatibility the image size checks permit scale
+          factors > 16. See bttv_crop_calc_limits(). */
+       c_width = min((unsigned int) crop->width, width * 16);
+       c_height = min((unsigned int) crop->height, height * 16);
+
+       geo->width = width;
+       geo->hscale = (c_width * 4096U + (width >> 1)) / width - 4096;
+       /* Even to store Cb first, odd for Cr. */
+       geo->hdelay = ((crop->left * width + c_width) / c_width) & ~1;
+
+       geo->sheight = c_height;
+       geo->vdelay = crop->top - tvnorm->cropcap.bounds.top + MIN_VDELAY;
+       sr = c_height >> !both_fields;
+       sr = (sr * 512U + (height >> 1)) / height - 512;
+       geo->vscale = (0x10000UL - sr) & 0x1fff;
+       geo->vscale |= both_fields ? (BT848_VSCALE_INT << 8) : 0;
+       geo->vtotal = tvnorm->vtotal;
+
+       geo->crop = (((geo->width   >> 8) & 0x03) |
+                    ((geo->hdelay  >> 6) & 0x0c) |
+                    ((geo->sheight >> 4) & 0x30) |
+                    ((geo->vdelay  >> 2) & 0xc0));
+
+       if (btv->opt_combfilter) {
+               geo->vtc  = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+               geo->comb = (width < 769) ? 1 : 0;
+       } else {
+               geo->vtc  = 0;
+               geo->comb = 0;
+       }
+}
+
+static void
 bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd)
 {
        int off = odd ? 0x80 : 0x00;
@@ -522,16 +587,51 @@ int
 bttv_buffer_activate_vbi(struct bttv *btv,
                         struct bttv_buffer *vbi)
 {
-       /* vbi capture */
+       struct btcx_riscmem *top;
+       struct btcx_riscmem *bottom;
+       int top_irq_flags;
+       int bottom_irq_flags;
+
+       top = NULL;
+       bottom = NULL;
+       top_irq_flags = 0;
+       bottom_irq_flags = 0;
+
        if (vbi) {
+               unsigned int crop, vdelay;
+
                vbi->vb.state = STATE_ACTIVE;
                list_del(&vbi->vb.queue);
-               bttv_risc_hook(btv, RISC_SLOT_O_VBI, &vbi->top,    0);
-               bttv_risc_hook(btv, RISC_SLOT_E_VBI, &vbi->bottom, 4);
-       } else {
-               bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0);
-               bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0);
+
+               /* VDELAY is start of video, end of VBI capturing. */
+               crop = btread(BT848_E_CROP);
+               vdelay = btread(BT848_E_VDELAY_LO) + ((crop & 0xc0) << 2);
+
+               if (vbi->geo.vdelay > vdelay) {
+                       vdelay = vbi->geo.vdelay & 0xfe;
+                       crop = (crop & 0x3f) | ((vbi->geo.vdelay >> 2) & 0xc0);
+
+                       btwrite(vdelay, BT848_E_VDELAY_LO);
+                       btwrite(crop,   BT848_E_CROP);
+                       btwrite(vdelay, BT848_O_VDELAY_LO);
+                       btwrite(crop,   BT848_O_CROP);
+               }
+
+               if (vbi->vbi_count[0] > 0) {
+                       top = &vbi->top;
+                       top_irq_flags = 4;
+               }
+
+               if (vbi->vbi_count[1] > 0) {
+                       top_irq_flags = 0;
+                       bottom = &vbi->bottom;
+                       bottom_irq_flags = 4;
+               }
        }
+
+       bttv_risc_hook(btv, RISC_SLOT_O_VBI, top, top_irq_flags);
+       bttv_risc_hook(btv, RISC_SLOT_E_VBI, bottom, bottom_irq_flags);
+
        return 0;
 }
 
@@ -611,28 +711,31 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
                int bpf = bpl * (buf->vb.height >> 1);
 
                bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height,
-                             V4L2_FIELD_HAS_BOTH(buf->vb.field),buf->tvnorm);
+                             V4L2_FIELD_HAS_BOTH(buf->vb.field),
+                             tvnorm,&buf->crop);
 
                switch (buf->vb.field) {
                case V4L2_FIELD_TOP:
                        bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist,
-                                        0,bpl,0,buf->vb.height);
+                                        /* offset */ 0,bpl,
+                                        /* padding */ 0,/* skip_lines */ 0,
+                                        buf->vb.height);
                        break;
                case V4L2_FIELD_BOTTOM:
                        bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist,
-                                        0,bpl,0,buf->vb.height);
+                                        0,bpl,0,0,buf->vb.height);
                        break;
                case V4L2_FIELD_INTERLACED:
                        bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist,
-                                        0,bpl,bpl,buf->vb.height >> 1);
+                                        0,bpl,bpl,0,buf->vb.height >> 1);
                        bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist,
-                                        bpl,bpl,bpl,buf->vb.height >> 1);
+                                        bpl,bpl,bpl,0,buf->vb.height >> 1);
                        break;
                case V4L2_FIELD_SEQ_TB:
                        bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist,
-                                        0,bpl,0,buf->vb.height >> 1);
+                                        0,bpl,0,0,buf->vb.height >> 1);
                        bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist,
-                                        bpf,bpl,0,buf->vb.height >> 1);
+                                        bpf,bpl,0,0,buf->vb.height >> 1);
                        break;
                default:
                        BUG();
@@ -662,7 +765,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
                switch (buf->vb.field) {
                case V4L2_FIELD_TOP:
                        bttv_calc_geo(btv,&buf->geo,buf->vb.width,
-                                     buf->vb.height,0,buf->tvnorm);
+                                     buf->vb.height,/* both_fields */ 0,
+                                     tvnorm,&buf->crop);
                        bttv_risc_planar(btv, &buf->top, buf->vb.dma.sglist,
                                         0,buf->vb.width,0,buf->vb.height,
                                         uoffset,voffset,buf->fmt->hshift,
@@ -670,7 +774,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
                        break;
                case V4L2_FIELD_BOTTOM:
                        bttv_calc_geo(btv,&buf->geo,buf->vb.width,
-                                     buf->vb.height,0,buf->tvnorm);
+                                     buf->vb.height,0,
+                                     tvnorm,&buf->crop);
                        bttv_risc_planar(btv, &buf->bottom, buf->vb.dma.sglist,
                                         0,buf->vb.width,0,buf->vb.height,
                                         uoffset,voffset,buf->fmt->hshift,
@@ -678,7 +783,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
                        break;
                case V4L2_FIELD_INTERLACED:
                        bttv_calc_geo(btv,&buf->geo,buf->vb.width,
-                                     buf->vb.height,1,buf->tvnorm);
+                                     buf->vb.height,1,
+                                     tvnorm,&buf->crop);
                        lines    = buf->vb.height >> 1;
                        ypadding = buf->vb.width;
                        cpadding = buf->vb.width >> buf->fmt->hshift;
@@ -700,7 +806,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
                        break;
                case V4L2_FIELD_SEQ_TB:
                        bttv_calc_geo(btv,&buf->geo,buf->vb.width,
-                                     buf->vb.height,1,buf->tvnorm);
+                                     buf->vb.height,1,
+                                     tvnorm,&buf->crop);
                        lines    = buf->vb.height >> 1;
                        ypadding = buf->vb.width;
                        cpadding = buf->vb.width >> buf->fmt->hshift;
@@ -731,11 +838,12 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
                /* build risc code */
                buf->vb.field = V4L2_FIELD_SEQ_TB;
                bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight,
-                             1,buf->tvnorm);
+                             1,tvnorm,&buf->crop);
                bttv_risc_packed(btv, &buf->top,  buf->vb.dma.sglist,
-                                0, RAW_BPL, 0, RAW_LINES);
+                                /* offset */ 0, RAW_BPL, /* padding */ 0,
+                                /* skip_lines */ 0, RAW_LINES);
                bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist,
-                                buf->vb.size/2 , RAW_BPL, 0, RAW_LINES);
+                                buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES);
        }
 
        /* copy format info */
@@ -761,7 +869,8 @@ bttv_overlay_risc(struct bttv *btv,
 
        /* calculate geometry */
        bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height,
-                     V4L2_FIELD_HAS_BOTH(ov->field), ov->tvnorm);
+                     V4L2_FIELD_HAS_BOTH(ov->field),
+                     &bttv_tvnorms[ov->tvnorm],&buf->crop);
 
        /* build risc code */
        switch (ov->field) {
index 6fc6b02..754f66b 100644 (file)
@@ -5,6 +5,9 @@
 
     (c) 2002 Gerd Knorr <kraxel@bytesex.org>
 
+    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
    to be about 244.  */
 #define VBI_OFFSET 244
 
+/* 2048 for compatibility with earlier driver versions. The driver
+   really stores 1024 + tvnorm->vbipack * 4 samples per line in the
+   buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI
+   is 0x1FF DWORDs) and VBI read()s store a frame counter in the last
+   four bytes of the VBI image. */
+#define VBI_BPL 2048
+
+/* Compatibility. */
 #define VBI_DEFLINES 16
-#define VBI_MAXLINES 32
 
 static unsigned int vbibufs = 4;
 static unsigned int vbi_debug = 0;
@@ -58,21 +68,12 @@ MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
 #define dprintk(fmt, arg...)   if (vbi_debug) \
        printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg)
 
+#define IMAGE_SIZE(fmt) \
+       (((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line)
+
 /* ----------------------------------------------------------------------- */
 /* vbi risc code + mm                                                      */
 
-static int
-vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf, int lines)
-{
-       int bpl = 2048;
-
-       bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist,
-                        0, bpl-4, 4, lines);
-       bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist,
-                        lines * bpl, bpl-4, 4, lines);
-       return 0;
-}
-
 static int vbi_buffer_setup(struct videobuf_queue *q,
                            unsigned int *count, unsigned int *size)
 {
@@ -81,8 +82,16 @@ static int vbi_buffer_setup(struct videobuf_queue *q,
 
        if (0 == *count)
                *count = vbibufs;
-       *size = fh->lines * 2 * 2048;
-       dprintk("setup: lines=%d\n",fh->lines);
+
+       *size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
+
+       dprintk("setup: samples=%u start=%d,%d count=%u,%u\n",
+               fh->vbi_fmt.fmt.samples_per_line,
+               fh->vbi_fmt.fmt.start[0],
+               fh->vbi_fmt.fmt.start[1],
+               fh->vbi_fmt.fmt.count[0],
+               fh->vbi_fmt.fmt.count[1]);
+
        return 0;
 }
 
@@ -93,18 +102,93 @@ static int vbi_buffer_prepare(struct videobuf_queue *q,
        struct bttv_fh *fh = q->priv_data;
        struct bttv *btv = fh->btv;
        struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+       const struct bttv_tvnorm *tvnorm;
+       unsigned int skip_lines0, skip_lines1, min_vdelay;
+       int redo_dma_risc;
        int rc;
 
-       buf->vb.size = fh->lines * 2 * 2048;
+       buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
        if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
                return -EINVAL;
 
+       tvnorm = fh->vbi_fmt.tvnorm;
+
+       /* There's no VBI_VDELAY register, RISC must skip the lines
+          we don't want. With default parameters we skip zero lines
+          as earlier driver versions did. The driver permits video
+          standard changes while capturing, so we use vbi_fmt.tvnorm
+          instead of btv->tvnorm to skip zero lines after video
+          standard changes as well. */
+
+       skip_lines0 = 0;
+       skip_lines1 = 0;
+
+       if (fh->vbi_fmt.fmt.count[0] > 0)
+               skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0]
+                                     - tvnorm->vbistart[0]));
+       if (fh->vbi_fmt.fmt.count[1] > 0)
+               skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1]
+                                     - tvnorm->vbistart[1]));
+
+       redo_dma_risc = 0;
+
+       if (buf->vbi_skip[0] != skip_lines0 ||
+           buf->vbi_skip[1] != skip_lines1 ||
+           buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] ||
+           buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) {
+               buf->vbi_skip[0] = skip_lines0;
+               buf->vbi_skip[1] = skip_lines1;
+               buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0];
+               buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1];
+               redo_dma_risc = 1;
+       }
+
        if (STATE_NEEDS_INIT == buf->vb.state) {
+               redo_dma_risc = 1;
                if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL)))
                        goto fail;
-               if (0 != (rc = vbi_buffer_risc(btv,buf,fh->lines)))
-                       goto fail;
        }
+
+       if (redo_dma_risc) {
+               unsigned int bpl, padding, offset;
+
+               bpl = 2044; /* max. vbipack */
+               padding = VBI_BPL - bpl;
+
+               if (fh->vbi_fmt.fmt.count[0] > 0) {
+                       rc = bttv_risc_packed(btv, &buf->top,
+                                             buf->vb.dma.sglist,
+                                             /* offset */ 0, bpl,
+                                             padding, skip_lines0,
+                                             fh->vbi_fmt.fmt.count[0]);
+                       if (0 != rc)
+                               goto fail;
+               }
+
+               if (fh->vbi_fmt.fmt.count[1] > 0) {
+                       offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL;
+
+                       rc = bttv_risc_packed(btv, &buf->bottom,
+                                             buf->vb.dma.sglist,
+                                             offset, bpl,
+                                             padding, skip_lines1,
+                                             fh->vbi_fmt.fmt.count[1]);
+                       if (0 != rc)
+                               goto fail;
+               }
+       }
+
+       /* VBI capturing ends at VDELAY, start of video capturing,
+          no matter where the RISC program ends. VDELAY minimum is 2,
+          bounds.top is the corresponding first field line number
+          times two. VDELAY counts half field lines. */
+       min_vdelay = MIN_VDELAY;
+       if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top)
+               min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top;
+
+       /* For bttv_buffer_activate_vbi(). */
+       buf->geo.vdelay = min_vdelay;
+
        buf->vb.state = STATE_PREPARED;
        buf->vb.field = field;
        dprintk("buf prepare %p: top=%p bottom=%p field=%s\n",
@@ -152,69 +236,215 @@ struct videobuf_queue_ops bttv_vbi_qops = {
 
 /* ----------------------------------------------------------------------- */
 
-void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines)
+static int
+try_fmt                        (struct v4l2_vbi_format *       f,
+                        const struct bttv_tvnorm *     tvnorm,
+                        __s32                          crop_start)
 {
-       int vdelay;
-
-       if (lines < 1)
-               lines = 1;
-       if (lines > VBI_MAXLINES)
-               lines = VBI_MAXLINES;
-       fh->lines = lines;
-
-       vdelay = btread(BT848_E_VDELAY_LO);
-       if (vdelay < lines*2) {
-               vdelay = lines*2;
-               btwrite(vdelay,BT848_E_VDELAY_LO);
-               btwrite(vdelay,BT848_O_VDELAY_LO);
+       __s32 min_start, max_start, max_end, f2_offset;
+       unsigned int i;
+
+       /* For compatibility with earlier driver versions we must pretend
+          the VBI and video capture window may overlap. In reality RISC
+          magic aborts VBI capturing at the first line of video capturing,
+          leaving the rest of the buffer unchanged, usually all zero.
+          VBI capturing must always start before video capturing. >> 1
+          because cropping counts field lines times two. */
+       min_start = tvnorm->vbistart[0];
+       max_start = (crop_start >> 1) - 1;
+       max_end = (tvnorm->cropcap.bounds.top
+                  + tvnorm->cropcap.bounds.height) >> 1;
+
+       if (min_start > max_start)
+               return -EBUSY;
+
+       BUG_ON(max_start >= max_end);
+
+       f->sampling_rate    = tvnorm->Fsc;
+       f->samples_per_line = VBI_BPL;
+       f->sample_format    = V4L2_PIX_FMT_GREY;
+       f->offset           = VBI_OFFSET;
+
+       f2_offset = tvnorm->vbistart[1] - tvnorm->vbistart[0];
+
+       for (i = 0; i < 2; ++i) {
+               if (0 == f->count[i]) {
+                       /* No data from this field. We leave f->start[i]
+                          alone because VIDIOCSVBIFMT is w/o and EINVALs
+                          when a driver does not support exactly the
+                          requested parameters. */
+               } else {
+                       s64 start, count;
+
+                       start = clamp(f->start[i], min_start, max_start);
+                       /* s64 to prevent overflow. */
+                       count = (s64) f->start[i] + f->count[i] - start;
+                       f->start[i] = start;
+                       f->count[i] = clamp(count, (s64) 1,
+                                           max_end - start);
+               }
+
+               min_start += f2_offset;
+               max_start += f2_offset;
+               max_end += f2_offset;
        }
+
+       if (0 == (f->count[0] | f->count[1])) {
+               /* As in earlier driver versions. */
+               f->start[0] = tvnorm->vbistart[0];
+               f->start[1] = tvnorm->vbistart[1];
+               f->count[0] = 1;
+               f->count[1] = 1;
+       }
+
+       f->flags = 0;
+
+       f->reserved[0] = 0;
+       f->reserved[1] = 0;
+
+       return 0;
 }
 
-void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+int
+bttv_vbi_try_fmt       (struct bttv_fh *               fh,
+                        struct v4l2_vbi_format *       f)
 {
+       struct bttv *btv = fh->btv;
        const struct bttv_tvnorm *tvnorm;
-       s64 count0,count1,count;
+       __s32 crop_start;
 
-       tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
-       f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
-       f->fmt.vbi.sampling_rate    = tvnorm->Fsc;
-       f->fmt.vbi.samples_per_line = 2048;
-       f->fmt.vbi.sample_format    = V4L2_PIX_FMT_GREY;
-       f->fmt.vbi.offset           = VBI_OFFSET;
-       f->fmt.vbi.flags            = 0;
-
-       /* s64 to prevent overflow. */
-       count0 = (s64) f->fmt.vbi.start[0] + f->fmt.vbi.count[0]
-               - tvnorm->vbistart[0];
-       count1 = (s64) f->fmt.vbi.start[1] + f->fmt.vbi.count[1]
-               - tvnorm->vbistart[1];
-       count  = clamp (max (count0, count1), (s64) 1, (s64) VBI_MAXLINES);
-
-       f->fmt.vbi.start[0] = tvnorm->vbistart[0];
-       f->fmt.vbi.start[1] = tvnorm->vbistart[1];
-       f->fmt.vbi.count[0] = count;
-       f->fmt.vbi.count[1] = count;
-
-       f->fmt.vbi.reserved[0] = 0;
-       f->fmt.vbi.reserved[1] = 0;
+       mutex_lock(&btv->lock);
+
+       tvnorm = &bttv_tvnorms[btv->tvnorm];
+       crop_start = btv->crop_start;
+
+       mutex_unlock(&btv->lock);
+
+       return try_fmt(f, tvnorm, crop_start);
 }
 
-void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+int
+bttv_vbi_set_fmt       (struct bttv_fh *               fh,
+                        struct v4l2_vbi_format *       f)
 {
+       struct bttv *btv = fh->btv;
        const struct bttv_tvnorm *tvnorm;
+       __s32 start1, end;
+       int rc;
+
+       mutex_lock(&btv->lock);
+
+       rc = -EBUSY;
+       if (fh->resources & RESOURCE_VBI)
+               goto fail;
+
+       tvnorm = &bttv_tvnorms[btv->tvnorm];
+
+       rc = try_fmt(f, tvnorm, btv->crop_start);
+       if (0 != rc)
+               goto fail;
+
+       start1 = f->start[1] - tvnorm->vbistart[1] + tvnorm->vbistart[0];
+
+       /* First possible line of video capturing. Should be
+          max(f->start[0] + f->count[0], start1 + f->count[1]) * 2
+          when capturing both fields. But for compatibility we must
+          pretend the VBI and video capture window may overlap,
+          so end = start + 1, the lowest possible value, times two
+          because vbi_fmt.end counts field lines times two. */
+       end = max(f->start[0], start1) * 2 + 2;
+
+       mutex_lock(&fh->vbi.lock);
+
+       fh->vbi_fmt.fmt    = *f;
+       fh->vbi_fmt.tvnorm = tvnorm;
+       fh->vbi_fmt.end    = end;
+
+       mutex_unlock(&fh->vbi.lock);
+
+       rc = 0;
+
+ fail:
+       mutex_unlock(&btv->lock);
+
+       return rc;
+}
+
+void
+bttv_vbi_get_fmt       (struct bttv_fh *               fh,
+                        struct v4l2_vbi_format *       f)
+{
+       const struct bttv_tvnorm *tvnorm;
+
+       *f = fh->vbi_fmt.fmt;
 
        tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
-       memset(f,0,sizeof(*f));
-       f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
-       f->fmt.vbi.sampling_rate    = tvnorm->Fsc;
-       f->fmt.vbi.samples_per_line = 2048;
-       f->fmt.vbi.sample_format    = V4L2_PIX_FMT_GREY;
-       f->fmt.vbi.offset           = VBI_OFFSET;
-       f->fmt.vbi.start[0]         = tvnorm->vbistart[0];
-       f->fmt.vbi.start[1]         = tvnorm->vbistart[1];
-       f->fmt.vbi.count[0]         = fh->lines;
-       f->fmt.vbi.count[1]         = fh->lines;
-       f->fmt.vbi.flags            = 0;
+
+       if (tvnorm != fh->vbi_fmt.tvnorm) {
+               __s32 max_end;
+               unsigned int i;
+
+               /* As in vbi_buffer_prepare() this imitates the
+                  behaviour of earlier driver versions after video
+                  standard changes, with default parameters anyway. */
+
+               max_end = (tvnorm->cropcap.bounds.top
+                          + tvnorm->cropcap.bounds.height) >> 1;
+
+               f->sampling_rate = tvnorm->Fsc;
+
+               for (i = 0; i < 2; ++i) {
+                       __s32 new_start;
+
+                       new_start = f->start[i]
+                               + tvnorm->vbistart[i]
+                               - fh->vbi_fmt.tvnorm->vbistart[i];
+
+                       f->start[i] = min(new_start, max_end - 1);
+                       f->count[i] = min((__s32) f->count[i],
+                                         max_end - f->start[i]);
+
+                       max_end += tvnorm->vbistart[1]
+                               - tvnorm->vbistart[0];
+               }
+       }
+}
+
+void
+bttv_vbi_fmt_reset     (struct bttv_vbi_fmt *          f,
+                        int                            norm)
+{
+       const struct bttv_tvnorm *tvnorm;
+       unsigned int real_samples_per_line;
+       unsigned int real_count;
+
+       tvnorm = &bttv_tvnorms[norm];
+
+       f->fmt.sampling_rate    = tvnorm->Fsc;
+       f->fmt.samples_per_line = VBI_BPL;
+       f->fmt.sample_format    = V4L2_PIX_FMT_GREY;
+       f->fmt.offset           = VBI_OFFSET;
+       f->fmt.start[0]         = tvnorm->vbistart[0];
+       f->fmt.start[1]         = tvnorm->vbistart[1];
+       f->fmt.count[0]         = VBI_DEFLINES;
+       f->fmt.count[1]         = VBI_DEFLINES;
+       f->fmt.flags            = 0;
+       f->fmt.reserved[0]      = 0;
+       f->fmt.reserved[1]      = 0;
+
+       /* For compatibility the buffer size must be 2 * VBI_DEFLINES *
+          VBI_BPL regardless of the current video standard. */
+       real_samples_per_line   = 1024 + tvnorm->vbipack * 4;
+       real_count              = ((tvnorm->cropcap.defrect.top >> 1)
+                                  - tvnorm->vbistart[0]);
+
+       BUG_ON(real_samples_per_line > VBI_BPL);
+       BUG_ON(real_count > VBI_DEFLINES);
+
+       f->tvnorm               = tvnorm;
+
+       /* See bttv_vbi_fmt_set(). */
+       f->end                  = tvnorm->vbistart[0] * 2 + 2;
 }
 
 /* ----------------------------------------------------------------------- */
index 3802caf..ad79b8d 100644 (file)
@@ -26,7 +26,7 @@
 #define _BTTVP_H_
 
 #include <linux/version.h>
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,16)
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,17)
 
 #include <linux/types.h>
 #include <linux/wait.h>
 #define RISC_SLOT_LOOP        14
 
 #define RESOURCE_OVERLAY       1
-#define RESOURCE_VIDEO         2
+#define RESOURCE_VIDEO_STREAM  2
 #define RESOURCE_VBI           4
+#define RESOURCE_VIDEO_READ    8
 
 #define RAW_LINES            640
 #define RAW_BPL             1024
 
 #define UNSET (-1U)
 
+/* Min. value in VDELAY register. */
+#define MIN_VDELAY 2
+/* Even to get Cb first, odd for Cr. */
+#define MAX_HDELAY (0x3FF & -2)
+/* Limits scaled width, which must be a multiple of 4. */
+#define MAX_HACTIVE (0x3FF & -4)
+
 #define clamp(x, low, high) min (max (low, x), high)
 
 /* ---------------------------------------------------------- */
@@ -92,8 +100,13 @@ struct bttv_tvnorm {
        u16   vtotal;
        int   sram;
        /* ITU-R frame line number of the first VBI line we can
-          capture, of the first and second field. */
+          capture, of the first and second field. The last possible line
+          is determined by cropcap.bounds. */
        u16   vbistart[2];
+       /* Horizontally this counts fCLKx1 samples following the leading
+          edge of the horizontal sync pulse, vertically ITU-R frame line
+          numbers of the first field times two (2, 4, 6, ... 524 or 624). */
+       struct v4l2_cropcap cropcap;
 };
 extern const struct bttv_tvnorm bttv_tvnorms[];
 
@@ -128,6 +141,9 @@ struct bttv_buffer {
        struct bttv_geometry       geo;
        struct btcx_riscmem        top;
        struct btcx_riscmem        bottom;
+       struct v4l2_rect           crop;
+       unsigned int               vbi_skip[2];
+       unsigned int               vbi_count[2];
 };
 
 struct bttv_buffer_set {
@@ -146,6 +162,34 @@ struct bttv_overlay {
        int                    setup_ok;
 };
 
+struct bttv_vbi_fmt {
+       struct v4l2_vbi_format fmt;
+
+       /* fmt.start[] and count[] refer to this video standard. */
+       const struct bttv_tvnorm *tvnorm;
+
+       /* Earliest possible start of video capturing with this
+          v4l2_vbi_format, in struct bttv_crop.rect units. */
+       __s32                  end;
+};
+
+/* bttv-vbi.c */
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm);
+
+struct bttv_crop {
+       /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */
+       struct v4l2_rect       rect;
+
+       /* Scaled image size limits with this crop rect. Divide
+          max_height, but not min_height, by two when capturing
+          single fields. See also bttv_crop_reset() and
+          bttv_crop_adjust() in bttv-driver.c. */
+       __s32                  min_scaled_width;
+       __s32                  min_scaled_height;
+       __s32                  max_scaled_width;
+       __s32                  max_scaled_height;
+};
+
 struct bttv_fh {
        struct bttv              *btv;
        int resources;
@@ -160,13 +204,19 @@ struct bttv_fh {
        int                      width;
        int                      height;
 
-       /* current settings */
+       /* video overlay */
        const struct bttv_format *ovfmt;
        struct bttv_overlay      ov;
 
-       /* video overlay */
+       /* Application called VIDIOC_S_CROP. */
+       int                      do_crop;
+
+       /* vbi capture */
        struct videobuf_queue    vbi;
-       int                      lines;
+       /* Current VBI capture window as seen through this fh (cannot
+          be global for compatibility with earlier drivers). Protected
+          by struct bttv.lock and struct bttv_fh.vbi.lock. */
+       struct bttv_vbi_fmt      vbi_fmt;
 };
 
 /* ---------------------------------------------------------- */
@@ -176,7 +226,8 @@ struct bttv_fh {
 int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
                     struct scatterlist *sglist,
                     unsigned int offset, unsigned int bpl,
-                    unsigned int pitch, unsigned int lines);
+                    unsigned int pitch, unsigned int skip_lines,
+                    unsigned int store_lines);
 
 /* control dma register + risc main loop */
 void bttv_set_dma(struct bttv *btv, int override);
@@ -202,9 +253,9 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
 /* ---------------------------------------------------------- */
 /* bttv-vbi.c                                                 */
 
-void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f);
-void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f);
-void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines);
+int bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
+void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
+int bttv_vbi_set_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
 
 extern struct videobuf_queue_ops bttv_vbi_qops;
 
@@ -233,7 +284,6 @@ extern int fini_bttv_i2c(struct bttv *btv);
 #define d2printk if (bttv_debug >= 2) printk
 
 #define BTTV_MAX_FBUF   0x208000
-#define VBIBUF_SIZE     (2048*VBI_MAXLINES*2)
 #define BTTV_TIMEOUT    (HZ/2) /* 0.5 seconds */
 #define BTTV_FREE_IDLE  (HZ)   /* one second */
 
@@ -314,7 +364,6 @@ struct bttv {
        spinlock_t s_lock;
        struct mutex lock;
        int resources;
-       struct mutex reslock;
 #ifdef VIDIOC_G_PRIORITY
        struct v4l2_prio_state prio;
 #endif
@@ -384,6 +433,21 @@ struct bttv {
 
        unsigned int users;
        struct bttv_fh init;
+
+       /* Default (0) and current (1) video capturing and overlay
+          cropping parameters in bttv_tvnorm.cropcap units. Protected
+          by bttv.lock. */
+       struct bttv_crop crop[2];
+
+       /* Earliest possible start of video capturing in
+          bttv_tvnorm.cropcap line units. Set by check_alloc_btres()
+          and free_btres(). Protected by bttv.lock. */
+       __s32                   vbi_end;
+
+       /* Latest possible end of VBI capturing (= crop[x].rect.top when
+          VIDEO_RESOURCES are locked). Set by check_alloc_btres()
+          and free_btres(). Protected by bttv.lock. */
+       __s32                   crop_start;
 };
 
 /* our devices */