/*
bttv - Bt848 frame grabber driver
-
- Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
- & Marcus Metzler (mocm@thp.uni-koeln.de)
- (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
-
+
+ Copyright (C) 1996,97,98 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ & Marcus Metzler <mocm@thp.uni-koeln.de>
+ (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+
+ some v4l2 code lines are taken from Justin's bttv2 driver which is
+ (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/poll.h>
-#include <linux/pci.h>
-#include <linux/signal.h>
-#include <asm/io.h>
-#include <linux/ioport.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/wrapper.h>
#include <linux/interrupt.h>
-#include <linux/kmod.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
+#include <linux/kdev_t.h>
+
+#include <asm/io.h>
#include "bttvp.h"
#include "tuner.h"
-#define DEBUG(x) /* Debug driver */
-#define MIN(a,b) (((a)>(b))?(b):(a))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
-static void bt848_set_risc_jmps(struct bttv *btv, int state);
+/* 2.4 / 2.5 driver compatibility stuff */
int bttv_num; /* number of Bt848s in use */
struct bttv bttvs[BTTV_MAX];
-/* configuration variables */
+unsigned int bttv_debug = 0;
+unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio = 0;
+
+/* config variables */
#if defined(__sparc__) || defined(__powerpc__) || defined(__hppa__)
static unsigned int bigendian=1;
#else
static unsigned int bigendian=0;
#endif
-static unsigned int radio[BTTV_MAX];
-static unsigned int fieldnr = 0;
+static unsigned int radio[4];
static unsigned int irq_debug = 0;
-static unsigned int gbuffers = 2;
-static unsigned int gbufsize = BTTV_MAX_FBUF;
-static unsigned int combfilter = 0;
-static unsigned int lumafilter = 0;
+static unsigned int gbuffers = 8;
+static unsigned int gbufsize = 0x208000;
+static unsigned int fdsr = 0;
+static unsigned int latency = -1;
+
static int video_nr = -1;
static int radio_nr = -1;
static int vbi_nr = -1;
-unsigned int bttv_debug = 0;
-unsigned int bttv_verbose = 1;
-unsigned int bttv_gpio = 0;
-/* insmod options */
+/* options */
+static unsigned int combfilter = 0;
+static unsigned int lumafilter = 0;
+static unsigned int automute = 1;
+static unsigned int chroma_agc = 0;
+static unsigned int adc_crush = 1;
+
+/* API features (turn on/off stuff for testing) */
+static unsigned int sloppy = 0;
+static unsigned int mmap = 1;
+#ifdef HAVE_V4L2
+static unsigned int v4l2 = 1;
+#endif
+
+
+/* insmod args */
MODULE_PARM(radio,"1-4i");
MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
MODULE_PARM(bigendian,"i");
MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
-MODULE_PARM(fieldnr,"i");
-MODULE_PARM_DESC(fieldnr,"count fields, default is 0 (no)");
MODULE_PARM(bttv_verbose,"i");
MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
MODULE_PARM(bttv_gpio,"i");
MODULE_PARM(irq_debug,"i");
MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
MODULE_PARM(gbuffers,"i");
-MODULE_PARM_DESC(gbuffers,"number of capture buffers, default is 2 (64 max)");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
MODULE_PARM(gbufsize,"i");
MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
-MODULE_PARM(combfilter,"i");
-MODULE_PARM(lumafilter,"i");
+MODULE_PARM(fdsr,"i");
+MODULE_PARM(latency,"i");
+MODULE_PARM_DESC(latency,"pci latency timer");
MODULE_PARM(video_nr,"i");
MODULE_PARM(radio_nr,"i");
MODULE_PARM(vbi_nr,"i");
-MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards");
+MODULE_PARM(combfilter,"i");
+MODULE_PARM(lumafilter,"i");
+MODULE_PARM(automute,"i");
+MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+MODULE_PARM(chroma_agc,"i");
+MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+MODULE_PARM(adc_crush,"i");
+MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+
+MODULE_PARM(sloppy,"i");
+MODULE_PARM(mmap,"i");
+#ifdef HAVE_V4L2
+MODULE_PARM(v4l2,"i");
+#endif
+
+MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
MODULE_LICENSE("GPL");
__setup("bttv.radio=", p_radio);
#endif
-#define I2C_TIMING (0x7<<4)
-#define I2C_DELAY 10
-
-#define I2C_SET(CTRL,DATA) \
- { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); }
-#define I2C_GET() (btread(BT848_I2C)&1)
+#ifndef HAVE_V4L2
+/* some dummy defines to avoid cluttering up the source code with
+ a huge number of ifdef's for V4L2 */
+# define V4L2_STD_PAL -1
+# define V4L2_STD_NTSC -1
+# define V4L2_STD_SECAM -1
+# define V4L2_STD_PAL_60 -1
+# define V4L2_STD_PAL_M -1
+# define V4L2_STD_PAL_N -1
+# define V4L2_STD_NTSC_N -1
+# define V4L2_PIX_FMT_GREY -1
+# define V4L2_PIX_FMT_HI240 -1
+# define V4L2_PIX_FMT_RGB555 -1
+# define V4L2_PIX_FMT_RGB555X -1
+# define V4L2_PIX_FMT_RGB565 -1
+# define V4L2_PIX_FMT_RGB565X -1
+# define V4L2_PIX_FMT_BGR24 -1
+# define V4L2_PIX_FMT_BGR32 -1
+# define V4L2_PIX_FMT_RGB32 -1
+# define V4L2_PIX_FMT_YUYV -1
+# define V4L2_PIX_FMT_UYVY -1
+# define V4L2_PIX_FMT_YUV422P -1
+# define V4L2_PIX_FMT_YUV420 -1
+# define V4L2_PIX_FMT_YVU420 -1
+# define V4L2_PIX_FMT_YUV411P -1
+# define V4L2_PIX_FMT_YUV410 -1
+# define V4L2_PIX_FMT_YVU410 -1
+# define V4L2_BUF_TYPE_CAPTURE -1
+# define V4L2_BUF_TYPE_VBI -1
+# define BTTV_APIS "[v4l]"
+#else
+# define BTTV_APIS "[v4l/v4l2]"
+#endif
-#define BURSTOFFSET 76
-#define BTTV_ERRORS 5
+/* ----------------------------------------------------------------------- */
+/* static data */
+const struct bttv_tvnorm bttv_tvnorms[] = {
+ /* PAL-BDGHI */
+ { V4L2_STD_PAL, 35468950,
+ 922, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ 1135, 186, 922, 0x20, 255},
+ /* NTSC */
+ { V4L2_STD_NTSC, 28636363,
+ 754, 480, 910, 0x70, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+ 910, 135, 754, 0x1a, 144},
+ /* SECAM */
+ { V4L2_STD_SECAM, 35468950,
+ 922, 576, 1135, 0x7f, 0xa0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+ 1135, 186, 922, 0x20, 255},
-/*******************************/
-/* Memory management functions */
-/*******************************/
+ /* these ones are bttv specific (for v4l1) */
+ /* PAL-NC */
+ { V4L2_STD_PAL_60, 28636363,
+ 640, 576, 910, 0x7f, 0x72, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+ 780, 130, 734, 0x1a, 144},
+ /* PAL-M */
+ { V4L2_STD_PAL_M, 28636363,
+ 640, 480, 910, 0x70, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+ 780, 135, 754, 0x1a, 144},
+ /* PAL-N */
+ { V4L2_STD_PAL_N, 35468950,
+ 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT0),
+ 944, 186, 922, 0x20, 144},
+ /* NTSC-Japan */
+ { V4L2_STD_NTSC_N, 28636363,
+ 640, 480, 910, 0x70, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+ 780, 135, 754, 0x16, 144},
+};
+const int BTTV_TVNORMS = (sizeof(bttv_tvnorms)/sizeof(struct bttv_tvnorm));
-#define MDEBUG(x) do { } while(0) /* Debug memory management */
+/* ----------------------------------------------------------------------- */
+/* bttv format list
+ packed pixel formats must come first */
+const struct bttv_format bttv_formats[] = {
+ {
+ name: "8 bpp, gray",
+ palette: VIDEO_PALETTE_GREY,
+ fourcc: V4L2_PIX_FMT_GREY,
+ btformat: BT848_COLOR_FMT_Y8,
+ depth: 8,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "8 bpp, dithered color",
+ palette: VIDEO_PALETTE_HI240,
+ fourcc: V4L2_PIX_FMT_HI240,
+ btformat: BT848_COLOR_FMT_RGB8,
+ depth: 8,
+ flags: FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+ },{
+ name: "15 bpp RGB, le",
+ palette: VIDEO_PALETTE_RGB555,
+ fourcc: V4L2_PIX_FMT_RGB555,
+ btformat: BT848_COLOR_FMT_RGB15,
+ depth: 16,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "15 bpp RGB, be",
+ palette: -1,
+ fourcc: V4L2_PIX_FMT_RGB555X,
+ btformat: BT848_COLOR_FMT_RGB15,
+ btswap: 0x03, /* byteswap */
+ depth: 16,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "16 bpp RGB, le",
+ palette: VIDEO_PALETTE_RGB565,
+ fourcc: V4L2_PIX_FMT_RGB565,
+ btformat: BT848_COLOR_FMT_RGB16,
+ depth: 16,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "16 bpp RGB, be",
+ palette: -1,
+ fourcc: V4L2_PIX_FMT_RGB565X,
+ btformat: BT848_COLOR_FMT_RGB16,
+ btswap: 0x03, /* byteswap */
+ depth: 16,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "24 bpp RGB, le",
+ palette: VIDEO_PALETTE_RGB24,
+ fourcc: V4L2_PIX_FMT_BGR24,
+ btformat: BT848_COLOR_FMT_RGB24,
+ depth: 24,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "32 bpp RGB, le",
+ palette: VIDEO_PALETTE_RGB32,
+ fourcc: V4L2_PIX_FMT_BGR32,
+ btformat: BT848_COLOR_FMT_RGB32,
+ depth: 32,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "32 bpp RGB, be",
+ palette: -1,
+ fourcc: V4L2_PIX_FMT_RGB32,
+ btformat: BT848_COLOR_FMT_RGB32,
+ btswap: 0x0f, /* byte+word swap */
+ depth: 32,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "4:2:2, packed, YUYV",
+ palette: VIDEO_PALETTE_YUV422,
+ fourcc: V4L2_PIX_FMT_YUYV,
+ btformat: BT848_COLOR_FMT_YUY2,
+ depth: 16,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "4:2:2, packed, YUYV",
+ palette: VIDEO_PALETTE_YUYV,
+ fourcc: V4L2_PIX_FMT_YUYV,
+ btformat: BT848_COLOR_FMT_YUY2,
+ depth: 16,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "4:2:2, packed, UYVY",
+ palette: VIDEO_PALETTE_UYVY,
+ fourcc: V4L2_PIX_FMT_UYVY,
+ btformat: BT848_COLOR_FMT_YUY2,
+ btswap: 0x03, /* byteswap */
+ depth: 16,
+ flags: FORMAT_FLAGS_PACKED,
+ },{
+ name: "4:2:2, planar, Y-Cb-Cr",
+ palette: VIDEO_PALETTE_YUV422P,
+ fourcc: V4L2_PIX_FMT_YUV422P,
+ btformat: BT848_COLOR_FMT_YCrCb422,
+ depth: 16,
+ flags: FORMAT_FLAGS_PLANAR,
+ hshift: 1,
+ vshift: 0,
+ },{
+ name: "4:2:0, planar, Y-Cb-Cr",
+ palette: VIDEO_PALETTE_YUV420P,
+ fourcc: V4L2_PIX_FMT_YUV420,
+ btformat: BT848_COLOR_FMT_YCrCb422,
+ depth: 12,
+ flags: FORMAT_FLAGS_PLANAR,
+ hshift: 1,
+ vshift: 1,
+ },{
+ name: "4:2:0, planar, Y-Cr-Cb",
+ palette: -1,
+ fourcc: V4L2_PIX_FMT_YVU420,
+ btformat: BT848_COLOR_FMT_YCrCb422,
+ depth: 12,
+ flags: FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ hshift: 1,
+ vshift: 1,
+ },{
+ name: "4:1:1, planar, Y-Cb-Cr",
+ palette: VIDEO_PALETTE_YUV411P,
+ fourcc: V4L2_PIX_FMT_YUV411P,
+ btformat: BT848_COLOR_FMT_YCrCb411,
+ depth: 12,
+ flags: FORMAT_FLAGS_PLANAR,
+ hshift: 2,
+ vshift: 0,
+ },{
+ name: "4:1:0, planar, Y-Cb-Cr",
+ palette: VIDEO_PALETTE_YUV410P,
+ fourcc: V4L2_PIX_FMT_YUV410,
+ btformat: BT848_COLOR_FMT_YCrCb411,
+ depth: 9,
+ flags: FORMAT_FLAGS_PLANAR,
+ hshift: 2,
+ vshift: 2,
+ },{
+ name: "4:1:0, planar, Y-Cr-Cb",
+ palette: -1,
+ fourcc: V4L2_PIX_FMT_YVU410,
+ btformat: BT848_COLOR_FMT_YCrCb411,
+ depth: 9,
+ flags: FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ hshift: 2,
+ vshift: 2,
+ },{
+ name: "raw scanlines",
+ palette: VIDEO_PALETTE_RAW,
+ fourcc: -1,
+ btformat: BT848_COLOR_FMT_RAW,
+ depth: 8,
+ flags: FORMAT_FLAGS_RAW,
+ }
+};
+const int BTTV_FORMATS = (sizeof(bttv_formats)/sizeof(struct bttv_format));
-/* [DaveM] I've recoded most of this so that:
- * 1) It's easier to tell what is happening
- * 2) It's more portable, especially for translating things
- * out of vmalloc mapped areas in the kernel.
- * 3) Less unnecessary translations happen.
- *
- * The code used to assume that the kernel vmalloc mappings
- * existed in the page tables of every process, this is simply
- * not guarenteed. We now use pgd_offset_k which is the
- * defined way to get at the kernel page tables.
- */
+/* ----------------------------------------------------------------------- */
+#ifdef HAVE_V4L2
+#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 5)
+
+static const struct v4l2_queryctrl no_ctl = {
+ name: "42",
+ flags: V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl bttv_ctls[] = {
+ /* --- video --- */
+ {
+ id: V4L2_CID_BRIGHTNESS,
+ name: "Brightness",
+ minimum: 0,
+ maximum: 65535,
+ step: 256,
+ default_value: 32768,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_VIDEO,
+ group: "Video",
+ },{
+ id: V4L2_CID_CONTRAST,
+ name: "Contrast",
+ minimum: 0,
+ maximum: 65535,
+ step: 128,
+ default_value: 32768,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_VIDEO,
+ group: "Video",
+ },{
+ id: V4L2_CID_SATURATION,
+ name: "Saturation",
+ minimum: 0,
+ maximum: 65535,
+ step: 128,
+ default_value: 32768,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_VIDEO,
+ group: "Video",
+ },{
+ id: V4L2_CID_HUE,
+ name: "Hue",
+ minimum: 0,
+ maximum: 65535,
+ step: 256,
+ default_value: 32768,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_VIDEO,
+ group: "Video",
+ },
+ /* --- audio --- */
+ {
+ id: V4L2_CID_AUDIO_MUTE,
+ name: "Mute",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ category: V4L2_CTRL_CAT_AUDIO,
+ group: "Audio",
+ },{
+ id: V4L2_CID_AUDIO_VOLUME,
+ name: "Volume",
+ minimum: 0,
+ maximum: 65535,
+ step: 65535/100,
+ default_value: 65535,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_AUDIO,
+ group: "Audio",
+ },{
+ id: V4L2_CID_AUDIO_BALANCE,
+ name: "Balance",
+ minimum: 0,
+ maximum: 65535,
+ step: 65535/100,
+ default_value: 32768,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_AUDIO,
+ group: "Audio",
+ },{
+ id: V4L2_CID_AUDIO_BASS,
+ name: "Bass",
+ minimum: 0,
+ maximum: 65535,
+ step: 65535/100,
+ default_value: 32768,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_AUDIO,
+ group: "Audio",
+ },{
+ id: V4L2_CID_AUDIO_TREBLE,
+ name: "Treble",
+ minimum: 0,
+ maximum: 65535,
+ step: 65535/100,
+ default_value: 32768,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ category: V4L2_CTRL_CAT_AUDIO,
+ group: "Audio",
+ },
+ /* --- private --- */
+ {
+ id: V4L2_CID_PRIVATE_CHROMA_AGC,
+ name: "chroma agc",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ group: "Private",
+ },{
+ id: V4L2_CID_PRIVATE_COMBFILTER,
+ name: "combfilter",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ group: "Private",
+ },{
+ id: V4L2_CID_PRIVATE_AUTOMUTE,
+ name: "automute",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ group: "Private",
+ },{
+ id: V4L2_CID_PRIVATE_LUMAFILTER,
+ name: "luma decimation filter",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ group: "Private",
+ },{
+ id: V4L2_CID_PRIVATE_AGC_CRUSH,
+ name: "agc crush",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ group: "Private",
+ }
+};
+const int BTTV_CTLS = (sizeof(bttv_ctls)/sizeof(struct v4l2_queryctrl));
+#endif /* HAVE_V4L2 */
-/*
- * Take a vmalloc address, and turn it into
- * the aliased kernel virtual address..
- *
- * CAREFUL! Anybody who does this gets to sit
- * in their own cr*p when it comes to virtual
- * cache aliases. It's _your_ problem.
- *
- * Also, note how it only works within one page.
- * If you're doing page-crossing stuff, you're on
- * your own.
- *
- * THIS IS BROKEN CODE! You shouldn't do things
- * like this.
- */
-static inline void *vmalloc_to_virt(void *addr)
-{
- struct page *page = vmalloc_to_page(addr);
- return page_address(page) + (~PAGE_MASK & (unsigned long)addr);
-}
+/* ----------------------------------------------------------------------- */
+/* resource management */
-static inline unsigned long kvirt_to_bus(unsigned long addr)
+static
+int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit)
{
- unsigned long ret;
- ret = virt_to_bus(vmalloc_to_virt((void *)addr));
- MDEBUG(printk("kv2b(%lx-->%lx)", addr, ret));
- return ret;
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ down(&btv->reslock);
+ if (btv->resources & bit) {
+ /* no, someone else uses it */
+ up(&btv->reslock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ btv->resources |= bit;
+ up(&btv->reslock);
+ return 1;
}
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the area.
- */
-static inline unsigned long kvirt_to_pa(unsigned long addr)
+static
+int check_btres(struct bttv_fh *fh, int bit)
{
- unsigned long ret;
- ret = virt_to_phys(vmalloc_to_virt((void *)addr));
- MDEBUG(printk("kv2pa(%lx-->%lx)", addr, ret));
- return ret;
+ return (fh->resources & bit);
}
-static void * rvmalloc(unsigned long size)
+static
+int locked_btres(struct bttv *btv, int bit)
{
- void * mem;
- unsigned long adr;
-
- size=PAGE_ALIGN(size);
- mem=vmalloc_32(size);
- if (NULL == mem)
- printk(KERN_INFO "bttv: vmalloc_32(%lu) failed\n",size);
- else {
- /* Clear the ram out, no junk to the user */
- memset(mem, 0, size);
- adr=(unsigned long) mem;
- while (size > 0)
- {
- mem_map_reserve(vmalloc_to_page((void *)adr));
- adr+=PAGE_SIZE;
- size-=PAGE_SIZE;
- }
- }
- return mem;
+ return (btv->resources & bit);
}
-static void rvfree(void * mem, unsigned long size)
+static
+void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits)
{
- unsigned long adr;
-
- if (mem)
- {
- adr=(unsigned long) mem;
- while ((long) size > 0)
- {
- mem_map_unreserve(vmalloc_to_page((void *)adr));
- adr+=PAGE_SIZE;
- size-=PAGE_SIZE;
- }
- vfree(mem);
+#if 1 /* DEBUG */
+ if ((fh->resources & bits) != bits) {
+ /* trying to free ressources not allocated by us ... */
+ printk("bttv: BUG! (btres)\n");
}
+#endif
+ down(&btv->reslock);
+ fh->resources &= ~bits;
+ btv->resources &= ~bits;
+ up(&btv->reslock);
}
-
-
+/* ----------------------------------------------------------------------- */
/*
- * Create the giant waste of buffer space we need for now
- * until we get DMA to user space sorted out (probably 2.3.x)
+ * sanity check for video framebuffer address ranges (overlay).
+ * let's see if that address range actually belongs to some
+ * pci display adapter.
*
- * We only create this as and when someone uses mmap
+ * FIXME: stuff isn't portable. It's also a v4l API bug, pass a
+ * physical address in VIDIOCSFBUF isn't portable too ...
*/
-
-static int fbuffer_alloc(struct bttv *btv)
-{
- if(!btv->fbuffer)
- btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize);
- else
- printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n",
- btv->nr);
- if(!btv->fbuffer)
- return -ENOBUFS;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-void bttv_gpio_tracking(struct bttv *btv, char *comment)
-{
- unsigned int outbits, data;
- outbits = btread(BT848_GPIO_OUT_EN);
- data = btread(BT848_GPIO_DATA);
- printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
- btv->nr,outbits,data & outbits, data & ~outbits, comment);
-}
-static char *audio_modes[] = { "audio: tuner", "audio: radio", "audio: extern",
- "audio: intern", "audio: off" };
-
-static void audio(struct bttv *btv, int mode, int no_irq_context)
+static int
+find_videomem(unsigned long from, unsigned long to)
{
- btaor(bttv_tvcards[btv->type].gpiomask, ~bttv_tvcards[btv->type].gpiomask,
- BT848_GPIO_OUT_EN);
-
- switch (mode)
- {
- case AUDIO_MUTE:
- btv->audio|=AUDIO_MUTE;
- break;
- case AUDIO_UNMUTE:
- btv->audio&=~AUDIO_MUTE;
- mode=btv->audio;
- break;
- case AUDIO_OFF:
- mode=AUDIO_OFF;
- break;
- case AUDIO_ON:
- mode=btv->audio;
- break;
- default:
- btv->audio&=AUDIO_MUTE;
- btv->audio|=mode;
- break;
+#if PCI_DMA_BUS_IS_PHYS
+ struct pci_dev *dev = NULL;
+ int i,match,found;
+
+ found = 0;
+ dprintk(KERN_DEBUG "bttv: checking video framebuffer address"
+ " (%lx-%lx)\n",from,to);
+ pci_for_each_dev(dev) {
+ if (dev->class != PCI_CLASS_NOT_DEFINED_VGA &&
+ dev->class >> 16 != PCI_BASE_CLASS_DISPLAY)
+ continue;
+ dprintk(KERN_DEBUG
+ " pci display adapter %04x:%04x at %02x:%02x.%x\n",
+ dev->vendor,dev->device,dev->bus->number,
+ PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ if (!(dev->resource[i].flags & IORESOURCE_MEM))
+ continue;
+ if (dev->resource[i].flags & IORESOURCE_READONLY)
+ continue;
+ match = (from >= dev->resource[i].start) &&
+ (to-1 <= dev->resource[i].end);
+ if (match)
+ found = 1;
+ dprintk(KERN_DEBUG " memory at %08lx-%08lx%s\n",
+ dev->resource[i].start,
+ dev->resource[i].end,
+ match ? " (check passed)" : "");
+ }
}
- /* if audio mute or not in H-lock, turn audio off */
- if ((btv->audio&AUDIO_MUTE))
- mode=AUDIO_OFF;
- if ((mode == AUDIO_TUNER) && (btv->radio))
- mode = AUDIO_RADIO;
- btaor(bttv_tvcards[btv->type].audiomux[mode],
- ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,audio_modes[mode]);
- if (no_irq_context)
- bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode));
-}
-
-
-extern inline void bt848_dma(struct bttv *btv, uint state)
-{
- if (state)
- btor(3, BT848_GPIO_DMA_CTL);
- else
- btand(~3, BT848_GPIO_DMA_CTL);
+ return found;
+#else
+ /* Hmm, the physical address passed to us is probably bogous */
+ dprintk(KERN_DEBUG "bttv: no overlay for this arch, sorry\n");
+ return 0;
+#endif
}
-
-/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/
+/* ----------------------------------------------------------------------- */
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */
/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
PLL_X = Reference pre-divider (0=1, 1=2)
fout=(fout%fin)*256;
fl=fout/fin;
- /*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/
btwrite(fl, BT848_PLL_F_LO);
btwrite(fh, BT848_PLL_F_HI);
btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
static int set_pll(struct bttv *btv)
{
int i;
- unsigned long tv;
if (!btv->pll.pll_crystal)
return 0;
if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
/* no PLL needed */
- if (btv->pll.pll_current == 0) {
- /* printk ("bttv%d: PLL: is off\n",btv->nr); */
+ if (btv->pll.pll_current == 0)
return 0;
- }
- if (bttv_verbose)
- printk ("bttv%d: PLL: switching off\n",btv->nr);
+ vprintk("bttv%d: PLL: switching off\n",btv->nr);
btwrite(0x00,BT848_TGCTRL);
btwrite(0x00,BT848_PLL_XCI);
btv->pll.pll_current = 0;
return 0;
}
- if (btv->pll.pll_ofreq == btv->pll.pll_current) {
- /* printk("bttv%d: PLL: no change required\n",btv->nr); */
- return 1;
- }
-
- if (bttv_verbose)
- printk("bttv%d: PLL: %d => %d ... ",btv->nr,
- btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+ if (btv->pll.pll_ofreq == btv->pll.pll_current)
+ return 0;
+ vprintk("bttv%d: PLL: %d => %d ",btv->nr,
+ btv->pll.pll_ifreq, btv->pll.pll_ofreq);
set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
- /* Let other people run while the PLL stabilizes */
- tv=jiffies+HZ/10; /* .1 seconds */
- do
- {
- schedule();
- }
- while(time_before(jiffies,tv));
-
- for (i=0; i<100; i++)
- {
- if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK))
- btwrite(0,BT848_DSTATUS);
- else
- {
+ for (i=0; i<10; i++) {
+ /* Let other people run while the PLL stabilizes */
+ vprintk(".");
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+ btwrite(0,BT848_DSTATUS);
+ } else {
btwrite(0x08,BT848_TGCTRL);
btv->pll.pll_current = btv->pll.pll_ofreq;
- if (bttv_verbose)
- printk("ok\n");
- return 1;
+ vprintk(" ok\n");
+ return 0;
}
- mdelay(10);
}
- btv->pll.pll_current = 0;
- if (bttv_verbose)
- printk("oops\n");
+ btv->pll.pll_current = -1;
+ vprintk("failed\n");
return -1;
}
-static void bt848_muxsel(struct bttv *btv, unsigned int input)
+/* ----------------------------------------------------------------------- */
+
+static void bt848_bright(struct bttv *btv, int bright)
{
+ int value;
-#if 0 /* seems no card uses this ... */
- btaor(bttv_tvcards[btv->type].gpiomask2,~bttv_tvcards[btv->type].gpiomask2,
- BT848_GPIO_OUT_EN);
-#endif
+ btv->bright = bright;
+
+ /* We want -128 to 127 we get 0-65535 */
+ value = (bright >> 8) - 128;
+ btwrite(value & 0xff, BT848_BRIGHT);
+}
+
+static void bt848_hue(struct bttv *btv, int hue)
+{
+ int value;
+
+ btv->hue = hue;
+
+ /* -128 to 127 */
+ value = (hue >> 8) - 128;
+ btwrite(value & 0xff, BT848_HUE);
+}
+
+static void bt848_contrast(struct bttv *btv, int cont)
+{
+ int value,hibit;
+
+ btv->contrast = cont;
+
+ /* 0-511 */
+ value = (cont >> 7);
+ hibit = (value >> 6) & 4;
+ btwrite(value & 0xff, BT848_CONTRAST_LO);
+ btaor(hibit, ~4, BT848_E_CONTROL);
+ btaor(hibit, ~4, BT848_O_CONTROL);
+}
+
+static void bt848_sat(struct bttv *btv, int color)
+{
+ int val_u,val_v,hibits;
+
+ btv->saturation = color;
+
+ /* 0-511 for the color */
+ val_u = color >> 7;
+ val_v = ((color>>7)*180L)/254;
+ hibits = (val_u >> 7) & 2;
+ hibits |= (val_v >> 8) & 1;
+ btwrite(val_u & 0xff, BT848_SAT_U_LO);
+ btwrite(val_v & 0xff, BT848_SAT_V_LO);
+ btaor(hibits, ~3, BT848_E_CONTROL);
+ btaor(hibits, ~3, BT848_O_CONTROL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+video_mux(struct bttv *btv, unsigned int input)
+{
+ int mux,mask2;
+
+ if (input >= bttv_tvcards[btv->type].video_inputs)
+ return -EINVAL;
+
+ /* needed by RemoteVideo MX */
+ mask2 = bttv_tvcards[btv->type].gpiomask2;
+ if (mask2)
+ btaor(mask2,~mask2,BT848_GPIO_OUT_EN);
+#if 0
/* This seems to get rid of some synchronization problems */
btand(~(3<<5), BT848_IFORM);
- mdelay(10);
+ schedule_timeout(HZ/10);
+#endif
- input %= bttv_tvcards[btv->type].video_inputs;
- if (input==bttv_tvcards[btv->type].svhs)
- {
+ if (input==bttv_tvcards[btv->type].svhs) {
btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
- }
- else
- {
+ } else {
btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
}
- btaor((bttv_tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM);
- audio(btv, (input!=bttv_tvcards[btv->type].tuner) ?
- AUDIO_EXTERN : AUDIO_TUNER, 1);
+ mux = bttv_tvcards[btv->type].muxsel[input] & 3;
+ btaor(mux<<5, ~(3<<5), BT848_IFORM);
+ dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
+ btv->nr,input,mux);
-#if 0 /* seems no card uses this ... */
- btaor(bttv_tvcards[btv->type].muxsel[input]>>4,
- ~bttv_tvcards[btv->type].gpiomask2, BT848_GPIO_DATA);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"muxsel");
-#endif
+ /* card specific hook */
+ if(bttv_tvcards[btv->type].muxsel_hook)
+ bttv_tvcards[btv->type].muxsel_hook (btv, input);
+ return 0;
}
-
-struct tvnorm
-{
- u32 Fsc;
- u16 swidth, sheight; /* scaled standard width, height */
- u16 totalwidth;
- u8 adelay, bdelay, iform;
- u32 scaledtwidth;
- u16 hdelayx1, hactivex1;
- u16 vdelay;
- u8 vbipack;
+static char *audio_modes[] = {
+ "audio: tuner", "audio: radio", "audio: extern",
+ "audio: intern", "audio: off"
};
-static struct tvnorm tvnorms[] = {
- /* PAL-BDGHI */
- /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
- /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
- { 35468950,
- 924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
- 1135, 186, 924,
-#ifdef VIDEODAT_HACK
- VBI_MAXLINES*2,
-#else
- 0x20,
-#endif
- 255},
-
- /* NTSC */
- { 28636363,
- 768, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
- 910, 128, 910, 0x1a, 144},
-#if 0
- /* SECAM EAST */
- { 35468950,
- 768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
- 944, 186, 922, 0x20, 255},
-#else
- /* SECAM L */
- { 35468950,
- 924, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
- 1135, 186, 922, 0x20, 255},
-#endif
- /* PAL-NC */
- { 28636363,
- 640, 576, 910, 0x68, 0x5d, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
- 780, 130, 734, 0x1a, 144},
- /* PAL-M */
- { 28636363,
- 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
- 780, 135, 754, 0x1a, 144},
- /* PAL-N */
- { 35468950,
- 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
- 944, 186, 922, 0x20, 144},
- /* NTSC-Japan */
- { 28636363,
- 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
- 780, 135, 754, 0x16, 144},
-};
-#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
-#define VBI_SPL 2044
+static int
+audio_mux(struct bttv *btv, int mode)
+{
+ int val,mux;
+
+ btaor(bttv_tvcards[btv->type].gpiomask,
+ ~bttv_tvcards[btv->type].gpiomask,BT848_GPIO_OUT_EN);
-/* RISC command to write one VBI data line */
-#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL
+ switch (mode) {
+ case AUDIO_MUTE:
+ btv->audio |= AUDIO_MUTE;
+ break;
+ case AUDIO_UNMUTE:
+ btv->audio &= ~AUDIO_MUTE;
+ break;
+ case AUDIO_TUNER:
+ case AUDIO_RADIO:
+ case AUDIO_EXTERN:
+ case AUDIO_INTERN:
+ btv->audio &= AUDIO_MUTE;
+ btv->audio |= mode;
+ }
+ dprintk("bttv%d: audio mux: mode=%d audio=%d\n",
+ btv->nr,mode,btv->audio);
+
+ mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio;
+ val = bttv_tvcards[btv->type].audiomux[mux];
+ btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,audio_modes[mux]);
+ if (!in_interrupt())
+ bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mux));
+ return 0;
+}
-static void make_vbitab(struct bttv *btv)
+static void
+i2c_vidiocschan(struct bttv *btv)
{
- int i;
- unsigned int *po=(unsigned int *) btv->vbi_odd;
- unsigned int *pe=(unsigned int *) btv->vbi_even;
-
- if (bttv_debug > 1)
- printk("bttv%d: vbi1: po=%08lx pe=%08lx\n",
- btv->nr,virt_to_bus(po), virt_to_bus(pe));
-
- *(po++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(po++)=0;
- for (i=0; i<VBI_MAXLINES; i++)
- {
- *(po++)=cpu_to_le32(VBI_RISC);
- *(po++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048));
- }
- *(po++)=cpu_to_le32(BT848_RISC_JUMP);
- *(po++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+4));
+ struct video_channel c;
- *(pe++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(pe++)=0;
- for (i=VBI_MAXLINES; i<VBI_MAXLINES*2; i++)
- {
- *(pe++)=cpu_to_le32(VBI_RISC);
- *(pe++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048));
- }
- *(pe++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16));
- *(pe++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+10));
-
- if (bttv_debug > 1)
- printk("bttv%d: vbi2: po=%08lx pe=%08lx\n",
- btv->nr,virt_to_bus(po), virt_to_bus(pe));
+ memset(&c,0,sizeof(c));
+ c.norm = btv->tvnorm;
+ c.channel = btv->input;
+ bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
}
-static int fmtbppx2[16] = {
- 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0
-};
+static int
+set_tvnorm(struct bttv *btv, unsigned int norm)
+{
+ const struct bttv_tvnorm *tvnorm;
-static int palette2fmt[] = {
- 0,
- BT848_COLOR_FMT_Y8,
- BT848_COLOR_FMT_RGB8,
- BT848_COLOR_FMT_RGB16,
- BT848_COLOR_FMT_RGB24,
- BT848_COLOR_FMT_RGB32,
- BT848_COLOR_FMT_RGB15,
- BT848_COLOR_FMT_YUY2,
- BT848_COLOR_FMT_YUY2,
- -1,
- -1,
- -1,
- BT848_COLOR_FMT_RAW,
- BT848_COLOR_FMT_YCrCb422,
- BT848_COLOR_FMT_YCrCb411,
- BT848_COLOR_FMT_YCrCb422,
- BT848_COLOR_FMT_YCrCb411,
-};
-#define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int))
+ if (norm < 0 || norm >= BTTV_TVNORMS)
+ return -EINVAL;
-static int make_rawrisctab(struct bttv *btv, unsigned int *ro,
- unsigned int *re, unsigned int *vbuf)
-{
- unsigned long line;
- unsigned long bpl=1024; /* bytes per line */
- unsigned long vadr=(unsigned long) vbuf;
-
- *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(ro++)=cpu_to_le32(0);
- *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(re++)=cpu_to_le32(0);
-
- /* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY
- is 2 and without separate VBI grabbing.
- We'll have to handle this inside the IRQ handler ... */
-
- for (line=0; line < 640; line++)
- {
- *(ro++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL);
- *(ro++)=cpu_to_le32(kvirt_to_bus(vadr));
- *(re++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL);
- *(re++)=cpu_to_le32(kvirt_to_bus(vadr+gbufsize/2));
- vadr+=bpl;
- }
-
- *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
- *(ro++)=cpu_to_le32(btv->bus_vbi_even);
- *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
- *(re++)=cpu_to_le32(btv->bus_vbi_odd);
+ btv->tvnorm = norm;
+ tvnorm = &bttv_tvnorms[norm];
+
+ btwrite(tvnorm->adelay, BT848_ADELAY);
+ btwrite(tvnorm->bdelay, BT848_BDELAY);
+ btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+ BT848_IFORM);
+ btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+ btwrite(1, BT848_VBI_PACK_DEL);
+ btv->pll.pll_ofreq = tvnorm->Fsc;
+ set_pll(btv);
return 0;
}
-static int make_prisctab(struct bttv *btv, unsigned int *ro,
- unsigned int *re,
- unsigned int *vbuf, unsigned short width,
- unsigned short height, unsigned short fmt)
+static void
+set_input(struct bttv *btv, unsigned int input)
{
- unsigned long line, lmask;
- unsigned long bl, blcr, blcb, rcmd;
- unsigned long todo;
- unsigned int **rp;
- int inter;
- unsigned long cbadr, cradr;
- unsigned long vadr=(unsigned long) vbuf;
- int shift, csize;
-
- if (bttv_debug > 1)
- printk("bttv%d: prisc1: ro=%08lx re=%08lx\n",
- btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
- switch(fmt)
- {
- case VIDEO_PALETTE_YUV422P:
- csize=(width*height)>>1;
- shift=1;
- lmask=0;
- break;
-
- case VIDEO_PALETTE_YUV411P:
- csize=(width*height)>>2;
- shift=2;
- lmask=0;
- break;
-
- case VIDEO_PALETTE_YUV420P:
- csize=(width*height)>>2;
- shift=1;
- lmask=1;
- break;
-
- case VIDEO_PALETTE_YUV410P:
- csize=(width*height)>>4;
- shift=2;
- lmask=3;
- break;
-
- default:
- return -1;
- }
- cbadr=vadr+(width*height);
- cradr=cbadr+csize;
- inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
-
- *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
- *(ro++)=0;
- *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
- *(re++)=0;
-
- for (line=0; line < (height<<(1^inter)); line++)
- {
- if(line==height)
- {
- vadr+=csize<<1;
- cbadr=vadr+(width*height);
- cradr=cbadr+csize;
- }
- if (inter)
- rp= (line&1) ? &re : &ro;
- else
- rp= (line>=height) ? &ro : &re;
-
-
- if(line&lmask)
- rcmd=BT848_RISC_WRITE1S23|BT848_RISC_SOL;
- else
- rcmd=BT848_RISC_WRITE123|BT848_RISC_SOL;
-
- todo=width;
- while(todo)
- {
- bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
- blcr=(PAGE_SIZE-((PAGE_SIZE-1)&cradr))<<shift;
- blcb=(PAGE_SIZE-((PAGE_SIZE-1)&cbadr))<<shift;
- bl=(blcr<bl) ? blcr : bl;
- bl=(blcb<bl) ? blcb : bl;
- bl=(bl>todo) ? todo : bl;
- blcr=bl>>shift;
- blcb=blcr;
- /* bl now containts the longest row that can be written */
- todo-=bl;
- if(!todo) rcmd|=BT848_RISC_EOL; /* if this is the last EOL */
-
- *((*rp)++)=cpu_to_le32(rcmd|bl);
- *((*rp)++)=cpu_to_le32(blcb|(blcr<<16));
- *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
- vadr+=bl;
- if((rcmd&(15<<28))==BT848_RISC_WRITE123)
- {
- *((*rp)++)=cpu_to_le32(kvirt_to_bus(cbadr));
- cbadr+=blcb;
- *((*rp)++)=cpu_to_le32(kvirt_to_bus(cradr));
- cradr+=blcr;
- }
-
- rcmd&=~BT848_RISC_SOL; /* only the first has SOL */
- }
- }
-
- *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
- *(ro++)=cpu_to_le32(btv->bus_vbi_even);
- *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
- *(re++)=cpu_to_le32(btv->bus_vbi_odd);
-
- if (bttv_debug > 1)
- printk("bttv%d: prisc2: ro=%08lx re=%08lx\n",
- btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
- return 0;
+ btv->input = input;
+ video_mux(btv,input);
+ audio_mux(btv,(input == bttv_tvcards[btv->type].tuner ?
+ AUDIO_TUNER : AUDIO_EXTERN));
+ set_tvnorm(btv,btv->tvnorm);
}
-
-static int make_vrisctab(struct bttv *btv, unsigned int *ro,
- unsigned int *re,
- unsigned int *vbuf, unsigned short width,
- unsigned short height, unsigned short palette)
+
+static void init_bt848(struct bttv *btv)
{
- unsigned long line;
- unsigned long bpl; /* bytes per line */
- unsigned long bl;
- unsigned long todo;
- unsigned int **rp;
- int inter;
- unsigned long vadr=(unsigned long) vbuf;
-
- if (palette==VIDEO_PALETTE_RAW)
- return make_rawrisctab(btv, ro, re, vbuf);
- if (palette>=VIDEO_PALETTE_PLANAR)
- return make_prisctab(btv, ro, re, vbuf, width, height, palette);
-
- if (bttv_debug > 1)
- printk("bttv%d: vrisc1: ro=%08lx re=%08lx\n",
- btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
- inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
- bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2;
+ int val;
- *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(ro++)=cpu_to_le32(0);
- *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(re++)=cpu_to_le32(0);
-
- for (line=0; line < (height<<(1^inter)); line++)
- {
- if (inter)
- rp= (line&1) ? &re : &ro;
- else
- rp= (line>=height) ? &ro : &re;
-
- bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
- if (bpl<=bl)
- {
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
- BT848_RISC_EOL|bpl);
- *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
- vadr+=bpl;
- }
- else
- {
- todo=bpl;
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|bl);
- *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
- vadr+=bl;
- todo-=bl;
- while (todo>PAGE_SIZE)
- {
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|PAGE_SIZE);
- *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
- vadr+=PAGE_SIZE;
- todo-=PAGE_SIZE;
- }
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|todo);
- *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
- vadr+=todo;
- }
+ btwrite(0, BT848_SRESET);
+ btwrite(0x00, BT848_CAP_CTL);
+ btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+ btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+
+ /* set planar and packed mode trigger points and */
+ /* set rising edge of inverted GPINTR pin as irq trigger */
+ btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+ BT848_GPIO_DMA_CTL_PLTP1_16|
+ BT848_GPIO_DMA_CTL_PLTP23_16|
+ BT848_GPIO_DMA_CTL_GPINTC|
+ BT848_GPIO_DMA_CTL_GPINTI,
+ BT848_GPIO_DMA_CTL);
+
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+
+ btwrite(0x20, BT848_E_VSCALE_HI);
+ btwrite(0x20, BT848_O_VSCALE_HI);
+ btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+
+ if (btv->opt_lumafilter) {
+ btwrite(0, BT848_E_CONTROL);
+ btwrite(0, BT848_O_CONTROL);
+ } else {
+ btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
}
-
- *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
- *(ro++)=cpu_to_le32(btv->bus_vbi_even);
- *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
- *(re++)=cpu_to_le32(btv->bus_vbi_odd);
-
- if (bttv_debug > 1)
- printk("bttv%d: vrisc2: ro=%08lx re=%08lx\n",
- btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
- return 0;
}
-static unsigned char lmaskt[8] =
-{ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
-static unsigned char rmaskt[8] =
-{ 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
-
-static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h)
+extern void bttv_reinit_bt848(struct bttv *btv)
{
- unsigned char lmask, rmask, *p;
- int W, l, r;
- int i;
+ unsigned long flags;
- if (bttv_debug > 1)
- printk("bttv clip: %dx%d+%d+%d\n",w,h,x,y);
-
- /* bitmap is fixed width, 128 bytes (1024 pixels represented) */
- if (x<0)
- {
- w+=x;
- x=0;
- }
- if (y<0)
- {
- h+=y;
- y=0;
- }
- if (w < 0 || h < 0) /* catch bad clips */
- return;
- /* out of range data should just fall through */
- if (y+h>=625)
- h=625-y;
- if (x+w>=1024)
- w=1024-x;
-
- l=x>>3;
- r=(x+w-1)>>3;
- W=r-l-1;
- lmask=lmaskt[x&7];
- rmask=rmaskt[(x+w-1)&7];
- p=clipmap+128*y+l;
-
- if (W>0)
- {
- for (i=0; i<h; i++, p+=128)
- {
- *p|=lmask;
- memset(p+1, 0xff, W);
- p[W+1]|=rmask;
- }
- } else if (!W) {
- for (i=0; i<h; i++, p+=128)
- {
- p[0]|=lmask;
- p[1]|=rmask;
- }
- } else {
- for (i=0; i<h; i++, p+=128)
- p[0]|=lmask&rmask;
- }
-
+ if (bttv_verbose)
+ printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->nr);
+ spin_lock_irqsave(&btv->s_lock,flags);
+ btv->errors=0;
+ bttv_set_dma(btv,0,0);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ init_bt848(btv);
+ btv->pll.pll_current = -1;
+ set_input(btv,btv->input);
}
-static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr)
+#ifdef HAVE_V4L2
+static int get_control(struct bttv *btv, struct v4l2_control *c)
{
- int i, line, x, y, bpl, width, height, inter, maxw;
- unsigned int bpp, dx, sx, **rp, *ro, *re, flags, len;
- unsigned long adr;
- unsigned char *clipmap, *clipline, cbit, lastbit, outofmem;
-
- /* take care: bpp != btv->win.bpp is allowed here */
- bpp = fmtbppx2[btv->win.color_fmt&0xf]/2;
- bpl=btv->win.bpl;
- adr=btv->win.vidadr + btv->win.x * btv->win.bpp + btv->win.y * bpl;
- inter=(btv->win.interlace&1)^1;
- width=btv->win.width;
- height=btv->win.height;
- if (bttv_debug > 1)
- printk("bttv%d: clip1: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
- btv->nr,btv->picture.palette,width,height,bpl,bpp);
- if(width > 1023)
- width = 1023; /* sanity check */
- if(height > 625)
- height = 625; /* sanity check */
- ro=btv->risc_scr_odd;
- re=btv->risc_scr_even;
-
- if (bttv_debug)
- printk("bttv%d: clip: ro=%08lx re=%08lx\n",
- btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
- if ((clipmap=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) {
- /* can't clip, don't generate any risc code */
- *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
- *(ro++)=cpu_to_le32(btv->bus_vbi_even);
- *(re++)=cpu_to_le32(BT848_RISC_JUMP);
- *(re++)=cpu_to_le32(btv->bus_vbi_odd);
- }
- if (ncr < 0) { /* bitmap was pased */
- memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE);
- } else { /* convert rectangular clips to a bitmap */
- memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */
- for (i=0; i<ncr; i++)
- clip_draw_rectangle(clipmap, cr[i].x, cr[i].y,
- cr[i].width, cr[i].height);
- }
- /* clip against viewing window AND screen
- so we do not have to rely on the user program
- */
- maxw = (bpl - btv->win.x * btv->win.bpp) / bpp;
- clip_draw_rectangle(clipmap, (width > maxw) ? maxw : width,
- 0, 1024, 768);
- clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ?
- (btv->win.sheight-btv->win.y) : height,1024,768);
- if (btv->win.x<0)
- clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768);
- if (btv->win.y<0)
- clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y));
-
- *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(ro++)=cpu_to_le32(0);
- *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
- *(re++)=cpu_to_le32(0);
+ struct video_audio va;
+ int i;
- /* translate bitmap to risc code */
- for (line=outofmem=0; line < (height<<inter) && !outofmem; line++)
- {
- y = line>>inter;
- rp= (line&1) ? &re : &ro;
- clipline = clipmap + (y<<7); /* running pointers ... */
- lastbit = *clipline & 1;
- for(x=dx=0,sx=0; x<=width && !outofmem;) {
- if (0 == (x&7)) {
- /* check bytes not bits if we can ... */
- if (lastbit) {
- while (0xff==*clipline && x<width-8) {
- x += 8;
- dx += 8;
- clipline++;
- }
- } else {
- while (0x00==*clipline && x<width-8) {
- x += 8;
- dx += 8;
- clipline++;
- }
- }
- }
- cbit = *clipline & (1<<(x&7));
- if (x < width && !lastbit == !cbit) {
- dx++;
- } else {
- /* generate the dma controller code */
- len = dx * bpp;
- flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0);
- flags |= ((!sx) ? BT848_RISC_SOL : 0);
- flags |= ((sx + dx == width) ? BT848_RISC_EOL : 0);
- if (!lastbit) {
- *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|flags|len);
- *((*rp)++)=cpu_to_le32(adr + bpp * sx);
- } else {
- *((*rp)++)=cpu_to_le32(BT848_RISC_SKIP|flags|len);
- }
- lastbit=cbit;
- sx += dx;
- dx = 1;
- if (ro - btv->risc_scr_odd>(RISCMEM_LEN>>3) - 16)
- outofmem++;
- if (re - btv->risc_scr_even>(RISCMEM_LEN>>3) - 16)
- outofmem++;
- }
- x++;
- if (0 == (x&7))
- clipline++;
- }
- if ((!inter)||(line&1))
- adr+=bpl;
+ for (i = 0; i < BTTV_CTLS; i++)
+ if (bttv_ctls[i].id == c->id)
+ break;
+ if (i == BTTV_CTLS)
+ return -EINVAL;
+ if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+ memset(&va,0,sizeof(va));
+ bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&va,0);
}
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = btv->bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = btv->hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = btv->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = btv->saturation;
+ break;
- vfree(clipmap);
- /* outofmem flag relies on the following code to discard extra data */
- *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
- *(ro++)=cpu_to_le32(btv->bus_vbi_even);
- *(re++)=cpu_to_le32(BT848_RISC_JUMP);
- *(re++)=cpu_to_le32(btv->bus_vbi_odd);
+ case V4L2_CID_AUDIO_MUTE:
+ c->value = (VIDEO_AUDIO_MUTE == va.flags) ? 1 : 0;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ c->value = va.volume;
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ c->value = va.balance;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ c->value = va.bass;
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ c->value = va.treble;
+ break;
- if (bttv_debug > 1)
- printk("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
- btv->nr,btv->picture.palette,width,height,bpl,bpp);
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ c->value = btv->opt_chroma_agc;
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ c->value = btv->opt_combfilter;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ c->value = btv->opt_lumafilter;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ c->value = btv->opt_automute;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ c->value = btv->opt_adc_crush;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
}
-/*
- * Set the registers for the size we have specified. Don't bother
- * trying to understand this without the BT848 manual in front of
- * you [AC].
- *
- * PS: The manual is free for download in .pdf format from
- * www.brooktree.com - nicely done those folks.
- */
-
-static inline void bt848_set_eogeo(struct bttv *btv, struct tvnorm *tvn,
- int odd, int width, int height)
+static int set_control(struct bttv *btv, struct v4l2_control *c)
{
- u16 vscale, hscale;
- u32 xsf, sr;
- u16 hdelay;
- u8 crop, vtc;
- int inter = (height>tvn->sheight/2) ? 0 : 1;
- int off = odd ? 0x80 : 0x00;
-
- xsf = (width*tvn->scaledtwidth)/tvn->swidth;
- hscale = ((tvn->totalwidth*4096UL)/xsf-4096);
- hdelay = tvn->hdelayx1;
- hdelay = (hdelay*width)/tvn->swidth;
- hdelay &= 0x3fe;
- sr=((tvn->sheight>>inter)*512)/height-512;
- vscale=(0x10000UL-sr)&0x1fff;
- crop=((width>>8)&0x03)|((hdelay>>6)&0x0c)|
- ((tvn->sheight>>4)&0x30)|((tvn->vdelay>>2)&0xc0);
- vscale |= inter ? (BT848_VSCALE_INT<<8) : 0;
-
- if (combfilter) {
- /* Some people say interpolation looks bad ... */
- vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
- if (width < 769)
- btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
- else
- btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
- } else {
- vtc = 0;
- btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
- }
-
- btwrite(vtc, BT848_E_VTC+off);
- btwrite(hscale>>8, BT848_E_HSCALE_HI+off);
- btwrite(hscale&0xff, BT848_E_HSCALE_LO+off);
- btaor((vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
- btwrite(vscale&0xff, BT848_E_VSCALE_LO+off);
- btwrite(width&0xff, BT848_E_HACTIVE_LO+off);
- btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off);
- btwrite(tvn->sheight&0xff, BT848_E_VACTIVE_LO+off);
- btwrite(tvn->vdelay&0xff, BT848_E_VDELAY_LO+off);
- btwrite(crop, BT848_E_CROP+off);
+ struct video_audio va;
+ int i,val;
+
+ for (i = 0; i < BTTV_CTLS; i++)
+ if (bttv_ctls[i].id == c->id)
+ break;
+ if (i == BTTV_CTLS)
+ return -EINVAL;
+ if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+ memset(&va,0,sizeof(va));
+ bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&va,0);
+ }
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ bt848_bright(btv,c->value);
+ break;
+ case V4L2_CID_HUE:
+ bt848_hue(btv,c->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ bt848_contrast(btv,c->value);
+ break;
+ case V4L2_CID_SATURATION:
+ bt848_sat(btv,c->value);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ if (c->value) {
+ va.flags |= VIDEO_AUDIO_MUTE;
+ audio_mux(btv, AUDIO_MUTE);
+ } else {
+ va.flags &= ~VIDEO_AUDIO_MUTE;
+ audio_mux(btv, AUDIO_UNMUTE);
+ }
+ break;
+
+ case V4L2_CID_AUDIO_VOLUME:
+ va.volume = c->value;
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ va.balance = c->value;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ va.bass = c->value;
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ va.treble = c->value;
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ btv->opt_chroma_agc = c->value;
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ btv->opt_combfilter = c->value;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ btv->opt_lumafilter = c->value;
+ if (btv->opt_lumafilter) {
+ btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ } else {
+ btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ }
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ btv->opt_automute = c->value;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ btv->opt_adc_crush = c->value;
+ btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+ bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&va,1);
+ }
+ return 0;
}
+#endif /* HAVE_V4L2 */
+/* ----------------------------------------------------------------------- */
-static void bt848_set_geo(struct bttv *btv,
- int no_irq_context)
+void bttv_gpio_tracking(struct bttv *btv, char *comment)
{
- u16 ewidth, eheight, owidth, oheight;
- u16 format, bswap;
- struct tvnorm *tvn;
+ unsigned int outbits, data;
+ outbits = btread(BT848_GPIO_OUT_EN);
+ data = btread(BT848_GPIO_DATA);
+ printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+ btv->nr,outbits,data & outbits, data & ~outbits, comment);
+}
- tvn=&tvnorms[btv->win.norm];
-
- btwrite(tvn->adelay, BT848_ADELAY);
- btwrite(tvn->bdelay, BT848_BDELAY);
- btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM);
- btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE);
- btwrite(1, BT848_VBI_PACK_DEL);
+void bttv_field_count(struct bttv *btv)
+{
+ int need_count = 0;
- btv->pll.pll_ofreq = tvn->Fsc;
- if (no_irq_context)
- set_pll(btv);
-
- btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0;
-
- if (0 == btv->risc_cap_odd &&
- 0 == btv->risc_cap_even) {
- /* overlay only */
- owidth = btv->win.width;
- oheight = btv->win.height;
- ewidth = btv->win.width;
- eheight = btv->win.height;
- format = btv->win.color_fmt;
- bswap = btv->fb_color_ctl;
- } else if (-1 != btv->gq_grab &&
- 0 == btv->risc_cap_odd &&
- !btv->win.interlace &&
- btv->scr_on) {
- /* odd field -> overlay, even field -> capture */
- owidth = btv->win.width;
- oheight = btv->win.height;
- ewidth = btv->gbuf[btv->gq_grab].width;
- eheight = btv->gbuf[btv->gq_grab].height;
- format = (btv->win.color_fmt & 0xf0) |
- (btv->gbuf[btv->gq_grab].fmt & 0x0f);
- bswap = btv->fb_color_ctl & 0x0a;
+ if (locked_btres(btv,RESOURCE_STREAMING))
+ need_count++;
+ if (btv->vbi.users)
+ need_count++;
+
+ if (need_count) {
+ /* start field counter */
+ btor(BT848_INT_VSYNC,BT848_INT_MASK);
} else {
- /* capture only */
- owidth = btv->gbuf[btv->gq_grab].width;
- oheight = btv->gbuf[btv->gq_grab].height;
- ewidth = btv->gbuf[btv->gq_grab].width;
- eheight = btv->gbuf[btv->gq_grab].height;
- format = btv->gbuf[btv->gq_grab].fmt;
- bswap = 0;
+ /* stop field counter */
+ btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+ btv->field_count = 0;
}
+}
- /* program odd + even fields */
- bt848_set_eogeo(btv, tvn, 1, owidth, oheight);
- bt848_set_eogeo(btv, tvn, 0, ewidth, eheight);
+static const struct bttv_format*
+format_by_palette(int palette)
+{
+ int i;
- btwrite(format, BT848_COLOR_FMT);
- btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+ for (i = 0; i < BTTV_FORMATS; i++) {
+ if (-1 == bttv_formats[i].palette)
+ continue;
+ if (bttv_formats[i].palette == palette)
+ return bttv_formats+i;
+ }
+ return NULL;
}
+#ifdef HAVE_V4L2
+static const struct bttv_format*
+format_by_fourcc(int fourcc)
+{
+ int i;
-static int bpp2fmt[4] = {
- BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16,
- BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32
-};
+ for (i = 0; i < BTTV_FORMATS; i++) {
+ if (-1 == bttv_formats[i].fourcc)
+ continue;
+ if (bttv_formats[i].fourcc == fourcc)
+ return bttv_formats+i;
+ }
+ return NULL;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* misc helpers */
-static void bt848_set_winsize(struct bttv *btv)
+static int
+bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+ struct bttv_buffer *new)
{
- unsigned short format;
+ struct bttv_buffer *old;
+ unsigned long flags;
+ int retval = 0;
+
+ if (new)
+ new->vb.state = STATE_DONE;
+ spin_lock_irqsave(&btv->s_lock,flags);
+ old = btv->screen;
+ btv->screen = new;
+ bttv_set_dma(btv, 0x03, 1);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ if (NULL == new)
+ free_btres(btv,fh,RESOURCE_OVERLAY);
+ if (NULL != old)
+ bttv_dma_free(btv, old);
+ return retval;
+}
- if (btv->picture.palette > 0 && btv->picture.palette <= VIDEO_PALETTE_YUV422) {
- /* format set by VIDIOCSPICT */
- format = palette2fmt[btv->picture.palette];
- } else {
- /* use default for the given color depth */
- format = (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 :
- bpp2fmt[(btv->win.bpp-1)&3];
- }
- btv->win.color_fmt = format;
- if (bigendian &&
- format == BT848_COLOR_FMT_RGB32) {
- btv->fb_color_ctl =
- BT848_COLOR_CTL_WSWAP_ODD |
- BT848_COLOR_CTL_WSWAP_EVEN |
- BT848_COLOR_CTL_BSWAP_ODD |
- BT848_COLOR_CTL_BSWAP_EVEN;
- } else if (bigendian &&
- (format == BT848_COLOR_FMT_RGB16 ||
- format == BT848_COLOR_FMT_RGB15)) {
- btv->fb_color_ctl =
- BT848_COLOR_CTL_BSWAP_ODD |
- BT848_COLOR_CTL_BSWAP_EVEN;
- } else {
- btv->fb_color_ctl = 0;
- }
-
- /* RGB8 seems to be a 9x5x5 GRB color cube starting at
- * color 16. Why the h... can't they even mention this in the
- * data sheet? [AC - because it's a standard format so I guess
- * it never occurred to them]
- * Enable dithering in this mode.
- */
-
- if (format==BT848_COLOR_FMT_RGB8)
- btand(~BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL);
- else
- btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL);
-
- bt848_set_geo(btv,1);
+static void
+bttv_stop_streaming(struct bttv *btv, struct bttv_fh *fh)
+{
+ unsigned long flags;
+ int i;
+
+ /* remove queued buffers from list */
+ spin_lock_irqsave(&btv->s_lock,flags);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == fh->bufs[i])
+ continue;
+ if (fh->bufs[i]->vb.state == STATE_QUEUED) {
+ list_del(&fh->bufs[i]->vb.queue);
+ fh->bufs[i]->vb.state = STATE_ERROR;
+ }
+ }
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+
+ /* free all buffers + clear queue */
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == fh->bufs[i])
+ continue;
+ bttv_dma_free(fh->btv,fh->bufs[i]);
+ }
+ INIT_LIST_HEAD(&fh->stream);
+ free_btres(btv,fh,RESOURCE_STREAMING);
+ bttv_field_count(btv);
}
-/*
- * Grab into virtual memory.
- */
+/* ----------------------------------------------------------------------- */
+/* video4linux (1) interface */
-static int vgrab(struct bttv *btv, struct video_mmap *mp)
+static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf,
+ const struct bttv_format *fmt,
+ int width, int height, int field)
{
- unsigned int *ro, *re;
- unsigned int *vbuf;
- unsigned long flags;
+ int redo_dma_risc = 0;
+ int rc;
- if(btv->fbuffer==NULL)
- {
- if(fbuffer_alloc(btv))
- return -ENOBUFS;
+ /* check settings */
+ if (NULL == fmt)
+ return -EINVAL;
+ if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+ width = RAW_BPL;
+ height = RAW_LINES*2;
+ if (width*height > buf->vb.bsize)
+ return -EINVAL;
+ buf->vb.size = buf->vb.bsize;
+ } else {
+ if (width < 48 ||
+ height < 32 ||
+ width > bttv_tvnorms[btv->tvnorm].swidth ||
+ height > bttv_tvnorms[btv->tvnorm].sheight)
+ return -EINVAL;
+ buf->vb.size = (width * height * fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
}
- if(mp->frame >= gbuffers || mp->frame < 0)
- return -EINVAL;
- if(btv->gbuf[mp->frame].stat != GBUFFER_UNUSED)
- return -EBUSY;
-
- if(mp->height < 32 || mp->width < 48)
- return -EINVAL;
- if (mp->format >= PALETTEFMT_MAX)
- return -EINVAL;
+ /* alloc + fill struct bttv_buffer (if changed) */
+ if (buf->vb.width != width || buf->vb.height != height ||
+ buf->tvnorm != btv->tvnorm || buf->fmt != fmt) {
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->tvnorm = btv->tvnorm;
+ buf->fmt = fmt;
+ redo_dma_risc = 1;
+ }
- if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2
- > gbufsize)
- return -EINVAL;
- if(-1 == palette2fmt[mp->format])
- return -EINVAL;
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ field = bttv_buffer_field(btv, field, VBUF_FIELD_EVEN,
+ btv->tvnorm, height);
+ if (field != buf->vb.field)
+ redo_dma_risc = 1;
+ if (redo_dma_risc)
+ bttv_dma_free(btv,buf);
+ }
- /*
- * Ok load up the BT848
- */
-
- vbuf=(unsigned int *)(btv->fbuffer+gbufsize*mp->frame);
- ro=btv->gbuf[mp->frame].risc;
- re=ro+2048;
- make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format);
-
- if (bttv_debug)
- printk("bttv%d: cap vgrab: queue %d (%d:%dx%d)\n",
- btv->nr,mp->frame,mp->format,mp->width,mp->height);
- spin_lock_irqsave(&btv->s_lock, flags);
- btv->gbuf[mp->frame].stat = GBUFFER_GRABBING;
- btv->gbuf[mp->frame].fmt = palette2fmt[mp->format];
- btv->gbuf[mp->frame].width = mp->width;
- btv->gbuf[mp->frame].height = mp->height;
- btv->gbuf[mp->frame].ro = virt_to_bus(ro);
- btv->gbuf[mp->frame].re = virt_to_bus(re);
-
-#if 1
- if (mp->height <= tvnorms[btv->win.norm].sheight/2 &&
- mp->format != VIDEO_PALETTE_RAW)
- btv->gbuf[mp->frame].ro = 0;
-#endif
+ /* alloc risc memory */
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ redo_dma_risc = 1;
+ if (0 != (rc = videobuf_iolock(btv->dev,&buf->vb)))
+ goto fail;
+ }
- if (-1 == btv->gq_grab && btv->gq_in == btv->gq_out) {
- btv->gq_start = 1;
- btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ);
- }
- btv->gqueue[btv->gq_in++] = mp->frame;
- btv->gq_in = btv->gq_in % MAX_GBUFFERS;
+ if (redo_dma_risc)
+ if (0 != (rc = bttv_buffer_risc(btv,buf)))
+ goto fail;
- btor(3, BT848_CAP_CTL);
- btor(3, BT848_GPIO_DMA_CTL);
- spin_unlock_irqrestore(&btv->s_lock, flags);
+ buf->vb.state = STATE_PREPARED;
return 0;
+
+ fail:
+ bttv_dma_free(btv,buf);
+ return rc;
}
-static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock)
+static void
+bttv_queue_buffer(struct bttv *btv, struct bttv_buffer *buf)
{
- return -EINVAL;
+ unsigned long flags;
+
+ buf->vb.state = STATE_QUEUED;
+ spin_lock_irqsave(&btv->s_lock,flags);
+ list_add_tail(&buf->vb.queue,&btv->capture);
+ bttv_set_dma(btv, 0x03, 1);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
}
-static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+static const char *v4l1_ioctls[] = {
+ "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT",
+ "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ",
+ "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT",
+ "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO",
+ "SMICROCODE", "GVBIFMT", "SVBIFMT" };
+#define V4L1_IOCTLS (sizeof(v4l1_ioctls)/sizeof(char*))
+
+static const char *v4l2_ioctls[] = {
+ "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT",
+ "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF",
+ "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON",
+ "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD",
+ "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER",
+ "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL",
+ "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43",
+ "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT",
+ "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR",
+ "S_MODULATOR"
+};
+#define V4L2_IOCTLS (sizeof(v4l2_ioctls)/sizeof(char*))
+
+int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
{
- struct bttv *btv= (struct bttv *)v;
- int q,todo;
- DECLARE_WAITQUEUE(wait, current);
+ switch (cmd) {
+ case BTTV_VERSION:
+ return BTTV_VERSION_CODE;
- /* BROKEN: RETURNS VBI WHEN IT SHOULD RETURN GRABBED VIDEO FRAME */
- todo=count;
- while (todo && todo>(q=VBIBUF_SIZE-btv->vbip))
+ /* *** v4l1 *** ************************************************ */
+ case VIDIOCGFREQ:
+#ifdef HAVE_V4L2
+ case VIDIOC_G_FREQ:
+#endif
{
- if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
- return -EFAULT;
- todo-=q;
- buf+=q;
-
- add_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_INTERRUPTIBLE;
- if (todo && q==VBIBUF_SIZE-btv->vbip)
- {
- if(nonblock)
- {
- remove_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_RUNNING;
- if(count==todo)
- return -EWOULDBLOCK;
- return count-todo;
- }
- schedule();
- if(signal_pending(current))
- {
- remove_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_RUNNING;
-
- if(todo==count)
- return -EINTR;
- else
- return count-todo;
- }
- }
- remove_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_RUNNING;
+ unsigned long *freq = arg;
+ *freq = btv->freq;
+ return 0;
}
- if (todo)
+ case VIDIOCSFREQ:
+#ifdef HAVE_V4L2
+ case VIDIOC_S_FREQ:
+#endif
{
- if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
- return -EFAULT;
- btv->vbip+=todo;
+ unsigned long *freq = arg;
+ down(&btv->lock);
+ btv->freq=*freq;
+ bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq);
+ if (btv->has_matchbox && btv->radio_user)
+ tea5757_set_freq(btv,*freq);
+ up(&btv->lock);
+ return 0;
}
- return count;
-}
-static inline void burst(int on)
-{
- tvnorms[0].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0);
- tvnorms[0].hdelayx1 = 186 - (on?BURSTOFFSET :0);
- tvnorms[2].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0);
- tvnorms[2].hdelayx1 = 186 - (on?BURSTOFFSET :0);
-}
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+
+ if (v->tuner) /* Only tuner 0 */
+ return -EINVAL;
+ strcpy(v->name, "Television");
+ v->rangelow = 0;
+ v->rangehigh = 0x7FFFFFFF;
+ v->flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
+ v->mode = btv->tvnorm;
+ v->signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
+ bttv_call_i2c_clients(btv,cmd,v);
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
-/*
- * called from irq handler on fatal errors. Takes the grabber chip
- * offline, flag it needs a reinitialization (which can't be done
- * from irq context) and wake up all sleeping proccesses. They would
- * block forever else. We also need someone who actually does the
- * reinitialization from process context...
- */
-static void bt848_offline(struct bttv *btv)
-{
- int i;
- spin_lock(&btv->s_lock);
+ if (v->tuner) /* Only tuner 0 */
+ return -EINVAL;
+ if (v->mode >= BTTV_TVNORMS)
+ return -EINVAL;
- /* cancel all outstanding grab requests */
- btv->gq_in = 0;
- btv->gq_out = 0;
- btv->gq_grab = -1;
- for (i = 0; i < gbuffers; i++)
- if (btv->gbuf[i].stat == GBUFFER_GRABBING)
- btv->gbuf[i].stat = GBUFFER_ERROR;
-
- /* disable screen overlay and DMA */
- btv->risc_cap_odd = 0;
- btv->risc_cap_even = 0;
- bt848_set_risc_jmps(btv,0);
-
- /* flag the chip needs a restart */
- btv->needs_restart = 1;
- spin_unlock(&btv->s_lock);
+ down(&btv->lock);
+ set_tvnorm(btv,v->mode);
+ bttv_call_i2c_clients(btv,cmd,v);
+ up(&btv->lock);
+ return 0;
+ }
+
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
- wake_up_interruptible(&btv->vbiq);
- wake_up_interruptible(&btv->capq);
-}
+ if (v->channel >= bttv_tvcards[btv->type].video_inputs)
+ return -EINVAL;
+ v->tuners=0;
+ v->flags = VIDEO_VC_AUDIO;
+ v->type = VIDEO_TYPE_CAMERA;
+ v->norm = btv->tvnorm;
+ if(v->channel == bttv_tvcards[btv->type].tuner) {
+ strcpy(v->name,"Television");
+ v->flags|=VIDEO_VC_TUNER;
+ v->type=VIDEO_TYPE_TV;
+ v->tuners=1;
+ } else if (v->channel == bttv_tvcards[btv->type].svhs) {
+ strcpy(v->name,"S-Video");
+ } else {
+ sprintf(v->name,"Composite%d",v->channel);
+ }
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
-static void bt848_restart(struct bttv *btv)
-{
- unsigned long irq_flags;
+ if (v->channel < 0 ||
+ v->channel >= bttv_tvcards[btv->type].video_inputs)
+ return -EINVAL;
+ if (v->norm >= BTTV_TVNORMS)
+ return -EINVAL;
- if (bttv_verbose)
- printk("bttv%d: resetting chip\n",btv->nr);
- btwrite(0xfffffUL, BT848_INT_STAT);
- btand(~15, BT848_GPIO_DMA_CTL);
- btwrite(0, BT848_SRESET);
- btwrite(virt_to_bus(btv->risc_jmp+2),
- BT848_RISC_STRT_ADD);
+ down(&btv->lock);
+ if (v->channel == btv->input &&
+ v->norm == btv->tvnorm) {
+ /* nothing to do */
+ up(&btv->lock);
+ return 0;
+ }
- /* enforce pll reprogramming */
- btv->pll.pll_current = 0;
- set_pll(btv);
+ btv->tvnorm = v->norm;
+ set_input(btv,v->channel);
+ up(&btv->lock);
+ return 0;
+ }
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- btv->errors = 0;
- btv->needs_restart = 0;
- bt848_set_geo(btv,0);
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-}
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
-/*
- * Open a bttv card. Right now the flags stuff is just playing
- */
+ memset(v,0,sizeof(*v));
+ strcpy(v->name,"Television");
+ v->flags |= VIDEO_AUDIO_MUTABLE;
+ v->mode = VIDEO_SOUND_MONO;
-static int bttv_open(struct video_device *dev, int flags)
-{
- struct bttv *btv = (struct bttv *)dev;
- int i,ret;
+ down(&btv->lock);
+ bttv_call_i2c_clients(btv,cmd,v);
- ret = -EBUSY;
- if (bttv_debug)
- printk("bttv%d: open called\n",btv->nr);
+ /* card specific hooks */
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,v,0);
- down(&btv->lock);
- if (btv->user)
- goto out_unlock;
-
- btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize);
- ret = -ENOMEM;
- if (!btv->fbuffer)
- goto out_unlock;
-
- btv->gq_in = 0;
- btv->gq_out = 0;
- btv->gq_grab = -1;
- for (i = 0; i < gbuffers; i++)
- btv->gbuf[i].stat = GBUFFER_UNUSED;
-
- if (btv->needs_restart)
- bt848_restart(btv);
- burst(0);
- set_pll(btv);
- btv->user++;
- up(&btv->lock);
- return 0;
+ up(&btv->lock);
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
- out_unlock:
- up(&btv->lock);
- return ret;
-}
+ if(v->audio < 0 ||
+ v->audio >= bttv_tvcards[btv->type].audio_inputs)
+ return -EINVAL;
-static void bttv_close(struct video_device *dev)
-{
- struct bttv *btv=(struct bttv *)dev;
- unsigned long irq_flags;
- int need_wait;
+ down(&btv->lock);
+ audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE);
+ bttv_call_i2c_clients(btv,cmd,v);
- down(&btv->lock);
- btv->user--;
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- need_wait = (-1 != btv->gq_grab);
- btv->gq_start = 0;
- btv->gq_in = 0;
- btv->gq_out = 0;
- btv->gq_grab = -1;
- btv->scr_on = 0;
- btv->risc_cap_odd = 0;
- btv->risc_cap_even = 0;
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-
- /*
- * A word of warning. At this point the chip
- * is still capturing because its FIFO hasn't emptied
- * and the DMA control operations are posted PCI
- * operations.
- */
-
- btread(BT848_I2C); /* This fixes the PCI posting delay */
-
- if (need_wait) {
- /*
- * This is sucky but right now I can't find a good way to
- * be sure its safe to free the buffer. We wait 5-6 fields
- * which is more than sufficient to be sure.
- */
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(HZ/10); /* Wait 1/10th of a second */
+ /* card specific hooks */
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,v,1);
+
+ up(&btv->lock);
+ return 0;
}
-
- /*
- * We have allowed it to drain.
- */
- if(btv->fbuffer)
- rvfree((void *) btv->fbuffer, gbuffers*gbufsize);
- btv->fbuffer=0;
- up(&btv->lock);
-}
+#ifdef HAVE_V4L2
+ /* *** v4l2 *** ************************************************ */
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_enumstd *e = arg;
+ if (e->index < 0 || e->index >= BTTV_TVNORMS)
+ return -EINVAL;
+ v4l2_video_std_construct(&e->std, bttv_tvnorms[e->index].v4l2_id, 0);
+ e->inputs = 0x0f;
+ e->outputs = 0x00;
+ return 0;
+ }
+ case VIDIOC_G_STD:
+ {
+ struct v4l2_standard *s = arg;
+ v4l2_video_std_construct(s,bttv_tvnorms[btv->tvnorm].v4l2_id,0);
+ return 0;
+ }
+ case VIDIOC_S_STD:
+ {
+ struct v4l2_standard *s = arg;
+ int i, id = v4l2_video_std_confirm(s);
-/***********************************/
-/* ioctls and supporting functions */
-/***********************************/
+ for(i = 0; i < BTTV_TVNORMS; i++)
+ if (id == bttv_tvnorms[i].v4l2_id)
+ break;
+ if (i == BTTV_TVNORMS)
+ return -EINVAL;
-extern inline void bt848_bright(struct bttv *btv, uint bright)
-{
- btwrite(bright&0xff, BT848_BRIGHT);
-}
+ down(&btv->lock);
+ set_tvnorm(btv,i);
+ i2c_vidiocschan(btv);
+ up(&btv->lock);
+ return 0;
+ }
-extern inline void bt848_hue(struct bttv *btv, uint hue)
-{
- btwrite(hue&0xff, BT848_HUE);
-}
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
-extern inline void bt848_contrast(struct bttv *btv, uint cont)
-{
- unsigned int conthi;
+ if (i->index >= bttv_tvcards[btv->type].video_inputs)
+ return -EINVAL;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->capability = 0;
+ i->assoc_audio = 0;
+ if (i->index == bttv_tvcards[btv->type].tuner) {
+ sprintf(i->name, "Television");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->capability = V4L2_INPUT_CAP_AUDIO;
+ } else if (i->index==bttv_tvcards[btv->type].svhs) {
+ sprintf(i->name, "S-Video");
+ } else {
+ sprintf(i->name,"Composite%d",i->index);
+ }
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *i = arg;
+ *i = btv->input;
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int *i = arg;
+
+ if (*i < 0 || *i > bttv_tvcards[btv->type].video_inputs)
+ return -EINVAL;
+ down(&btv->lock);
+ set_input(btv,*i);
+ i2c_vidiocschan(btv);
+ up(&btv->lock);
+ return 0;
+ }
+
+ case VIDIOC_G_TUNER: {
+ struct v4l2_tuner *t = arg;
- conthi=(cont>>6)&4;
- btwrite(cont&0xff, BT848_CONTRAST_LO);
- btaor(conthi, ~4, BT848_E_CONTROL);
- btaor(conthi, ~4, BT848_O_CONTROL);
+ down(&btv->lock);
+ memset(t,0,sizeof(*t));
+ t->input = bttv_tvcards[btv->type].tuner;
+ strcpy(t->name, "Television");
+ v4l2_video_std_construct(&t->std, bttv_tvnorms[btv->tvnorm].v4l2_id, 0);
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+ t->signal = 0xffff;
+ {
+ /* Hmmm ... */
+ struct video_audio va;
+ memset(&va, 0, sizeof(struct video_audio));
+ bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&va,0);
+ if(va.mode & VIDEO_SOUND_STEREO)
+ t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ if(va.mode & VIDEO_SOUND_LANG1)
+ t->rxsubchans |= V4L2_TUNER_SUB_LANG1;
+ if(va.mode & VIDEO_SOUND_LANG2)
+ t->rxsubchans |= V4L2_TUNER_SUB_LANG2;
+ }
+ /* FIXME: fill capability+audmode */
+ up(&btv->lock);
+ return 0;
+ }
+ case VIDIOC_S_TUNER: {
+ struct v4l2_tuner *t = arg;
+
+ if(t->input!=bttv_tvcards[btv->type].tuner)
+ return -EINVAL;
+ down(&btv->lock);
+ {
+ struct video_audio va;
+ memset(&va, 0, sizeof(struct video_audio));
+ if (t->audmode == V4L2_TUNER_MODE_MONO)
+ va.mode = VIDEO_SOUND_MONO;
+ else if (t->audmode == V4L2_TUNER_MODE_STEREO)
+ va.mode = VIDEO_SOUND_STEREO;
+ else if (t->audmode == V4L2_TUNER_MODE_LANG1)
+ va.mode = VIDEO_SOUND_LANG1;
+ else if (t->audmode == V4L2_TUNER_MODE_LANG2)
+ va.mode = VIDEO_SOUND_LANG2;
+ bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&va,1);
+ }
+ up(&btv->lock);
+ return 0;
+ }
+#endif /* HAVE_V4L2 */
+
+ default:
+ return -ENOIOCTLCMD;
+
+ }
+ return 0;
}
-extern inline void bt848_sat_u(struct bttv *btv, unsigned long data)
+static int setup_window(struct bttv_fh *fh, struct bttv *btv,
+ int x, int y, int width, int height,
+ struct video_clip *user_clips, int nclips)
{
- u32 datahi;
+ struct video_clip *clips = NULL;
+ int n,size,retval = 0;
+
+ if (width < 48 ||
+ height < 32 ||
+ width > bttv_tvnorms[btv->tvnorm].swidth ||
+ height > bttv_tvnorms[btv->tvnorm].sheight ||
+ NULL == fh->ovfmt)
+ return -EINVAL;
+ if (nclips > 2048)
+ return -EINVAL;
- datahi=(data>>7)&2;
- btwrite(data&0xff, BT848_SAT_U_LO);
- btaor(datahi, ~2, BT848_E_CONTROL);
- btaor(datahi, ~2, BT848_O_CONTROL);
+ /* copy clips -- luckily v4l1 + v4l2 are binary
+ compatible here ...*/
+ n = nclips;
+ size = sizeof(struct video_clip)*(n+4);
+ clips = kmalloc(size,GFP_KERNEL);
+ if (NULL == clips)
+ return -ENOMEM;
+ if (n > 0) {
+ if (copy_from_user(clips,user_clips,
+ sizeof(struct video_clip)*nclips)) {
+ kfree(clips);
+ return -EFAULT;
+ }
+ }
+ /* clip against screen */
+ if (NULL != btv->fbuf.base)
+ n = bttv_screen_clips(&btv->fbuf, x, y,
+ width, height,
+ clips, n);
+ bttv_sort_clips(clips,nclips);
+
+ down(&fh->lock);
+ if (fh->ov.clips)
+ kfree(fh->ov.clips);
+ fh->ov.clips = clips;
+ fh->ov.nclips = nclips;
+
+ fh->ov.x = x;
+ fh->ov.y = y;
+ fh->ov.width = width;
+ fh->ov.height = height;
+ btv->init.ov.width = width;
+ btv->init.ov.height = height;
+
+ /* update overlay if needed */
+ retval = 0;
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_alloc(sizeof(*new),V4L2_BUF_TYPE_CAPTURE);
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv,fh,new);
+ }
+ up(&fh->lock);
+ return retval;
}
-static inline void bt848_sat_v(struct bttv *btv, unsigned long data)
+static void release_buffer(struct file *file, struct videobuf_buffer *vb)
{
- u32 datahi;
+ struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+ struct bttv_fh *fh = file->private_data;
- datahi=(data>>8)&1;
- btwrite(data&0xff, BT848_SAT_V_LO);
- btaor(datahi, ~1, BT848_E_CONTROL);
- btaor(datahi, ~1, BT848_O_CONTROL);
+ bttv_dma_free(fh->btv,buf);
}
-/*
- * ioctl routine
- */
-
-
-static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static int bttv_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
{
- struct bttv *btv=(struct bttv *)dev;
- unsigned long irq_flags;
- int i,ret = 0;
-
- if (bttv_debug > 1)
- printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd);
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ int retval;
+
+ if (bttv_debug > 1) {
+ switch (_IOC_TYPE(cmd)) {
+ case 'v':
+ printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n",
+ btv->nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ?
+ v4l1_ioctls[_IOC_NR(cmd)] : "???");
+ break;
+ case 'V':
+ printk("bttv%d: ioctl 0x%x (v4l2, VIDIOC_%s)\n",
+ btv->nr, cmd, (_IOC_NR(cmd) < V4L2_IOCTLS) ?
+ v4l2_ioctls[_IOC_NR(cmd)] : "???");
+ break;
+ default:
+ printk("bttv%d: ioctl 0x%x (???)\n",
+ btv->nr, cmd);
+ }
+ }
+ if (btv->errors)
+ bttv_reinit_bt848(btv);
switch (cmd) {
+
+ /* *** v4l1 *** ************************************************ */
case VIDIOCGCAP:
{
- struct video_capability b;
- strcpy(b.name,btv->video_dev.name);
- b.type = VID_TYPE_CAPTURE|
- ((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) |
+ struct video_capability *cap = arg;
+
+ memset(cap,0,sizeof(*cap));
+ strcpy(cap->name,btv->video_dev.name);
+ cap->type = VID_TYPE_CAPTURE|
+ VID_TYPE_TUNER|
VID_TYPE_OVERLAY|
VID_TYPE_CLIPPING|
- VID_TYPE_FRAMERAM|
- VID_TYPE_SCALES;
- b.channels = bttv_tvcards[btv->type].video_inputs;
- b.audios = bttv_tvcards[btv->type].audio_inputs;
- b.maxwidth = tvnorms[btv->win.norm].swidth;
- b.maxheight = tvnorms[btv->win.norm].sheight;
- b.minwidth = 48;
- b.minheight = 32;
- if(copy_to_user(arg,&b,sizeof(b)))
- return -EFAULT;
- return 0;
+ VID_TYPE_SCALES;
+ cap->channels = bttv_tvcards[btv->type].video_inputs;
+ cap->audios = bttv_tvcards[btv->type].audio_inputs;
+ cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth;
+ cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
+ cap->minwidth = 48;
+ cap->minheight = 32;
+ return 0;
}
- case VIDIOCGCHAN:
+
+ case VIDIOCGPICT:
{
- struct video_channel v;
- if(copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
- v.flags=VIDEO_VC_AUDIO;
- v.tuners=0;
- v.type=VIDEO_TYPE_CAMERA;
- v.norm = btv->win.norm;
- if (v.channel>=bttv_tvcards[btv->type].video_inputs)
- return -EINVAL;
- if(v.channel==bttv_tvcards[btv->type].tuner)
- {
- strcpy(v.name,"Television");
- v.flags|=VIDEO_VC_TUNER;
- v.type=VIDEO_TYPE_TV;
- v.tuners=1;
- }
- else if(v.channel==bttv_tvcards[btv->type].svhs)
- strcpy(v.name,"S-Video");
- else
- sprintf(v.name,"Composite%d",v.channel);
-
- if(copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
+ struct video_picture *pic = arg;
+
+ memset(pic,0,sizeof(*pic));
+ pic->brightness = btv->bright;
+ pic->contrast = btv->contrast;
+ pic->hue = btv->hue;
+ pic->colour = btv->saturation;
+ if (fh->buf.fmt) {
+ pic->depth = fh->buf.fmt->depth;
+ pic->palette = fh->buf.fmt->palette;
+ }
return 0;
}
- /*
- * Each channel has 1 tuner
- */
- case VIDIOCSCHAN:
+ case VIDIOCSPICT:
{
- struct video_channel v;
- if(copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
+ struct video_picture *pic = arg;
+ const struct bttv_format *fmt;
- if (v.channel>bttv_tvcards[btv->type].video_inputs)
+ fmt = format_by_palette(pic->palette);
+ if (NULL == fmt)
return -EINVAL;
- if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms)))
- return -EOPNOTSUPP;
+ down(&fh->lock);
+ retval = -EINVAL;
+ if (fmt->depth != pic->depth && !sloppy)
+ goto fh_unlock_and_return;
+ fh->ovfmt = fmt;
+ fh->buf.fmt = fmt;
+ btv->init.ovfmt = fmt;
+ btv->init.buf.fmt = fmt;
+ if (bigendian) {
+ /* dirty hack time: swap bytes for overlay if the
+ display adaptor is big endian (insmod option) */
+ if (fmt->palette == VIDEO_PALETTE_RGB555 ||
+ fmt->palette == VIDEO_PALETTE_RGB565 ||
+ fmt->palette == VIDEO_PALETTE_RGB32) {
+ fh->ovfmt = fmt+1;
+ }
+ }
+ bt848_bright(btv,pic->brightness);
+ bt848_contrast(btv,pic->contrast);
+ bt848_hue(btv,pic->hue);
+ bt848_sat(btv,pic->colour);
+ up(&fh->lock);
+ return 0;
+ }
- bttv_call_i2c_clients(btv,cmd,&v);
- down(&btv->lock);
- bt848_muxsel(btv, v.channel);
- btv->channel=v.channel;
- if (btv->win.norm != v.norm) {
- btv->win.norm = v.norm;
- make_vbitab(btv);
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- bt848_set_winsize(btv);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+ case VIDIOCGWIN:
+ {
+ struct video_window *win = arg;
+
+ memset(win,0,sizeof(*win));
+ win->x = fh->ov.x;
+ win->y = fh->ov.y;
+ win->width = fh->ov.width;
+ win->height = fh->ov.height;
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *win = arg;
+
+ retval = setup_window(fh,btv,win->x,win->y,
+ win->width,win->height,
+ win->clips,
+ win->clipcount);
+ if (0 == retval) {
+ /* on v4l1 this ioctl affects the read() size too */
+ fh->buf.vb.width = fh->ov.width;
+ fh->buf.vb.height = fh->ov.height;
+ btv->init.buf.vb.width = fh->ov.width;
+ btv->init.buf.vb.height = fh->ov.height;
}
- up(&btv->lock);
+ return retval;
+ }
+
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *fbuf = arg;
+ *fbuf = btv->fbuf;
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOCSFBUF:
{
- struct video_tuner v;
- if(copy_from_user(&v,arg,sizeof(v))!=0)
- return -EFAULT;
-#if 0 /* tuner.signal might be of intrest for non-tuner sources too ... */
- if(v.tuner||btv->channel) /* Only tuner 0 */
+ struct video_buffer *fbuf = arg;
+ const struct bttv_format *fmt;
+ unsigned long end;
+
+ if(!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ end = (unsigned long)fbuf->base +
+ fbuf->height * fbuf->bytesperline;
+ if (0 == find_videomem((unsigned long)fbuf->base,end))
return -EINVAL;
-#endif
- strcpy(v.name, "Television");
- v.rangelow=0;
- v.rangehigh=0xFFFFFFFF;
- v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
- v.mode = btv->win.norm;
- v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
- bttv_call_i2c_clients(btv,cmd,&v);
- if(copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
+ down(&fh->lock);
+ retval = -EINVAL;
+ if (sloppy) {
+ /* also set the default palette -- for backward
+ compatibility with older versions */
+ switch (fbuf->depth) {
+ case 8:
+ fmt = format_by_palette(VIDEO_PALETTE_HI240);
+ break;
+ case 16:
+ fmt = format_by_palette(VIDEO_PALETTE_RGB565);
+ break;
+ case 24:
+ fmt = format_by_palette(VIDEO_PALETTE_RGB24);
+ break;
+ case 32:
+ fmt = format_by_palette(VIDEO_PALETTE_RGB32);
+ break;
+ case 15:
+ fbuf->depth = 16;
+ fmt = format_by_palette(VIDEO_PALETTE_RGB555);
+ break;
+ default:
+ fmt = NULL;
+ break;
+ }
+ if (NULL == fmt)
+ goto fh_unlock_and_return;
+ fh->ovfmt = fmt;
+ fh->buf.fmt = fmt;
+ btv->init.ovfmt = fmt;
+ btv->init.buf.fmt = fmt;
+ } else {
+ if (15 == fbuf->depth)
+ fbuf->depth = 16;
+ if (fbuf->depth != 8 && fbuf->depth != 16 &&
+ fbuf->depth != 24 && fbuf->depth != 32)
+ goto fh_unlock_and_return;
+ }
+ btv->fbuf = *fbuf;
+ up(&fh->lock);
return 0;
}
- /* We have but one tuner */
- case VIDIOCSTUNER:
+
+ case VIDIOCCAPTURE:
+#ifdef HAVE_V4L2
+ case VIDIOC_PREVIEW:
+#endif
{
- struct video_tuner v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- /* Only one channel has a tuner */
- if(v.tuner!=bttv_tvcards[btv->type].tuner)
- return -EINVAL;
-
- if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC
- &&v.mode!=VIDEO_MODE_SECAM)
- return -EOPNOTSUPP;
- bttv_call_i2c_clients(btv,cmd,&v);
- if (btv->win.norm != v.mode) {
- btv->win.norm = v.mode;
- down(&btv->lock);
- set_pll(btv);
- make_vbitab(btv);
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- bt848_set_winsize(btv);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
- up(&btv->lock);
+ struct bttv_buffer *new;
+ int *on = arg;
+
+ if (*on) {
+ /* verify args */
+ if (NULL == btv->fbuf.base)
+ return -EINVAL;
+ if (fh->ov.width <48 ||
+ fh->ov.height<32 ||
+ fh->ov.width >bttv_tvnorms[btv->tvnorm].swidth ||
+ fh->ov.height>bttv_tvnorms[btv->tvnorm].sheight ||
+ NULL == fh->ovfmt)
+ return -EINVAL;
}
+
+ if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
+ return -EBUSY;
+
+ down(&fh->lock);
+ if (*on) {
+ fh->ov.tvnorm = btv->tvnorm;
+ new = videobuf_alloc(sizeof(*new),
+ V4L2_BUF_TYPE_CAPTURE);
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ } else {
+ new = NULL;
+ }
+
+ /* switch over */
+ retval = bttv_switch_overlay(btv,fh,new);
+ up(&fh->lock);
+ return retval;
+ }
+
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *mbuf = arg;
+ int i;
+
+ if (!mmap)
+ return -EINVAL;
+ down(&fh->lock);
+ retval = videobuf_mmap_setup
+ (file,(struct videobuf_buffer**)fh->bufs,
+ sizeof(struct bttv_buffer),
+ gbuffers,gbufsize,V4L2_BUF_TYPE_CAPTURE,
+ release_buffer);
+ if (retval < 0)
+ goto fh_unlock_and_return;
+ memset(mbuf,0,sizeof(*mbuf));
+ mbuf->frames = gbuffers;
+ mbuf->size = gbuffers * gbufsize;
+ for (i = 0; i < gbuffers; i++)
+ mbuf->offsets[i] = i * gbufsize;
+ up(&fh->lock);
return 0;
}
- case VIDIOCGPICT:
+ case VIDIOCMCAPTURE:
{
- struct video_picture p=btv->picture;
- if(copy_to_user(arg, &p, sizeof(p)))
- return -EFAULT;
+ struct video_mmap *vm = arg;
+ struct bttv_buffer *buf;
+
+ if (vm->frame >= VIDEO_MAX_FRAME)
+ return -EINVAL;
+
+ down(&fh->lock);
+ retval = -EINVAL;
+ buf = fh->bufs[vm->frame];
+ if (NULL == buf)
+ goto fh_unlock_and_return;
+ if (0 == buf->vb.baddr)
+ goto fh_unlock_and_return;
+ if (buf->vb.state == STATE_QUEUED ||
+ buf->vb.state == STATE_ACTIVE)
+ goto fh_unlock_and_return;
+
+ retval = bttv_prepare_buffer(btv,buf,
+ format_by_palette(vm->format),
+ vm->width,vm->height,0);
+ if (0 != retval)
+ goto fh_unlock_and_return;
+ bttv_queue_buffer(btv,buf);
+ up(&fh->lock);
return 0;
}
- case VIDIOCSPICT:
+ case VIDIOCSYNC:
+ {
+ int *frame = arg;
+ struct bttv_buffer *buf;
+
+ if (*frame >= VIDEO_MAX_FRAME)
+ return -EINVAL;
+
+ down(&fh->lock);
+ retval = -EINVAL;
+ buf = fh->bufs[*frame];
+ if (NULL == buf)
+ goto fh_unlock_and_return;
+ retval = videobuf_waiton(&buf->vb,0,1);
+ if (0 != retval)
+ goto fh_unlock_and_return;
+ switch (buf->vb.state) {
+ case STATE_ERROR:
+ retval = -EIO;
+ /* fall through */
+ case STATE_DONE:
+ videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
+ bttv_dma_free(btv,buf);
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ up(&fh->lock);
+ return retval;
+ }
+
+ case BTTV_VERSION:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ case VIDIOCGCHAN:
+ case VIDIOCSCHAN:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return bttv_common_ioctls(btv,cmd,arg);
+
+
+#ifdef HAVE_V4L2
+ /* *** v4l2 *** ************************************************ */
+ case VIDIOC_QUERYCAP:
{
- struct video_picture p;
- if(copy_from_user(&p, arg,sizeof(p)))
- return -EFAULT;
- if (p.palette > PALETTEFMT_MAX)
+ struct v4l2_capability *cap = arg;
+
+ if (0 == v4l2)
return -EINVAL;
- down(&btv->lock);
- /* We want -128 to 127 we get 0-65535 */
- bt848_bright(btv, (p.brightness>>8)-128);
- /* 0-511 for the colour */
- bt848_sat_u(btv, p.colour>>7);
- bt848_sat_v(btv, ((p.colour>>7)*201L)/237);
- /* -128 to 127 */
- bt848_hue(btv, (p.hue>>8)-128);
- /* 0-511 */
- bt848_contrast(btv, p.contrast>>7);
- btv->picture = p;
- up(&btv->lock);
+ strcpy(cap->name,btv->video_dev.name);
+ cap->type = V4L2_TYPE_CAPTURE;
+ cap->flags = V4L2_FLAG_TUNER | V4L2_FLAG_PREVIEW
+ | V4L2_FLAG_READ | V4L2_FLAG_SELECT;
+ if (mmap)
+ cap->flags |= V4L2_FLAG_STREAMING;
+ cap->inputs = bttv_tvcards[btv->type].video_inputs;
+ cap->outputs = 0;
+ cap->audios = bttv_tvcards[btv->type].audio_inputs;
+ cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth;
+ cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
+ cap->minwidth = 48;
+ cap->minheight = 32;
+ cap->maxframerate = 30;
return 0;
}
- case VIDIOCSWIN:
- {
- struct video_window vw;
- struct video_clip *vcp = NULL;
-
- if(copy_from_user(&vw,arg,sizeof(vw)))
- return -EFAULT;
- down(&btv->lock);
- if(vw.flags || vw.width < 16 || vw.height < 16)
- {
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- btv->scr_on = 0;
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
- up(&btv->lock);
- return -EINVAL;
- }
- if (btv->win.bpp < 4)
- { /* adjust and align writes */
- vw.x = (vw.x + 3) & ~3;
- vw.width &= ~3;
+ case VIDIOC_ENUM_PIXFMT:
+ case VIDIOC_ENUM_FBUFFMT:
+ {
+ struct v4l2_fmtdesc *f = arg;
+ int i, index;
+
+ index = -1;
+ for (i = 0; i < BTTV_FORMATS; i++) {
+ if (bttv_formats[i].fourcc != -1)
+ index++;
+ if (index == f->index)
+ break;
}
- if (btv->needs_restart)
- bt848_restart(btv);
- btv->win.x=vw.x;
- btv->win.y=vw.y;
- btv->win.width=vw.width;
- btv->win.height=vw.height;
-
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- bt848_set_risc_jmps(btv,0);
- bt848_set_winsize(btv);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-
- /*
- * Do any clips.
- */
- if(vw.clipcount<0) {
- if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) {
- up(&btv->lock);
- return -ENOMEM;
- }
- if(copy_from_user(vcp, vw.clips,
- VIDEO_CLIPMAP_SIZE)) {
- up(&btv->lock);
- vfree(vcp);
- return -EFAULT;
- }
- } else if (vw.clipcount > 2048) {
- up(&btv->lock);
+ if (BTTV_FORMATS == i)
return -EINVAL;
- } else if (vw.clipcount) {
- if((vcp=vmalloc(sizeof(struct video_clip)*
- (vw.clipcount))) == NULL) {
- up(&btv->lock);
- return -ENOMEM;
- }
- if(copy_from_user(vcp,vw.clips,
- sizeof(struct video_clip)*
- vw.clipcount)) {
- up(&btv->lock);
- vfree(vcp);
- return -EFAULT;
- }
- }
- make_clip_tab(btv, vcp, vw.clipcount);
- if (vw.clipcount != 0)
- vfree(vcp);
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
- up(&btv->lock);
+ if (cmd == VIDIOC_ENUM_FBUFFMT &&
+ 0 == (bttv_formats[i].flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+ memset(f,0,sizeof(*f));
+ f->index = index;
+ strncpy(f->description,bttv_formats[i].name,31);
+ f->pixelformat = bttv_formats[i].fourcc;
+ f->depth = bttv_formats[i].depth;
return 0;
}
- case VIDIOCGWIN:
+
+ case VIDIOC_G_FMT:
{
- struct video_window vw;
- memset(&vw,0,sizeof(vw));
- vw.x=btv->win.x;
- vw.y=btv->win.y;
- vw.width=btv->win.width;
- vw.height=btv->win.height;
- if(btv->win.interlace)
- vw.flags|=VIDEO_WINDOW_INTERLACE;
- if(copy_to_user(arg,&vw,sizeof(vw)))
- return -EFAULT;
+ struct v4l2_format *f = arg;
+
+ memset(f,0,sizeof(*f));
+ f->type = V4L2_BUF_TYPE_CAPTURE;
+ f->fmt.pix.width = fh->buf.vb.width;
+ f->fmt.pix.height = fh->buf.vb.height;
+ f->fmt.pix.depth = fh->buf.fmt->depth;
+ f->fmt.pix.pixelformat = fh->buf.fmt->fourcc;
+ f->fmt.pix.sizeimage =
+ (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth)/8;
return 0;
}
- case VIDIOCCAPTURE:
+ case VIDIOC_S_FMT:
{
- int v;
- if(copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
- if(btv->win.vidadr == 0)
+ struct v4l2_format *f = arg;
+ const struct bttv_format *fmt;
+
+ if ((f->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+ return -EINVAL;
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+ if (f->fmt.pix.width < 48 ||
+ f->fmt.pix.height < 32)
return -EINVAL;
- if (btv->win.width==0 || btv->win.height==0)
+ if (f->fmt.pix.flags & V4L2_FMT_FLAG_BYTESPERLINE)
+ /* FIXME -- not implemented yet */
return -EINVAL;
- if (1 == no_overlay)
- return -EIO;
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- if (v == 1 && btv->win.vidadr != 0)
- btv->scr_on = 1;
- if (v == 0)
- btv->scr_on = 0;
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+
+ down(&fh->lock);
+ /* fixup format */
+ if (f->fmt.pix.width > bttv_tvnorms[btv->tvnorm].swidth)
+ f->fmt.pix.width = bttv_tvnorms[btv->tvnorm].swidth;
+ if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight)
+ f->fmt.pix.height = bttv_tvnorms[btv->tvnorm].sheight;
+ if (!(f->fmt.pix.flags & V4L2_FMT_FLAG_INTERLACED) &&
+ f->fmt.pix.height>bttv_tvnorms[btv->tvnorm].sheight/2)
+ f->fmt.pix.height=bttv_tvnorms[btv->tvnorm].sheight/2;
+
+ if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight/2) {
+ /* must interlace -- no field splitting available */
+ f->fmt.pix.flags &= ~(V4L2_FMT_FLAG_TOPFIELD|
+ V4L2_FMT_FLAG_BOTFIELD);
+ } else {
+ /* one field is enouth -- no interlace needed */
+ f->fmt.pix.flags &= ~V4L2_FMT_FLAG_INTERLACED;
+ }
+
+ /* update our state informations */
+ fh->buf.fmt = fmt;
+ fh->buf.vb.width = f->fmt.pix.width;
+ fh->buf.vb.height = f->fmt.pix.height;
+ btv->init.buf.fmt = fmt;
+ btv->init.buf.vb.width = f->fmt.pix.width;
+ btv->init.buf.vb.height = f->fmt.pix.height;
+
+ /* update data for the application */
+ f->fmt.pix.depth = fmt->depth;
+ f->fmt.pix.sizeimage =
+ (fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8;
+ up(&fh->lock);
return 0;
}
- case VIDIOCGFBUF:
+
+ case VIDIOC_G_FBUF:
{
- struct video_buffer v;
- v.base=(void *)btv->win.vidadr;
- v.height=btv->win.sheight;
- v.width=btv->win.swidth;
- v.depth=btv->win.depth;
- v.bytesperline=btv->win.bpl;
- if(copy_to_user(arg, &v,sizeof(v)))
- return -EFAULT;
+ struct v4l2_framebuffer *fb = arg;
+
+ memset(fb,0,sizeof(*fb));
+ fb->base[0] = btv->fbuf.base;
+ fb->fmt.width = btv->fbuf.width;
+ fb->fmt.height = btv->fbuf.height;
+ fb->fmt.bytesperline = btv->fbuf.bytesperline;
+ fb->fmt.flags = V4L2_FMT_FLAG_BYTESPERLINE;
+ fb->capability = V4L2_FBUF_CAP_CLIPPING;
+ if (fh->ovfmt) {
+ fb->fmt.depth = fh->ovfmt->depth;
+ fb->fmt.pixelformat = fh->ovfmt->fourcc;
+ }
return 0;
-
}
- case VIDIOCSFBUF:
+ case VIDIOC_S_FBUF:
{
- struct video_buffer v;
+ struct v4l2_framebuffer *fb = arg;
+ const struct bttv_format *fmt;
+ unsigned long end;
+
if(!capable(CAP_SYS_ADMIN) &&
!capable(CAP_SYS_RAWIO))
return -EPERM;
- if(copy_from_user(&v, arg,sizeof(v)))
- return -EFAULT;
- if(v.depth!=8 && v.depth!=15 && v.depth!=16 &&
- v.depth!=24 && v.depth!=32 && v.width > 16 &&
- v.height > 16 && v.bytesperline > 16)
+
+ /* check args */
+ end = (unsigned long)fb->base[0] +
+ fb->fmt.height * fb->fmt.bytesperline;
+ if (0 == find_videomem((unsigned long)fb->base[0],end))
return -EINVAL;
- down(&btv->lock);
- if (v.base)
- btv->win.vidadr=(unsigned long)v.base;
- btv->win.sheight=v.height;
- btv->win.swidth=v.width;
- btv->win.bpp=((v.depth+7)&0x38)/8;
- btv->win.depth=v.depth;
- btv->win.bpl=v.bytesperline;
-
-#if 0 /* was broken for ages and nobody noticed. Looks like we don't need
- it any more as everybody explicitly sets the palette using VIDIOCSPICT
- these days */
- /* set sefault color format */
- switch (v.depth) {
- case 8: btv->picture.palette = VIDEO_PALETTE_HI240; break;
- case 15: btv->picture.palette = VIDEO_PALETTE_RGB555; break;
- case 16: btv->picture.palette = VIDEO_PALETTE_RGB565; break;
- case 24: btv->picture.palette = VIDEO_PALETTE_RGB24; break;
- case 32: btv->picture.palette = VIDEO_PALETTE_RGB32; break;
+
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+ if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ down(&fh->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;
}
-#endif
-
- if (bttv_debug)
- printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
- v.base, v.width,v.height, btv->win.bpp, btv->win.bpl);
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- bt848_set_winsize(btv);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
- up(&btv->lock);
- return 0;
+
+ /* ok, accept it */
+ btv->fbuf.base = fb->base[0];
+ btv->fbuf.width = fb->fmt.width;
+ btv->fbuf.height = fb->fmt.height;
+ btv->fbuf.depth = fmt->depth;
+ if (fb->fmt.flags & V4L2_FMT_FLAG_BYTESPERLINE)
+ btv->fbuf.bytesperline = fb->fmt.bytesperline;
+ else
+ btv->fbuf.bytesperline = btv->fbuf.width*fmt->depth/8;
+
+ retval = 0;
+ fh->ovfmt = fmt;
+ btv->init.ovfmt = fmt;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ fh->ov.x = 0;
+ fh->ov.y = 0;
+ fh->ov.width = fb->fmt.width;
+ fh->ov.height = fb->fmt.height;
+ btv->init.ov.width = fb->fmt.width;
+ btv->init.ov.height = fb->fmt.height;
+ if (fh->ov.clips)
+ kfree(fh->ov.clips);
+ fh->ov.clips = NULL;
+ fh->ov.nclips = 0;
+
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_alloc(sizeof(*new),
+ V4L2_BUF_TYPE_CAPTURE);
+ bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
+ retval = bttv_switch_overlay(btv,fh,new);
+ }
+ }
+ up(&fh->lock);
+ return retval;
}
- case VIDIOCKEY:
+ case VIDIOC_G_WIN:
{
- /* Will be handled higher up .. */
+ struct v4l2_window *win = arg;
+
+ memset(win,0,sizeof(*win));
+ win->x = fh->ov.x;
+ win->y = fh->ov.y;
+ win->width = fh->ov.width;
+ win->height = fh->ov.height;
return 0;
}
- case VIDIOCGFREQ:
+ case VIDIOC_S_WIN:
{
- unsigned long v=btv->win.freq;
- if(copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- return 0;
+ struct v4l2_window *win = arg;
+
+ return setup_window(fh,btv,win->x,win->y,
+ win->width,win->height,
+ (struct video_clip*)win->clips,
+ win->clipcount);
}
- case VIDIOCSFREQ:
+
+ case VIDIOC_REQBUFS:
{
- unsigned long v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- btv->win.freq=v;
- bttv_call_i2c_clients(btv,cmd,&v);
-#if 1
- if (btv->radio && btv->has_matchbox)
- tea5757_set_freq(btv,v);
-#endif
+ struct v4l2_requestbuffers *req = arg;
+ int size,count;
+
+ if (!mmap)
+ return -EINVAL;
+ if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+ return -EINVAL;
+ if (req->count < 0)
+ return -EINVAL;
+
+ down(&fh->lock);
+ retval = -EINVAL;
+ if (NULL == fh->buf.fmt ||
+ 0 == fh->buf.vb.width ||
+ 0 == fh->buf.vb.height)
+ goto fh_unlock_and_return;
+
+ size = (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth) >> 3;
+ size = (size + PAGE_SIZE - 1) & PAGE_MASK;
+ count = req->count;
+ if (count > VIDEO_MAX_FRAME)
+ count = VIDEO_MAX_FRAME;
+ while (size * count > gbuffers * gbufsize)
+ count--;
+ retval = videobuf_mmap_setup(file,
+ (struct videobuf_buffer**)fh->bufs,
+ sizeof(struct bttv_buffer),
+ count,size,V4L2_BUF_TYPE_CAPTURE,
+ release_buffer);
+ if (retval < 0)
+ goto fh_unlock_and_return;
+ req->type = V4L2_BUF_TYPE_CAPTURE;
+ req->count = count;
+ up(&fh->lock);
return 0;
}
-
- case VIDIOCGAUDIO:
+ case VIDIOC_QUERYBUF:
{
- struct video_audio v;
-
- v=btv->audio_dev;
- v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE);
- v.flags|=VIDEO_AUDIO_MUTABLE;
- strcpy(v.name,"TV");
-
- v.mode = VIDEO_SOUND_MONO;
- bttv_call_i2c_clients(btv,cmd,&v);
-
- /* card specific hooks */
- if (bttv_tvcards[btv->type].audio_hook)
- bttv_tvcards[btv->type].audio_hook(btv,&v,0);
+ struct v4l2_buffer *b = arg;
- if(copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
+ if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+ return -EINVAL;
+ if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
+ return -EINVAL;
+ if (NULL == fh->bufs[b->index])
+ return -EINVAL;
+ videobuf_status(b,&fh->bufs[b->index]->vb);
return 0;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_QBUF:
{
- struct video_audio v;
+ struct v4l2_buffer *b = arg;
+ struct bttv_buffer *buf;
+ int field = 0;
- if(copy_from_user(&v,arg, sizeof(v)))
- return -EFAULT;
- down(&btv->lock);
- if(v.flags&VIDEO_AUDIO_MUTE)
- audio(btv, AUDIO_MUTE, 1);
- /* One audio source per tuner -- huh? <GA> */
- if(v.audio<0 || v.audio >= bttv_tvcards[btv->type].audio_inputs) {
- up(&btv->lock);
+ if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+ return -EINVAL;
+ if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
return -EINVAL;
- }
- /* bt848_muxsel(btv,v.audio); */
- if(!(v.flags&VIDEO_AUDIO_MUTE))
- audio(btv, AUDIO_UNMUTE, 1);
-
- bttv_call_i2c_clients(btv,cmd,&v);
-
- /* card specific hooks */
- if (bttv_tvcards[btv->type].audio_hook)
- bttv_tvcards[btv->type].audio_hook(btv,&v,1);
- btv->audio_dev=v;
- up(&btv->lock);
+ down(&fh->lock);
+ retval = -EINVAL;
+ buf = fh->bufs[b->index];
+ if (NULL == buf)
+ goto fh_unlock_and_return;
+ if (0 == buf->vb.baddr)
+ goto fh_unlock_and_return;
+ if (buf->vb.state == STATE_QUEUED ||
+ buf->vb.state == STATE_ACTIVE)
+ goto fh_unlock_and_return;
+
+ if (b->flags & V4L2_BUF_FLAG_TOPFIELD)
+ field |= VBUF_FIELD_ODD;
+ if (b->flags & V4L2_BUF_FLAG_BOTFIELD)
+ field |= VBUF_FIELD_EVEN;
+ retval = bttv_prepare_buffer(btv,buf,fh->buf.fmt,
+ fh->buf.vb.width,fh->buf.vb.height,field);
+ if (0 != retval)
+ goto fh_unlock_and_return;
+
+ list_add_tail(&buf->vb.stream,&fh->stream);
+ if (check_btres(fh, RESOURCE_STREAMING))
+ bttv_queue_buffer(btv,buf);
+ up(&fh->lock);
return 0;
}
-
- case VIDIOCSYNC:
+ case VIDIOC_DQBUF:
{
- DECLARE_WAITQUEUE(wait, current);
+ struct v4l2_buffer *b = arg;
+ struct bttv_buffer *buf;
- if(copy_from_user((void *)&i,arg,sizeof(int)))
- return -EFAULT;
- if (i < 0 || i >= gbuffers)
+ if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
return -EINVAL;
- switch (btv->gbuf[i].stat) {
- case GBUFFER_UNUSED:
- ret = -EINVAL;
+
+ down(&fh->lock);
+ retval = -EINVAL;
+ if (list_empty(&fh->stream))
+ goto fh_unlock_and_return;
+ buf = list_entry(fh->stream.next, struct bttv_buffer, vb.stream);
+ retval = videobuf_waiton(&buf->vb,0,1);
+ if (retval < 0)
+ goto fh_unlock_and_return;
+ switch (buf->vb.state) {
+ case STATE_ERROR:
+ retval = -EIO;
+ /* fall through */
+ case STATE_DONE:
+ videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
+ buf->vb.state = STATE_IDLE;
break;
- case GBUFFER_GRABBING:
- add_wait_queue(&btv->capq, &wait);
- current->state = TASK_INTERRUPTIBLE;
- while(btv->gbuf[i].stat==GBUFFER_GRABBING) {
- if (bttv_debug)
- printk("bttv%d: cap sync: sleep on %d\n",btv->nr,i);
- schedule();
- if(signal_pending(current)) {
- remove_wait_queue(&btv->capq, &wait);
- current->state = TASK_RUNNING;
- return -EINTR;
- }
- }
- remove_wait_queue(&btv->capq, &wait);
- current->state = TASK_RUNNING;
- /* fall throuth */
- case GBUFFER_DONE:
- case GBUFFER_ERROR:
- ret = (btv->gbuf[i].stat == GBUFFER_ERROR) ? -EIO : 0;
- if (bttv_debug)
- printk("bttv%d: cap sync: buffer %d, retval %d\n",btv->nr,i,ret);
- btv->gbuf[i].stat = GBUFFER_UNUSED;
- }
- if (btv->needs_restart) {
- down(&btv->lock);
- bt848_restart(btv);
- up(&btv->lock);
+ default:
+ retval = -EINVAL;
+ goto fh_unlock_and_return;
}
- return ret;
- }
-
- case BTTV_FIELDNR:
- if(copy_to_user((void *) arg, (void *) &btv->last_field,
- sizeof(btv->last_field)))
- return -EFAULT;
- break;
-
- case BTTV_PLLSET: {
- struct bttv_pll_info p;
- if(!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if(copy_from_user(&p , (void *) arg, sizeof(btv->pll)))
- return -EFAULT;
- down(&btv->lock);
- btv->pll.pll_ifreq = p.pll_ifreq;
- btv->pll.pll_ofreq = p.pll_ofreq;
- btv->pll.pll_crystal = p.pll_crystal;
- up(&btv->lock);
- break;
- }
-
- case VIDIOCMCAPTURE:
- {
- struct video_mmap vm;
- int ret;
- if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm)))
- return -EFAULT;
- down(&btv->lock);
- ret = vgrab(btv, &vm);
- up(&btv->lock);
- return ret;
+ list_del(&buf->vb.stream);
+ memset(b,0,sizeof(*b));
+ videobuf_status(b,&buf->vb);
+ up(&fh->lock);
+ return retval;
}
-
- case VIDIOCGMBUF:
+ case VIDIOC_STREAMON:
{
- struct video_mbuf vm;
- memset(&vm, 0 , sizeof(vm));
- vm.size=gbufsize*gbuffers;
- vm.frames=gbuffers;
- for (i = 0; i < gbuffers; i++)
- vm.offsets[i]=i*gbufsize;
- if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
- return -EFAULT;
- return 0;
- }
+ struct list_head *list;
+ struct bttv_buffer *buf;
- case VIDIOCGUNIT:
- {
- struct video_unit vu;
- vu.video=btv->video_dev.minor;
- vu.vbi=btv->vbi_dev.minor;
- if(btv->radio_dev.minor!=-1)
- vu.radio=btv->radio_dev.minor;
- else
- vu.radio=VIDEO_NO_UNIT;
- vu.audio=VIDEO_NO_UNIT;
- vu.teletext=VIDEO_NO_UNIT;
- if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu)))
- return -EFAULT;
+ if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING))
+ return -EBUSY;
+ bttv_field_count(btv);
+ down(&fh->lock);
+ list_for_each(list,&fh->stream) {
+ buf = list_entry(list, struct bttv_buffer, vb.stream);
+ if (buf->vb.state == STATE_PREPARED)
+ bttv_queue_buffer(btv,buf);
+ }
+ up(&fh->lock);
return 0;
}
-
- case BTTV_BURST_ON:
+ case VIDIOC_STREAMOFF:
{
- burst(1);
+ down(&fh->lock);
+ retval = -EINVAL;
+ if (!check_btres(fh, RESOURCE_STREAMING))
+ goto fh_unlock_and_return;
+ bttv_stop_streaming(btv,fh);
+ up(&fh->lock);
return 0;
}
- case BTTV_BURST_OFF:
+ case VIDIOC_QUERYCTRL:
{
- burst(0);
+ struct v4l2_queryctrl *c = arg;
+ int i;
+
+ v4l2_fill_ctrl_category(c);
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+ for (i = 0; i < BTTV_CTLS; i++)
+ if (bttv_ctls[i].id == c->id)
+ break;
+ if (i == BTTV_CTLS) {
+ *c = no_ctl;
+ return 0;
+ }
+ *c = bttv_ctls[i];
+ if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+ struct video_audio va;
+ memset(&va,0,sizeof(va));
+ bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+ if (bttv_tvcards[btv->type].audio_hook)
+ bttv_tvcards[btv->type].audio_hook(btv,&va,0);
+ switch (bttv_ctls[i].id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ if (!(va.flags & VIDEO_AUDIO_VOLUME))
+ *c = no_ctl;
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ if (!(va.flags & VIDEO_AUDIO_BALANCE))
+ *c = no_ctl;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ if (!(va.flags & VIDEO_AUDIO_BASS))
+ *c = no_ctl;
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(va.flags & VIDEO_AUDIO_TREBLE))
+ *c = no_ctl;
+ break;
+ }
+ }
return 0;
}
-
- case BTTV_VERSION:
- {
- return BTTV_VERSION_CODE;
- }
-
- case BTTV_PICNR:
+ case VIDIOC_G_CTRL:
+ return get_control(btv,arg);
+ case VIDIOC_S_CTRL:
+ return set_control(btv,arg);
+ case VIDIOC_G_PARM:
{
- /* return picture;*/
- return 0;
+ struct v4l2_streamparm *parm = arg;
+ struct v4l2_standard s;
+ if (parm->type != V4L2_BUF_TYPE_CAPTURE)
+ return -EINVAL;
+ memset(parm,0,sizeof(*parm));
+ v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, 0);
+ parm->parm.capture.timeperframe = v4l2_video_std_tpf(&s);
+ return 0;
}
+
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQ:
+ case VIDIOC_S_FREQ:
+ return bttv_common_ioctls(btv,cmd,arg);
+#endif /* HAVE_V4L2 */
default:
return -ENOIOCTLCMD;
}
return 0;
-}
-/*
- * This maps the vmalloced and reserved fbuffer to user space.
- *
- * FIXME:
- * - PAGE_READONLY should suffice!?
- * - remap_page_range is kind of inefficient for page by page remapping.
- * But e.g. pte_alloc() does not work in modules ... :-(
- */
+ fh_unlock_and_return:
+ up(&fh->lock);
+ return retval;
+}
-static int do_bttv_mmap(struct vm_area_struct *vma, struct bttv *btv, const char *adr, unsigned long size)
+/* start capture to a kernel bounce buffer */
+static int bttv_read_capture(struct bttv_fh *fh)
{
- unsigned long start=(unsigned long) adr;
- unsigned long page,pos;
-
- if (size>gbuffers*gbufsize)
- return -EINVAL;
- if (!btv->fbuffer) {
- if(fbuffer_alloc(btv))
- return -EINVAL;
- }
- pos=(unsigned long) btv->fbuffer;
- while (size > 0) {
- page = kvirt_to_pa(pos);
- if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
- start+=PAGE_SIZE;
- pos+=PAGE_SIZE;
- size-=PAGE_SIZE;
- }
- return 0;
+ struct bttv *btv = fh->btv;
+ int rc;
+
+ dprintk("bttv%d: read start\n",btv->nr);
+ rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,
+ fh->buf.vb.width,fh->buf.vb.height,0);
+ if (0 != rc)
+ return rc;
+
+ bttv_queue_buffer(btv,&fh->read_buf);
+ fh->read_off = 0;
+ return 0;
}
-static int bttv_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size)
+/*
+ * blocking read for a complete video frame
+ * => no kernel bounce buffer needed.
+ */
+static ssize_t
+bttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv,
+ char *data,size_t count, loff_t *ppos)
{
- struct bttv *btv=(struct bttv *)dev;
- int r;
-
- down(&btv->lock);
- r=do_bttv_mmap(vma, btv, adr, size);
- up(&btv->lock);
- return r;
+ int rc;
+
+ /* setup stuff */
+ dprintk("bttv%d: read zerocopy\n",btv->nr);
+ fh->read_buf.vb.baddr = (unsigned long)data;
+ fh->read_buf.vb.bsize = count;
+ rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,
+ fh->buf.vb.width,fh->buf.vb.height,0);
+ if (0 != rc)
+ goto done;
+
+ /* start capture & wait */
+ bttv_queue_buffer(btv,&fh->read_buf);
+ rc = videobuf_waiton(&fh->read_buf.vb,0,1);
+ if (0 == rc) {
+ videobuf_dma_pci_sync(btv->dev,&fh->read_buf.vb.dma);
+ rc = fh->read_buf.vb.size;
+ }
+
+ done:
+ /* cleanup */
+ bttv_dma_free(btv,&fh->read_buf);
+ fh->read_buf.vb.baddr = 0;
+ fh->read_buf.vb.size = 0;
+ return rc;
}
-static struct video_device bttv_template=
+static ssize_t bttv_read(struct file *file, char *data,
+ size_t count, loff_t *ppos)
{
- owner: THIS_MODULE,
- name: "UNSET",
- type: VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
- hardware: VID_HARDWARE_BT848,
- open: bttv_open,
- close: bttv_close,
- read: bttv_read,
- write: bttv_write,
- ioctl: bttv_ioctl,
- mmap: bttv_mmap,
- minor: -1,
-};
-
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ int rc, bytes, size;
-static long vbi_read(struct video_device *v, char *buf, unsigned long count,
- int nonblock)
-{
- struct bttv *btv=(struct bttv *)(v-2);
- int q,todo;
- DECLARE_WAITQUEUE(wait, current);
+ if (btv->errors)
+ bttv_reinit_bt848(btv);
+ if (locked_btres(btv,RESOURCE_STREAMING))
+ return -EBUSY;
- todo=count;
- while (todo && todo>(q=VBIBUF_SIZE-btv->vbip))
- {
- if (btv->needs_restart) {
- down(&btv->lock);
- bt848_restart(btv);
- up(&btv->lock);
- }
- if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
- return -EFAULT;
- todo-=q;
- buf+=q;
+ down(&fh->lock);
+ size = fh->buf.fmt->depth*fh->buf.vb.width*fh->buf.vb.height >> 3;
+ if (-1 == fh->read_off && count >= size &&
+ !(file->f_flags & O_NONBLOCK)) {
+ rc = bttv_read_zerocopy(fh,btv,data,count,ppos);
+ if (rc >= 0)
+ /* ok, all done */
+ goto unlock_out;
+ /* fallback to kernel bounce buffer on failures */
+ }
+
+ if (-1 == fh->read_off) {
+ /* need to capture a new frame */
+ rc = bttv_read_capture(fh);
+ if (0 != rc)
+ goto unlock_out;
+ }
+
+ /* wait until capture is done */
+ rc = videobuf_waiton(&fh->read_buf.vb, file->f_flags & O_NONBLOCK,1);
+ if (0 != rc)
+ goto unlock_out;
+
+ /* copy to userspace */
+ bytes = count;
+ if (bytes > fh->read_buf.vb.size - fh->read_off)
+ bytes = fh->read_buf.vb.size - fh->read_off;
+ rc = -EFAULT;
+ if (copy_to_user(data,fh->read_buf.vb.dma.vmalloc +
+ fh->read_off,bytes))
+ goto unlock_out;
+
+ rc = bytes;
+ fh->read_off += bytes;
+ dprintk("bttv%d: read %d bytes\n",btv->nr,bytes);
+ if (fh->read_off == fh->read_buf.vb.size) {
+ /* all data copied, cleanup */
+ dprintk("bttv%d: read done\n",btv->nr);
+ bttv_dma_free(btv,&fh->read_buf);
+ fh->read_off = -1;
+ }
+
+ unlock_out:
+ up(&fh->lock);
+ return rc;
+}
- add_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_INTERRUPTIBLE;
- if (todo && q==VBIBUF_SIZE-btv->vbip)
- {
- if(nonblock)
- {
- remove_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_RUNNING;
- if(count==todo)
- return -EWOULDBLOCK;
- return count-todo;
- }
- schedule();
- if(signal_pending(current))
- {
- remove_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_RUNNING;
- if(todo==count)
- return -EINTR;
- else
- return count-todo;
+static unsigned int bttv_poll(struct file *file, poll_table *wait)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv_buffer *buf;
+
+ if (check_alloc_btres(fh->btv,fh,RESOURCE_STREAMING)) {
+ /* streaming capture */
+ if (list_empty(&fh->stream))
+ return POLLERR;
+ buf = list_entry(fh->stream.next,struct bttv_buffer,vb.stream);
+ } else {
+ /* read() capture */
+ down(&fh->lock);
+ if (-1 == fh->read_off) {
+ /* need to capture a new frame */
+ if (locked_btres(fh->btv,RESOURCE_STREAMING) ||
+ 0 != bttv_read_capture(fh)) {
+ up(&fh->lock);
+ return POLLERR;
}
}
- remove_wait_queue(&btv->vbiq, &wait);
- current->state = TASK_RUNNING;
- }
- if (todo)
- {
- if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
- return -EFAULT;
- btv->vbip+=todo;
+ up(&fh->lock);
+ buf = &fh->read_buf;
}
- return count;
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == STATE_DONE ||
+ buf->vb.state == STATE_ERROR)
+ return POLLIN|POLLRDNORM;
+ return 0;
}
-static unsigned int vbi_poll(struct video_device *dev, struct file *file,
- poll_table *wait)
+static int bttv_open(struct inode *inode, struct file *file)
{
- struct bttv *btv=(struct bttv *)(dev-2);
- unsigned int mask = 0;
-
- poll_wait(file, &btv->vbiq, wait);
+ unsigned int minor = minor(inode->i_rdev);
+ struct bttv *btv = NULL;
+ struct bttv_fh *fh;
+ int i;
- if (btv->vbip < VBIBUF_SIZE)
- mask |= (POLLIN | POLLRDNORM);
+ dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
- return mask;
-}
+ for (i = 0; i < bttv_num; i++) {
+ if (bttvs[i].video_dev.minor == minor) {
+ btv = &bttvs[i];
+ break;
+ }
+ }
+ if (NULL == btv)
+ return -ENODEV;
-static int vbi_open(struct video_device *dev, int flags)
-{
- struct bttv *btv=(struct bttv *)(dev-2);
- unsigned long irq_flags;
+ dprintk(KERN_DEBUG "bttv%d: open called (video)\n",btv->nr);
- down(&btv->lock);
- if (btv->needs_restart)
- bt848_restart(btv);
- set_pll(btv);
- btv->vbip=VBIBUF_SIZE;
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- btv->vbi_on = 1;
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
- up(&btv->lock);
+ /* allocate per filehandle data */
+ fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ *fh = btv->init;
+ init_MUTEX(&fh->lock);
+ INIT_LIST_HEAD(&fh->stream);
+ init_waitqueue_head(&fh->read_buf.vb.done);
+ fh->read_off = -1;
- return 0;
+ i2c_vidiocschan(btv);
+ return 0;
}
-static void vbi_close(struct video_device *dev)
+static int bttv_release(struct inode *inode, struct file *file)
{
- struct bttv *btv=(struct bttv *)(dev-2);
- unsigned long irq_flags;
-
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- btv->vbi_on = 0;
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+
+ /* turn off overlay, stop outstanding captures */
+ if (check_btres(fh, RESOURCE_OVERLAY))
+ bttv_switch_overlay(btv,fh,NULL);
+ if (check_btres(fh, RESOURCE_STREAMING))
+ bttv_stop_streaming(btv,fh);
+ bttv_dma_free(btv,&fh->read_buf);
+
+ file->private_data = NULL;
+ kfree(fh);
+ return 0;
}
-static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static int
+bttv_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct bttv *btv=(struct bttv *)(dev-2);
+ struct bttv_fh *fh = file->private_data;
+ int err;
- switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability b;
- strcpy(b.name,btv->vbi_dev.name);
- b.type = ((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) |
- VID_TYPE_TELETEXT;
- b.channels = 0;
- b.audios = 0;
- b.maxwidth = 0;
- b.maxheight = 0;
- b.minwidth = 0;
- b.minheight = 0;
- if(copy_to_user(arg,&b,sizeof(b)))
- return -EFAULT;
- return 0;
- }
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
- case VIDIOCGCHAN:
- case VIDIOCSCHAN:
- case BTTV_VERSION:
- return bttv_ioctl(dev-2,cmd,arg);
- case BTTV_VBISIZE:
- /* make alevt happy :-) */
- return VBIBUF_SIZE;
- default:
+ if (!mmap)
return -EINVAL;
- }
+ down(&fh->lock);
+ err = videobuf_mmap_mapper(vma,(struct videobuf_buffer**)fh->bufs);
+ up(&fh->lock);
+ return err;
}
-static struct video_device vbi_template=
+static struct file_operations bttv_fops =
+{
+ owner: THIS_MODULE,
+ open: bttv_open,
+ release: bttv_release,
+ ioctl: video_generic_ioctl,
+ llseek: no_llseek,
+ read: bttv_read,
+ mmap: bttv_mmap,
+ poll: bttv_poll,
+};
+
+static struct video_device bttv_template =
{
- owner: THIS_MODULE,
- name: "bttv vbi",
- type: VID_TYPE_CAPTURE|VID_TYPE_TELETEXT,
- hardware: VID_HARDWARE_BT848,
- open: vbi_open,
- close: vbi_close,
- read: vbi_read,
- write: bttv_write,
- poll: vbi_poll,
- ioctl: vbi_ioctl,
- minor: -1,
+ name: "UNSET",
+ type: VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY|
+ VID_TYPE_CLIPPING|VID_TYPE_SCALES,
+ hardware: VID_HARDWARE_BT848,
+ fops: &bttv_fops,
+ kernel_ioctl: bttv_ioctl,
+ minor: -1,
};
+/* ----------------------------------------------------------------------- */
+/* radio interface */
-static int radio_open(struct video_device *dev, int flags)
+static int radio_open(struct inode *inode, struct file *file)
{
- struct bttv *btv = (struct bttv *)(dev-1);
- unsigned long v;
+ unsigned int minor = minor(inode->i_rdev);
+ struct bttv *btv = NULL;
+ unsigned long v = 400*16;
+ int i;
- down(&btv->lock);
- if (btv->user)
- goto busy_unlock;
- btv->user++;
+ dprintk("bttv: open minor=%d\n",minor);
- btv->radio = 1;
- v = 400*16;
- bttv_call_i2c_clients(btv,VIDIOCSFREQ,&v);
- bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
- bt848_muxsel(btv,0);
- up(&btv->lock);
+ for (i = 0; i < bttv_num; i++) {
+ if (bttvs[i].radio_dev.minor == minor) {
+ btv = &bttvs[i];
+ break;
+ }
+ }
+ if (NULL == btv)
+ return -ENODEV;
+
+ dprintk("bttv%d: open called (radio)\n",btv->nr);
+ down(&btv->lock);
+ if (btv->radio_user) {
+ up(&btv->lock);
+ return -EBUSY;
+ }
+ btv->radio_user++;
+ file->private_data = btv;
- return 0;
+ i2c_vidiocschan(btv);
+ bttv_call_i2c_clients(btv,VIDIOCSFREQ,&v);
+ bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
+ audio_mux(btv,AUDIO_RADIO);
- busy_unlock:
up(&btv->lock);
- return -EBUSY;
+ return 0;
}
-static void radio_close(struct video_device *dev)
+static int radio_release(struct inode *inode, struct file *file)
{
- struct bttv *btv=(struct bttv *)(dev-1);
+ struct bttv *btv = file->private_data;
- down(&btv->lock);
- btv->user--;
- btv->radio = 0;
- up(&btv->lock);
+ btv->radio_user--;
+ return 0;
}
-static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+static int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
{
- return -EINVAL;
-}
+ struct bttv *btv = file->private_data;
-static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
-{
- struct bttv *btv=(struct bttv *)(dev-1);
- switch (cmd) {
+ switch (cmd) {
case VIDIOCGCAP:
{
- struct video_capability v;
- strcpy(v.name,btv->video_dev.name);
- v.type = VID_TYPE_TUNER;
- v.channels = 1;
- v.audios = 1;
- /* No we don't do pictures */
- v.maxwidth = 0;
- v.maxheight = 0;
- v.minwidth = 0;
- v.minheight = 0;
- if (copy_to_user(arg, &v, sizeof(v)))
- return -EFAULT;
- return 0;
- break;
- }
- case VIDIOCGTUNER:
- {
- struct video_tuner v;
- if(copy_from_user(&v,arg,sizeof(v))!=0)
- return -EFAULT;
- if(v.tuner||btv->channel) /* Only tuner 0 */
- return -EINVAL;
- strcpy(v.name, "Radio");
- /* japan: 76.0 MHz - 89.9 MHz
- western europe: 87.5 MHz - 108.0 MHz
- russia: 65.0 MHz - 108.0 MHz */
- v.rangelow=(int)(65*16);
- v.rangehigh=(int)(108*16);
- v.flags= 0; /* XXX */
- v.mode = 0; /* XXX */
- bttv_call_i2c_clients(btv,cmd,&v);
- if(copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- return 0;
+ struct video_capability *cap = arg;
+
+ memset(cap,0,sizeof(*cap));
+ strcpy(cap->name,btv->radio_dev.name);
+ cap->type = VID_TYPE_TUNER;
+ cap->channels = 1;
+ cap->audios = 1;
+ return 0;
}
- case VIDIOCSTUNER:
- {
- struct video_tuner v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- /* Only channel 0 has a tuner */
- if(v.tuner!=0 || btv->channel)
- return -EINVAL;
- /* XXX anything to do ??? */
+
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+
+ if(v->tuner)
+ return -EINVAL;
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "Radio");
+ /* japan: 76.0 MHz - 89.9 MHz
+ western europe: 87.5 MHz - 108.0 MHz
+ russia: 65.0 MHz - 108.0 MHz */
+ v->rangelow=(int)(65*16);
+ v->rangehigh=(int)(108*16);
+ bttv_call_i2c_clients(btv,cmd,v);
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ /* nothing to do */
return 0;
- }
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
+
+ case BTTV_VERSION:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
- bttv_ioctl((struct video_device *)btv,cmd,arg);
- break;
+ return bttv_common_ioctls(btv,cmd,arg);
+
default:
return -ENOIOCTLCMD;
}
return 0;
}
-static struct video_device radio_template=
+static struct file_operations radio_fops =
{
- owner: THIS_MODULE,
- name: "bttv radio",
- type: VID_TYPE_TUNER,
- hardware: VID_HARDWARE_BT848,
- open: radio_open,
- close: radio_close,
- read: radio_read, /* just returns -EINVAL */
- write: bttv_write, /* just returns -EINVAL */
- ioctl: radio_ioctl,
- minor: -1,
+ owner: THIS_MODULE,
+ open: radio_open,
+ release: radio_release,
+ ioctl: video_generic_ioctl,
+ llseek: no_llseek,
};
-
-static void bt848_set_risc_jmps(struct bttv *btv, int flags)
+static struct video_device radio_template =
{
- if (-1 == flags) {
- /* defaults */
- flags = 0;
- if (btv->scr_on)
- flags |= 0x03;
- if (btv->vbi_on)
- flags |= 0x0c;
- }
-
- if (bttv_debug > 1)
- printk("bttv%d: set_risc_jmp %08lx:",
- btv->nr,virt_to_bus(btv->risc_jmp));
-
- /* Sync to start of odd field */
- btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC
- |BT848_FIFO_STATUS_VRE);
- btv->risc_jmp[1]=cpu_to_le32(0);
-
- /* Jump to odd vbi sub */
- btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20));
- if (flags&8) {
- if (bttv_debug > 1)
- printk(" ev=%08lx",virt_to_bus(btv->vbi_odd));
- btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd));
- } else {
- if (bttv_debug > 1)
- printk(" -----------");
- btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->risc_jmp+4));
- }
-
- /* Jump to odd sub */
- btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0xe<<20));
- if (0 != btv->risc_cap_odd) {
- if (bttv_debug > 1)
- printk(" e%d=%08x",btv->gq_grab,btv->risc_cap_odd);
- flags |= 3;
- btv->risc_jmp[5]=cpu_to_le32(btv->risc_cap_odd);
- } else if ((flags&2) &&
- (!btv->win.interlace || 0 == btv->risc_cap_even)) {
- if (bttv_debug > 1)
- printk(" eo=%08lx",virt_to_bus(btv->risc_scr_odd));
- btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_scr_odd));
- } else {
- if (bttv_debug > 1)
- printk(" -----------");
- btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_jmp+6));
- }
+ name: "bt848/878 radio",
+ type: VID_TYPE_TUNER|VID_TYPE_TELETEXT,
+ hardware: VID_HARDWARE_BT848,
+ fops: &radio_fops,
+ kernel_ioctl: radio_ioctl,
+ minor: -1,
+};
+/* ----------------------------------------------------------------------- */
+/* irq handler */
- /* Sync to start of even field */
- btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC
- |BT848_FIFO_STATUS_VRO);
- btv->risc_jmp[7]=cpu_to_le32(0);
+static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK",
+ "VPRES", "6", "7", "I2CDONE", "GPINT", "10",
+ "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
+ "RIPERR", "PABORT", "OCERR", "SCERR" };
- /* Jump to even vbi sub */
- btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP);
- if (flags&4) {
- if (bttv_debug > 1)
- printk(" ov=%08lx",virt_to_bus(btv->vbi_even));
- btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->vbi_even));
- } else {
- if (bttv_debug > 1)
- printk(" -----------");
- btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->risc_jmp+10));
- }
-
- /* Jump to even sub */
- btv->risc_jmp[10]=cpu_to_le32(BT848_RISC_JUMP|(8<<20));
- if (0 != btv->risc_cap_even) {
- if (bttv_debug > 1)
- printk(" o%d=%08x",btv->gq_grab,btv->risc_cap_even);
- flags |= 3;
- btv->risc_jmp[11]=cpu_to_le32(btv->risc_cap_even);
- } else if ((flags&1) &&
- btv->win.interlace) {
- if (bttv_debug > 1)
- printk(" oo=%08lx",virt_to_bus(btv->risc_scr_even));
- btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_scr_even));
- } else {
- if (bttv_debug > 1)
- printk(" -----------");
- btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_jmp+12));
+static void bttv_print_irqbits(u32 print, u32 mark)
+{
+ int i;
+
+ printk("bits:");
+ for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
+ if (print & (1 << i))
+ printk(" %s",irq_name[i]);
+ if (mark & (1 << i))
+ printk("*");
}
-
- if (btv->gq_start) {
- btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ);
- } else {
- btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP);
- }
- btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp));
-
- /* enable cpaturing and DMA */
- if (bttv_debug > 1)
- printk(" flags=0x%x dma=%s\n",
- flags,(flags&0x0f) ? "on" : "off");
- btaor(flags, ~0x0f, BT848_CAP_CTL);
- if (flags&0x0f)
- bt848_dma(btv, 3);
- else
- bt848_dma(btv, 0);
}
-# define do_video_register(dev,type,nr) video_register_device(dev,type,nr)
-
-static int __devinit init_video_dev(struct bttv *btv)
+static void bttv_print_riscaddr(struct bttv *btv)
{
- audio(btv, AUDIO_MUTE, 1);
-
- if(do_video_register(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
- return -1;
- if(do_video_register(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0)
- {
- video_unregister_device(&btv->video_dev);
- return -1;
- }
- if (btv->has_radio)
- {
- if(do_video_register(&btv->radio_dev, VFL_TYPE_RADIO, radio_nr)<0)
- {
- video_unregister_device(&btv->vbi_dev);
- video_unregister_device(&btv->video_dev);
- return -1;
- }
- }
- return 1;
+ printk(" main: %08Lx\n",
+ (u64)btv->main.dma);
+ printk(" vbi : o=%08Lx e=%08Lx\n",
+ btv->vcurr ? (u64)btv->vcurr->odd.dma : 0,
+ btv->vcurr ? (u64)btv->vcurr->even.dma : 0);
+ printk(" cap : o=%08Lx e=%08Lx\n",
+ btv->odd ? (u64)btv->odd->odd.dma : 0,
+ btv->even ? (u64)btv->even->even.dma : 0);
+ printk(" scr : o=%08Lx e=%08Lx\n",
+ btv->screen ? (u64)btv->screen->odd.dma : 0,
+ btv->screen ? (u64)btv->screen->even.dma : 0);
}
-static int __devinit init_bt848(struct bttv *btv)
+static void bttv_irq_timeout(unsigned long data)
{
- int j;
- unsigned long irq_flags;
-
- btv->user=0;
- init_MUTEX(&btv->lock);
-
- /* dump current state of the gpio registers before changing them,
- * might help to make a new card work */
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"init #1");
+ struct bttv *btv = (struct bttv *)data;
+ struct bttv_buffer *o_even,*o_odd,*o_vcurr;
+ struct bttv_buffer *capture;
- /* reset the bt848 */
- btwrite(0, BT848_SRESET);
- DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n", btv->nr, (unsigned long) btv->bt848_mem));
-
- /* not registered yet */
- btv->video_dev.minor = -1;
- btv->radio_dev.minor = -1;
- btv->vbi_dev.minor = -1;
-
- /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */
- btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */
- btv->win.interlace=1;
- btv->win.x=0;
- btv->win.y=0;
- btv->win.width=320;
- btv->win.height=240;
- btv->win.bpp=2;
- btv->win.depth=16;
- btv->win.color_fmt=BT848_COLOR_FMT_RGB16;
- btv->win.bpl=1024*btv->win.bpp;
- btv->win.swidth=1024;
- btv->win.sheight=768;
- btv->win.vidadr=0;
- btv->vbi_on=0;
- btv->scr_on=0;
-
- btv->risc_scr_odd=0;
- btv->risc_scr_even=0;
- btv->risc_cap_odd=0;
- btv->risc_cap_even=0;
- btv->risc_jmp=0;
- btv->vbibuf=0;
- btv->field=btv->last_field=0;
+ if (bttv_verbose) {
+ printk(KERN_INFO "bttv%d: timeout: risc=%08x, ",
+ btv->nr,btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(btread(BT848_INT_STAT),0);
+ printk("\n");
+ }
- btv->errors=0;
- btv->needs_restart=0;
- btv->has_radio=radio[btv->nr];
+ spin_lock(&btv->s_lock);
+ o_odd = btv->odd;
+ o_even = btv->even;
+ o_vcurr = btv->vcurr;
+ btv->odd = NULL;
+ btv->even = NULL;
+ btv->vcurr = NULL;
+
+ /* deactivate stuff */
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0);
+ bttv_set_dma(btv, 0, 0);
+
+ /* wake up + free */
+ if (o_odd == o_even) {
+ if (NULL != o_odd) {
+ o_odd->vb.state = STATE_ERROR;
+ wake_up(&o_odd->vb.done);
+ }
+ } else {
+ if (NULL != o_odd) {
+ o_odd->vb.state = STATE_ERROR;
+ wake_up(&o_odd->vb.done);
+ }
+ if (NULL != o_even) {
+ o_even->vb.state = STATE_ERROR;
+ wake_up(&o_even->vb.done);
+ }
+ }
+ if (NULL != o_vcurr) {
+ o_vcurr->vb.state = STATE_ERROR;
+ wake_up(&o_vcurr->vb.done);
+ }
- if (!(btv->risc_scr_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
- return -1;
- if (!(btv->risc_scr_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
- return -1;
- if (!(btv->risc_jmp =(unsigned int *) kmalloc(2048, GFP_KERNEL)))
- return -1;
- btv->vbi_odd=btv->risc_jmp+16;
- btv->vbi_even=btv->vbi_odd+256;
- btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp+12);
- btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6);
-
- btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD);
- btv->vbibuf=(unsigned char *) vmalloc_32(VBIBUF_SIZE);
- if (!btv->vbibuf)
- return -1;
- if (!(btv->gbuf = kmalloc(sizeof(struct bttv_gbuf)*gbuffers,GFP_KERNEL)))
- return -1;
- for (j = 0; j < gbuffers; j++) {
- if (!(btv->gbuf[j].risc = kmalloc(16384,GFP_KERNEL)))
- return -1;
+ /* cancel all outstanding capture / vbi requests */
+ while (!list_empty(&btv->capture)) {
+ capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ list_del(&capture->vb.queue);
+ capture->vb.state = STATE_ERROR;
+ wake_up(&capture->vb.done);
+ }
+ while (!list_empty(&btv->vcapture)) {
+ capture = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ list_del(&capture->vb.queue);
+ capture->vb.state = STATE_ERROR;
+ wake_up(&capture->vb.done);
}
- memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random
- memory to the user */
-
- btv->fbuffer=NULL;
-
-/* btwrite(0, BT848_TDEC); */
- btwrite(0x10, BT848_COLOR_CTL);
- btwrite(0x00, BT848_CAP_CTL);
- /* set planar and packed mode trigger points and */
- /* set rising edge of inverted GPINTR pin as irq trigger */
- btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
- BT848_GPIO_DMA_CTL_PLTP1_16|
- BT848_GPIO_DMA_CTL_PLTP23_16|
- BT848_GPIO_DMA_CTL_GPINTC|
- BT848_GPIO_DMA_CTL_GPINTI,
- BT848_GPIO_DMA_CTL);
-
- /* select direct input */
- btwrite(0x00, BT848_GPIO_REG_INP);
- btwrite(0x00, BT848_GPIO_OUT_EN);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"init #2");
+ btv->errors++;
+ spin_unlock(&btv->s_lock);
+}
- btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO,
- BT848_IFORM);
+static void
+bttv_irq_switch_fields(struct bttv *btv)
+{
+ struct bttv_buffer *o_even,*o_odd,*o_vcurr;
+ struct bttv_buffer *capture;
+ int irqflags = 0;
+#ifdef HAVE_V4L2
+ stamp_t ts;
+#endif
- btwrite(0xd8, BT848_CONTRAST_LO);
- bt848_bright(btv, 0x10);
+ spin_lock(&btv->s_lock);
+ o_odd = btv->odd;
+ o_even = btv->even;
+ o_vcurr = btv->vcurr;
+ btv->odd = NULL;
+ btv->even = NULL;
+ btv->vcurr = NULL;
+
+ /* vbi request ? */
+ if (!list_empty(&btv->vcapture)) {
+ irqflags = 1;
+ btv->vcurr = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ list_del(&btv->vcurr->vb.queue);
+ }
+
+ /* capture request ? */
+ if (!list_empty(&btv->capture)) {
+ irqflags = 1;
+ capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ list_del(&capture->vb.queue);
+ if (capture->vb.field & VBUF_FIELD_ODD)
+ btv->odd = capture;
+ if (capture->vb.field & VBUF_FIELD_EVEN)
+ btv->even = capture;
+
+ /* capture request for other field ? */
+ if (!(capture->vb.field & VBUF_FIELD_INTER) &&
+ !list_empty(&btv->capture)) {
+ capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ if (!(capture->vb.field & VBUF_FIELD_INTER)) {
+ if (NULL == btv->odd &&
+ capture->vb.field & VBUF_FIELD_ODD) {
+ btv->odd = capture;
+ list_del(&capture->vb.queue);
+ }
+ if (NULL == btv->even &&
+ capture->vb.field & VBUF_FIELD_EVEN) {
+ btv->even = capture;
+ list_del(&capture->vb.queue);
+ }
+ }
+ }
+ }
- btwrite(0x20, BT848_E_VSCALE_HI);
- btwrite(0x20, BT848_O_VSCALE_HI);
- btwrite(/*BT848_ADC_SYNC_T|*/
- BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC);
+ /* screen overlay ? */
+ if (NULL != btv->screen) {
+ if (btv->screen->vb.field & VBUF_FIELD_INTER) {
+ if (NULL == btv->odd && NULL == btv->even) {
+ btv->odd = btv->screen;
+ btv->even = btv->screen;
+ }
+ } else {
+ if ((btv->screen->vb.field & VBUF_FIELD_ODD) &&
+ NULL == btv->odd) {
+ btv->odd = btv->screen;
+ }
+ if ((btv->screen->vb.field & VBUF_FIELD_EVEN) &&
+ NULL == btv->even) {
+ btv->even = btv->screen;
+ }
+ }
+ }
- if (lumafilter) {
- btwrite(0, BT848_E_CONTROL);
- btwrite(0, BT848_O_CONTROL);
+ if (irq_debug)
+ printk(KERN_DEBUG
+ "bttv: irq odd=%p even=%p screen=%p vbi=%p\n",
+ btv->odd,btv->even,btv->screen,btv->vcurr);
+
+ /* activate new fields */
+ bttv_buffer_activate(btv,btv->odd,btv->even);
+ if (btv->vcurr) {
+ btv->vcurr->vb.state = STATE_ACTIVE;
+ bttv_risc_hook(btv, RISC_SLOT_O_VBI, &btv->vcurr->odd, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_VBI, &btv->vcurr->even, 0);
} else {
- btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
- btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0);
}
-
- btv->picture.colour=254<<7;
- btv->picture.brightness=128<<8;
- btv->picture.hue=128<<8;
- btv->picture.contrast=0xd8<<7;
-
- btwrite(0x00, BT848_E_SCLOOP);
- btwrite(0x00, BT848_O_SCLOOP);
-
- /* clear interrupt status */
- btwrite(0xfffffUL, BT848_INT_STAT);
-
- /* set interrupt mask */
- btwrite(btv->triton1|
- /*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|
- BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/
- (fieldnr ? BT848_INT_VSYNC : 0)|
- BT848_INT_GPINT|
- BT848_INT_SCERR|
- BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
- BT848_INT_FMTCHG|BT848_INT_HLOCK,
- BT848_INT_MASK);
-
- bt848_muxsel(btv, 1);
- bt848_set_winsize(btv);
- make_vbitab(btv);
- spin_lock_irqsave(&btv->s_lock, irq_flags);
- bt848_set_risc_jmps(btv,-1);
- spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-
- /* needs to be done before i2c is registered */
- if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878)
- bttv_boot_msp34xx(btv,5);
- if (btv->type == BTTV_VOODOOTV_FM)
- bttv_boot_msp34xx(btv,20);
-
- /* register i2c */
- btv->tuner_type=-1;
- init_bttv_i2c(btv);
-
- /* some card-specific stuff (needs working i2c) */
- bttv_init_card(btv);
-
- /*
- * Now add the template and register the device unit.
- */
- init_video_dev(btv);
-
- return 0;
+ bttv_set_dma(btv, 0, irqflags);
+
+ /* wake up + free */
+#ifdef HAVE_V4L2
+ v4l2_masterclock_gettime(&ts);
+#endif
+ if (o_odd == o_even) {
+ if (NULL != o_odd && btv->odd != o_odd) {
+#ifdef HAVE_V4L2
+ o_odd->vb.ts = ts;
+#endif
+ o_odd->vb.field_count = btv->field_count;
+ o_odd->vb.state = STATE_DONE;
+ wake_up(&o_odd->vb.done);
+ }
+ } else {
+ if (NULL != o_odd && btv->odd != o_odd) {
+#ifdef HAVE_V4L2
+ o_odd->vb.ts = ts;
+#endif
+ o_odd->vb.field_count = btv->field_count;
+ o_odd->vb.state = STATE_DONE;
+ wake_up(&o_odd->vb.done);
+ }
+ if (NULL != o_even && btv->even != o_even) {
+#ifdef HAVE_V4L2
+ o_even->vb.ts = ts;
+#endif
+ o_even->vb.field_count = btv->field_count;
+ o_even->vb.state = STATE_DONE;
+ wake_up(&o_even->vb.done);
+ }
+ }
+ if (NULL != o_vcurr) {
+#ifdef HAVE_V4L2
+ o_vcurr->vb.ts = ts;
+#endif
+ o_vcurr->vb.field_count = btv->field_count;
+ o_vcurr->vb.state = STATE_DONE;
+ wake_up(&o_vcurr->vb.done);
+ }
+ spin_unlock(&btv->s_lock);
}
-/* ----------------------------------------------------------------------- */
-
-static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK",
- "VPRES", "6", "7", "I2CDONE", "GPINT", "10",
- "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
- "RIPERR", "PABORT", "OCERR", "SCERR" };
-
static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
{
u32 stat,astat;
dstat=btread(BT848_DSTATUS);
if (irq_debug) {
- int i;
- printk(KERN_DEBUG "bttv%d: irq loop=%d risc=%x, bits:",
- btv->nr, count, stat>>28);
- for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
- if (stat & (1 << i))
- printk(" %s",irq_name[i]);
- if (astat & (1 << i))
- printk("*");
- }
+ printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d "
+ "riscs=%x, riscc=%08x, ",
+ btv->nr, count, btv->field_count,
+ stat>>28, btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
if (stat & BT848_INT_HLOCK)
printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC)
? "yes" : "no");
printk("\n");
}
- if (astat&BT848_INT_GPINT)
- wake_up_interruptible(&btv->gpioq);
-
if (astat&BT848_INT_VSYNC)
- btv->field++;
-
- if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) {
- if (bttv_verbose)
- printk("bttv%d: irq:%s%s risc_count=%08x\n",
- btv->nr,
- (astat&BT848_INT_SCERR) ? " SCERR" : "",
- (astat&BT848_INT_OCERR) ? " OCERR" : "",
- btread(BT848_RISC_COUNT));
- btv->errors++;
- if (btv->errors < BTTV_ERRORS) {
- spin_lock(&btv->s_lock);
- btand(~15, BT848_GPIO_DMA_CTL);
- btwrite(virt_to_bus(btv->risc_jmp+2),
- BT848_RISC_STRT_ADD);
- bt848_set_geo(btv,0);
- bt848_set_risc_jmps(btv,-1);
- spin_unlock(&btv->s_lock);
- } else {
- if (bttv_verbose)
- printk("bttv%d: aiee: error loops\n",btv->nr);
- bt848_offline(btv);
- }
- }
- if (astat&BT848_INT_RISCI)
- {
- if (bttv_debug > 1)
- printk("bttv%d: IRQ_RISCI\n",btv->nr);
-
- /* captured VBI frame */
- if (stat&(1<<28))
- {
- btv->vbip=0;
- /* inc vbi frame count for detecting drops */
- (*(u32 *)&(btv->vbibuf[VBIBUF_SIZE - 4]))++;
- wake_up_interruptible(&btv->vbiq);
- }
+ btv->field_count++;
- /* captured full frame */
- if (stat&(2<<28) && btv->gq_grab != -1)
- {
- btv->last_field=btv->field;
- if (bttv_debug)
- printk("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab);
- do_gettimeofday(&btv->gbuf[btv->gq_grab].tv);
- spin_lock(&btv->s_lock);
- btv->gbuf[btv->gq_grab].stat = GBUFFER_DONE;
- btv->gq_grab = -1;
- if (btv->gq_in != btv->gq_out)
- {
- btv->gq_grab = btv->gqueue[btv->gq_out++];
- btv->gq_out = btv->gq_out % MAX_GBUFFERS;
- if (bttv_debug)
- printk("bttv%d: cap irq: capture %d\n",btv->nr,btv->gq_grab);
- btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro;
- btv->risc_cap_even = btv->gbuf[btv->gq_grab].re;
- bt848_set_risc_jmps(btv,-1);
- bt848_set_geo(btv,0);
- btwrite(BT848_COLOR_CTL_GAMMA,
- BT848_COLOR_CTL);
- } else {
- btv->risc_cap_odd = 0;
- btv->risc_cap_even = 0;
- bt848_set_risc_jmps(btv,-1);
- bt848_set_geo(btv,0);
- btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA,
- BT848_COLOR_CTL);
- }
- spin_unlock(&btv->s_lock);
- wake_up_interruptible(&btv->capq);
- break;
- }
- if (stat&(8<<28) && btv->gq_start)
- {
- spin_lock(&btv->s_lock);
- btv->gq_start = 0;
- btv->gq_grab = btv->gqueue[btv->gq_out++];
- btv->gq_out = btv->gq_out % MAX_GBUFFERS;
- if (bttv_debug)
- printk("bttv%d: cap irq: capture %d [start]\n",btv->nr,btv->gq_grab);
- btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro;
- btv->risc_cap_even = btv->gbuf[btv->gq_grab].re;
- bt848_set_risc_jmps(btv,-1);
- bt848_set_geo(btv,0);
- btwrite(BT848_COLOR_CTL_GAMMA,
- BT848_COLOR_CTL);
- spin_unlock(&btv->s_lock);
- }
- }
+ if (astat & BT848_INT_GPINT)
+ wake_up(&btv->gpioq);
+
+ if ((astat & BT848_INT_RISCI) && (stat & (1<<28)))
+ bttv_irq_switch_fields(btv);
- if (astat&BT848_INT_HLOCK) {
- if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio))
- audio(btv, AUDIO_ON,0);
+ if ((astat & BT848_INT_HLOCK) && btv->opt_automute) {
+ if ((dstat & BT848_DSTATUS_HLOC) || btv->radio_user)
+ audio_mux(btv, AUDIO_UNMUTE);
else
- audio(btv, AUDIO_OFF,0);
+ audio_mux(btv, AUDIO_MUTE);
}
-
+
+ if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+ printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->nr,
+ (astat & BT848_INT_SCERR) ? "SCERR" : "",
+ (astat & BT848_INT_OCERR) ? "OCERR" : "",
+ btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
+ printk("\n");
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+ if (fdsr && astat & BT848_INT_FDSR) {
+ printk(KERN_INFO "bttv%d: FDSR @ %08x\n",
+ btv->nr,btread(BT848_RISC_COUNT));
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+
count++;
if (count > 20) {
btwrite(0, BT848_INT_MASK);
printk(KERN_ERR
"bttv%d: IRQ lockup, cleared int mask\n", btv->nr);
- bt848_offline(btv);
}
}
}
+/* ----------------------------------------------------------------------- */
+/* initialitation */
-/*
- * Scan for a Bt848 card, request the irq and map the io memory
- */
-
-/* Can't be marked __devexit with a reference from bttv_probe. */
-static void bttv_remove(struct pci_dev *pci_dev)
+/* register video4linux devices */
+static int __devinit bttv_register_video(struct bttv *btv)
{
- u8 command;
- int j;
- struct bttv *btv = pci_get_drvdata(pci_dev);
-
- if (bttv_verbose)
- printk("bttv%d: unloading\n",btv->nr);
-
- /* unregister i2c_bus */
- if (0 == btv->i2c_rc)
- i2c_bit_del_bus(&btv->i2c_adap);
-
- /* turn off all capturing, DMA and IRQs */
- btand(~15, BT848_GPIO_DMA_CTL);
-
- /* first disable interrupts before unmapping the memory! */
- btwrite(0, BT848_INT_MASK);
- btwrite(~0x0UL,BT848_INT_STAT);
- btwrite(0x0, BT848_GPIO_OUT_EN);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"cleanup");
-
- /* disable PCI bus-mastering */
- pci_read_config_byte(btv->dev, PCI_COMMAND, &command);
- command &= ~PCI_COMMAND_MASTER;
- pci_write_config_byte(btv->dev, PCI_COMMAND, command);
-
- /* unmap and free memory */
- for (j = 0; j < gbuffers; j++)
- if (btv->gbuf[j].risc)
- kfree(btv->gbuf[j].risc);
- if (btv->gbuf)
- kfree((void *) btv->gbuf);
-
- if (btv->risc_scr_odd)
- kfree((void *) btv->risc_scr_odd);
-
- if (btv->risc_scr_even)
- kfree((void *) btv->risc_scr_even);
-
- DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%p.\n", btv->risc_jmp));
- if (btv->risc_jmp)
- kfree((void *) btv->risc_jmp);
-
- DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%p.\n", btv->vbibuf));
- if (btv->vbibuf)
- vfree((void *) btv->vbibuf);
-
- free_irq(btv->irq,btv);
- DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem));
- if (btv->bt848_mem)
- iounmap(btv->bt848_mem);
+ if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
+ return -1;
+ printk(KERN_INFO "bttv%d: registered device video%d\n",
+ btv->nr,btv->video_dev.minor & 0x1f);
- if (btv->video_dev.minor!=-1)
+ if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) {
video_unregister_device(&btv->video_dev);
- if (btv->vbi_dev.minor!=-1)
- video_unregister_device(&btv->vbi_dev);
- if (btv->radio_dev.minor != -1)
- video_unregister_device(&btv->radio_dev);
-
- release_mem_region(pci_resource_start(btv->dev,0),
- pci_resource_len(btv->dev,0));
- /* wake up any waiting processes
- because shutdown flag is set, no new processes (in this queue)
- are expected
- */
- btv->shutdown=1;
- wake_up(&btv->gpioq);
+ return -1;
+ }
+ printk(KERN_INFO "bttv%d: registered device vbi%d\n",
+ btv->nr,btv->vbi_dev.minor & 0x1f);
- pci_set_drvdata(pci_dev, NULL);
- return;
+ if (!btv->has_radio)
+ return 0;
+ if (video_register_device(&btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) {
+ video_unregister_device(&btv->vbi_dev);
+ video_unregister_device(&btv->video_dev);
+ return -1;
+ }
+ printk(KERN_INFO "bttv%d: registered device radio%d\n",
+ btv->nr,btv->radio_dev.minor & 0x1f);
+ return 0;
}
-static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
+/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+/* response on cards with no firmware is not enabled by OF */
+static void pci_set_command(struct pci_dev *dev)
{
- int result;
- unsigned char lat;
- struct bttv *btv;
#if defined(__powerpc__)
unsigned int cmd;
+
+ pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+ cmd = (cmd | PCI_COMMAND_MEMORY );
+ pci_write_config_dword(dev, PCI_COMMAND, cmd);
#endif
+}
- printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
+static int __devinit bttv_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int result;
+ unsigned char lat;
+ struct bttv *btv;
+ if (bttv_num == BTTV_MAX)
+ return -ENOMEM;
+ printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
btv=&bttvs[bttv_num];
- btv->dev=dev;
- btv->nr = bttv_num;
- btv->bt848_mem=NULL;
- btv->vbibuf=NULL;
- btv->risc_jmp=NULL;
- btv->vbi_odd=NULL;
- btv->vbi_even=NULL;
- init_waitqueue_head(&btv->vbiq);
- init_waitqueue_head(&btv->capq);
- btv->vbip=VBIBUF_SIZE;
- btv->s_lock = SPIN_LOCK_UNLOCKED;
- init_waitqueue_head(&btv->gpioq);
- btv->shutdown=0;
+ memset(btv,0,sizeof(*btv));
+ btv->nr = bttv_num;
+ sprintf(btv->name,"bttv%d",btv->nr);
+
+ /* initialize structs / fill in defaults */
+ init_MUTEX(&btv->lock);
+ init_MUTEX(&btv->reslock);
+ btv->s_lock = SPIN_LOCK_UNLOCKED;
+ init_waitqueue_head(&btv->gpioq);
+ INIT_LIST_HEAD(&btv->capture);
+ INIT_LIST_HEAD(&btv->vcapture);
+
+ init_MUTEX(&btv->vbi.lock);
+ INIT_LIST_HEAD(&btv->vbi.stream);
+
+ btv->timeout.function = bttv_irq_timeout;
+ btv->timeout.data = (unsigned long)btv;
- memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template));
- memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
- memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
+ btv->i2c_rc = -1;
+ btv->tuner_type = -1;
+
+ memcpy(&btv->video_dev, &bttv_template, sizeof(bttv_template));
+ memcpy(&btv->radio_dev, &radio_template, sizeof(radio_template));
+ memcpy(&btv->vbi_dev, &bttv_vbi_template, sizeof(bttv_vbi_template));
+ btv->video_dev.minor = -1;
+ btv->video_dev.priv = btv;
+ btv->radio_dev.minor = -1;
+ btv->radio_dev.priv = btv;
+ btv->vbi_dev.minor = -1;
+ btv->vbi_dev.priv = btv;
+ btv->has_radio=radio[btv->nr];
- btv->id=dev->device;
- btv->irq=dev->irq;
- btv->bt848_adr=pci_resource_start(dev,0);
- if (pci_enable_device(dev))
+ /* pci stuff (init, get irq/mmip, ... */
+ btv->dev = dev;
+ btv->id = dev->device;
+ if (pci_enable_device(dev)) {
+ printk(KERN_WARNING "bttv%d: Can't enable device.\n",
+ btv->nr);
return -EIO;
+ }
+ if (pci_set_dma_mask(dev, 0xffffffff)) {
+ printk(KERN_WARNING "bttv%d: No suitable DMA available.\n",
+ btv->nr);
+ return -EIO;
+ }
if (!request_mem_region(pci_resource_start(dev,0),
pci_resource_len(dev,0),
- "bttv")) {
+ btv->name)) {
return -EBUSY;
}
- if (btv->id >= 878)
- btv->i2c_command = 0x83;
- else
- btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);
+ pci_set_master(dev);
+ pci_set_command(dev);
+ pci_set_drvdata(dev,btv);
+ if (!pci_dma_supported(dev,0xffffffff)) {
+ printk("bttv: Oops: no 32bit PCI DMA ???\n");
+ result = -EIO;
+ goto fail1;
+ }
+ if (-1 != latency) {
+ printk(KERN_INFO "bttv%d: setting pci latency timer to %d\n",
+ bttv_num,latency);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, latency);
+ }
pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %02x:%02x.%x, ",
bttv_num,btv->id, btv->revision, dev->bus->number,
PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));
- printk("irq: %d, latency: %d, memory: 0x%lx\n",
- btv->irq, lat, btv->bt848_adr);
+ printk("irq: %d, latency: %d, mmio: 0x%lx\n",
+ btv->dev->irq, lat, pci_resource_start(dev,0));
- bttv_idcard(btv);
-
-#if defined(__powerpc__)
- /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
- /* response on cards with no firmware is not enabled by OF */
- pci_read_config_dword(dev, PCI_COMMAND, &cmd);
- cmd = (cmd | PCI_COMMAND_MEMORY );
- pci_write_config_dword(dev, PCI_COMMAND, cmd);
-#endif
-
#ifdef __sparc__
- btv->bt848_mem=(unsigned char *)btv->bt848_adr;
+ /* why no ioremap for sparc? */
+ btv->bt848_mmio=(unsigned char *)pci_resource_start(dev,0);
#else
- btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
+ btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000);
#endif
-
- /* clear interrupt mask */
- btwrite(0, BT848_INT_MASK);
- result = request_irq(btv->irq, bttv_irq,
- SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
- if (result==-EINVAL)
- {
- printk(KERN_ERR "bttv%d: Bad irq number or handler\n",
- bttv_num);
- goto fail1;
- }
- if (result==-EBUSY)
- {
- printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq);
+ /* identify card */
+ bttv_idcard(btv);
+
+ /* disable irqs, register irq handler */
+ btwrite(0, BT848_INT_MASK);
+ result = request_irq(btv->dev->irq, bttv_irq,
+ SA_SHIRQ | SA_INTERRUPT,btv->name,(void *)btv);
+ if (result < 0) {
+ printk(KERN_ERR "bttv%d: can't get IRQ %d\n",
+ bttv_num,btv->dev->irq);
goto fail1;
}
- if (result < 0)
- goto fail1;
-
+
if (0 != bttv_handle_chipset(btv)) {
- result = -1;
+ result = -EIO;
goto fail2;
- }
+ }
+
+ /* init options from insmod args */
+ btv->opt_combfilter = combfilter;
+ btv->opt_lumafilter = lumafilter;
+ btv->opt_automute = automute;
+ btv->opt_chroma_agc = chroma_agc;
+ btv->opt_adc_crush = adc_crush;
- pci_set_master(dev);
- pci_set_drvdata(dev,btv);
+ /* fill struct bttv with some useful defaults */
+ btv->init.btv = btv;
+ btv->init.ov.width = 320;
+ btv->init.ov.height = 240;
+ btv->init.buf.fmt = format_by_palette(VIDEO_PALETTE_RGB24);
+ btv->init.buf.vb.width = 320;
+ btv->init.buf.vb.height = 240;
+ btv->input = 0;
+
+ /* initialize hardware */
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pre-init");
+
+ bttv_risc_init_main(btv);
+ init_bt848(btv);
+
+ /* gpio */
+ btwrite(0x00, BT848_GPIO_REG_INP);
+ btwrite(0x00, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"init");
+
+ /* interrupt */
+ btwrite(0xfffffUL, BT848_INT_STAT);
+ btwrite((btv->triton1) |
+ BT848_INT_GPINT|
+ BT848_INT_SCERR|
+ (fdsr ? BT848_INT_FDSR : 0) |
+ BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
+ BT848_INT_FMTCHG|BT848_INT_HLOCK,
+ BT848_INT_MASK);
+
+ /* needs to be done before i2c is registered */
+ bttv_init_card1(btv);
- if(init_bt848(btv) < 0) {
- bttv_remove(dev);
- return -EIO;
- }
- bttv_num++;
+ /* register i2c */
+ init_bttv_i2c(btv);
+
+ /* some card-specific stuff (needs working i2c) */
+ bttv_init_card2(btv);
+
+ /* register video4linux */
+ bttv_register_video(btv);
+ bt848_bright(btv,32768);
+ bt848_contrast(btv,32768);
+ bt848_hue(btv,32768);
+ bt848_sat(btv,32768);
+ audio_mux(btv,AUDIO_MUTE);
+ set_input(btv,0);
+
+ /* everything is fine */
+ bttv_num++;
return 0;
fail2:
- free_irq(btv->irq,btv);
+ free_irq(btv->dev->irq,btv);
+
fail1:
release_mem_region(pci_resource_start(btv->dev,0),
pci_resource_len(btv->dev,0));
+ pci_set_drvdata(dev,NULL);
return result;
}
+static void __devexit bttv_remove(struct pci_dev *pci_dev)
+{
+ struct bttv *btv = pci_get_drvdata(pci_dev);
+
+ if (bttv_verbose)
+ printk("bttv%d: unloading\n",btv->nr);
+
+ /* shutdown everything (DMA+IRQs) */
+ btand(~15, BT848_GPIO_DMA_CTL);
+ btwrite(0, BT848_INT_MASK);
+ btwrite(~0x0UL,BT848_INT_STAT);
+ btwrite(0x0, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"cleanup");
+
+ /* tell gpio modules we are leaving ... */
+ btv->shutdown=1;
+ wake_up(&btv->gpioq);
+
+ /* unregister i2c_bus */
+ if (0 == btv->i2c_rc)
+ i2c_bit_del_bus(&btv->i2c_adap);
+
+ /* unregister video4linux */
+ if (btv->video_dev.minor!=-1)
+ video_unregister_device(&btv->video_dev);
+ if (btv->radio_dev.minor!=-1)
+ video_unregister_device(&btv->radio_dev);
+ if (btv->vbi_dev.minor!=-1)
+ video_unregister_device(&btv->vbi_dev);
+
+ /* free allocated memory */
+ bttv_riscmem_free(btv->dev,&btv->main);
+
+ /* free ressources */
+ free_irq(btv->dev->irq,btv);
+ release_mem_region(pci_resource_start(btv->dev,0),
+ pci_resource_len(btv->dev,0));
+
+ pci_set_drvdata(pci_dev, NULL);
+ return;
+}
+
static struct pci_device_id bttv_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
remove: bttv_remove,
};
-int bttv_init_module(void)
+static int bttv_init_module(void)
{
bttv_num = 0;
- printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n",
+ printk(KERN_INFO "bttv: driver version %d.%d.%d loaded "
+ BTTV_APIS "\n",
(BTTV_VERSION_CODE >> 16) & 0xff,
(BTTV_VERSION_CODE >> 8) & 0xff,
BTTV_VERSION_CODE & 0xff);
- if (gbuffers < 2 || gbuffers > MAX_GBUFFERS)
+ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
gbuffers = 2;
if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF)
gbufsize = BTTV_MAX_FBUF;
+ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
if (bttv_verbose)
- printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n",
- gbuffers,gbufsize/1024,gbuffers*gbufsize/1024);
+ printk(KERN_INFO "bttv: using %d buffers with %dk (%d pages) each for capture\n",
+ gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
bttv_check_chipset();
return pci_module_init(&bttv_pci_driver);
}
-void bttv_cleanup_module(void)
+static void bttv_cleanup_module(void)
{
pci_unregister_driver(&bttv_pci_driver);
return;