]> err.no Git - linux-2.6/blobdiff - drivers/media/video/bt8xx/bttv-vbi.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6] / drivers / media / video / bt8xx / bttv-vbi.c
index 63676e7bd635f6b403a69be86e0e24d602c56337..1f0cc79e2a33e8e75c969f7e003d62d7abd0e793 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
 */
 
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/kdev_t.h>
 #include <asm/io.h>
    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;
@@ -59,21 +67,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)
 {
@@ -82,8 +81,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;
 }
 
@@ -94,19 +101,95 @@ 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;
 
-       if (STATE_NEEDS_INIT == buf->vb.state) {
+       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 (VIDEOBUF_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;
        }
-       buf->vb.state = STATE_PREPARED;
+
+       if (redo_dma_risc) {
+               unsigned int bpl, padding, offset;
+               struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+               bpl = 2044; /* max. vbipack */
+               padding = VBI_BPL - bpl;
+
+               if (fh->vbi_fmt.fmt.count[0] > 0) {
+                       rc = bttv_risc_packed(btv, &buf->top,
+                                             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,
+                                             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 = VIDEOBUF_PREPARED;
        buf->vb.field = field;
        dprintk("buf prepare %p: top=%p bottom=%p field=%s\n",
                vb, &buf->top, &buf->bottom,
@@ -126,7 +209,7 @@ vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
        struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
 
        dprintk("queue %p\n",vb);
-       buf->vb.state = STATE_QUEUED;
+       buf->vb.state = VIDEOBUF_QUEUED;
        list_add_tail(&buf->vb.queue,&btv->vcapture);
        if (NULL == btv->cvbi) {
                fh->btv->loop_irq |= 4;
@@ -141,7 +224,7 @@ static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer
        struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
 
        dprintk("free %p\n",vb);
-       bttv_dma_free(&fh->cap,fh->btv,buf);
+       bttv_dma_free(q,fh->btv,buf);
 }
 
 struct videobuf_queue_ops bttv_vbi_qops = {
@@ -153,69 +236,213 @@ 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_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
 {
+       struct bttv_fh *fh = 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(&frt->fmt.vbi, tvnorm, crop_start);
+}
+
+
+int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
+{
+       struct bttv_fh *fh = 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(&frt->fmt.vbi, tvnorm, btv->crop_start);
+       if (0 != rc)
+               goto fail;
+
+       start1 = frt->fmt.vbi.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(frt->fmt.vbi.start[0], start1) * 2 + 2;
+
+       mutex_lock(&fh->vbi.lock);
+
+       fh->vbi_fmt.fmt    = frt->fmt.vbi;
+       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_format *f)
+
+int bttv_g_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
 {
+       struct bttv_fh *fh = f;
        const struct bttv_tvnorm *tvnorm;
 
+       frt->fmt.vbi = 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;
+
+               frt->fmt.vbi.sampling_rate = tvnorm->Fsc;
+
+               for (i = 0; i < 2; ++i) {
+                       __s32 new_start;
+
+                       new_start = frt->fmt.vbi.start[i]
+                               + tvnorm->vbistart[i]
+                               - fh->vbi_fmt.tvnorm->vbistart[i];
+
+                       frt->fmt.vbi.start[i] = min(new_start, max_end - 1);
+                       frt->fmt.vbi.count[i] =
+                               min((__s32) frt->fmt.vbi.count[i],
+                                         max_end - frt->fmt.vbi.start[i]);
+
+                       max_end += tvnorm->vbistart[1]
+                               - tvnorm->vbistart[0];
+               }
+       }
+       return 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;
 }
 
 /* ----------------------------------------------------------------------- */